gui: Allow File|Open to select an encoding for system files.
[pspp] / src / libpspp / zip-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2011, 2013 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <errno.h>
26 #include <xalloc.h>
27 #include <libpspp/assertion.h>
28
29 #include <byteswap.h>
30 #include <crc.h>
31
32 #include "inflate.h"
33
34 #include "str.h"
35
36 #include "zip-reader.h"
37 #include "zip-private.h"
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41 #define N_(msgid) (msgid)
42
43
44 static bool find_eocd (FILE *fp, off_t *off);
45
46 static int
47 stored_read (struct zip_member *zm, void *buf, size_t n)
48 {
49   return fread (buf, 1, n, zm->fp);
50 }
51
52 static bool
53 stored_init (struct zip_member *zm UNUSED)
54 {
55   return true;
56 }
57
58 static void
59 stored_finish (struct zip_member *zm UNUSED)
60 {
61   /* Nothing required */
62 }
63
64
65 static struct decompressor decompressors[n_COMPRESSION] = 
66   {
67     {stored_init, stored_read, stored_finish},
68     {inflate_init, inflate_read, inflate_finish}
69   };
70
71 static enum compression
72 comp_code (struct zip_member *zm, uint16_t c)
73 {
74   enum compression which;
75   switch (c)
76     {
77     case 0:
78       which = COMPRESSION_STORED;
79       break;
80     case 8:
81       which = COMPRESSION_INFLATE;
82       break;
83     default:
84       ds_put_format (zm->errs, _("Unsupported compression type (%d)"), c);
85       which = n_COMPRESSION;
86       break;
87     }
88   return which;
89 }
90
91
92 struct zip_reader
93 {
94   char *filename;                  /* The name of the file from which the data is read */
95   FILE *fr;                        /* The stream from which the meta data is read */
96   uint16_t n_members;              /* The number of members in this archive */
97   struct zip_member **members;     /* The members (may be null pointers until the headers have been read */
98   int nm;
99   struct string *errs;
100 };
101
102 void
103 zip_member_finish (struct zip_member *zm)
104 {
105   ds_clear (zm->errs);
106   /*  Probably not useful, because we would have to read right to the end of the member
107   if (zm->expected_crc != zm->crc)
108     {
109       ds_put_cstr (zm->errs, _("CRC error reading zip"));
110     }
111   */
112   zip_member_unref (zm);
113 }
114
115
116
117 /* Destroy the zip reader */
118 void
119 zip_reader_destroy (struct zip_reader *zr)
120 {
121   int i;
122   if (zr == NULL) 
123     return;
124
125   fclose (zr->fr);
126   free (zr->filename);
127
128   for (i = 0; i < zr->n_members; ++i)
129     {
130       zip_member_unref (zr->members[i]);
131     }
132   free (zr->members);
133   free (zr);
134 }
135
136
137 void
138 zm_dump (const struct zip_member *zm)
139 {
140   printf ("%d\t%08x\t %s\n", zm->ucomp_size, zm->expected_crc, zm->name);
141 }
142
143
144 /* Skip N bytes in F */
145 static void
146 skip_bytes (FILE *f, size_t n)
147 {
148   fseeko (f, n, SEEK_CUR);
149 }
150
151 /* Read N bytes from F, storing the result in X */
152 static void
153 get_bytes (FILE *f, void *x, size_t n)
154 {
155   fread (x, 1, n, f);
156 }
157
158 /* Read a 32 bit value from F */
159 static void
160 get_u32 (FILE *f, uint32_t *v)
161 {
162   uint32_t x;
163   get_bytes (f, &x, sizeof x);
164 #ifdef WORDS_BIGENDIAN
165   *v = bswap_32 (x);
166 #else
167   *v = x;
168 #endif
169 }
170
171 /* Read a 16 bit value from F */
172 static void
173 get_u16 (FILE *f, uint16_t *v)
174 {
175   uint16_t x;
176   get_bytes (f, &x, sizeof x);
177 #ifdef WORDS_BIGENDIAN
178   *v = bswap_16 (x);
179 #else
180   *v = x;
181 #endif
182 }
183
184
185 /* Read 32 bit integer and compare it with EXPECTED.
186    place an error string in ERR if necessary. */
187 static bool
188 check_magic (FILE *f, uint32_t expected, struct string *err)
189 {
190   uint32_t magic;
191
192   get_u32 (f, &magic);
193
194   if ((expected != magic))
195     {
196       ds_put_format (err,
197                      _("Corrupt file at 0x%llx: Expected %"PRIx32"; got %"PRIx32), 
198                      (long long int) ftello (f) - sizeof (uint32_t), expected, magic);
199
200       return false;
201     }
202   return true;
203 }
204
205
206 /* Reads upto BYTES bytes from ZM and puts them in BUF.
207    Returns the number of bytes read, or -1 on error */
208 int
209 zip_member_read (struct zip_member *zm, void *buf, size_t bytes)
210 {
211   int bytes_read = 0;
212
213   ds_clear (zm->errs);
214
215   if ( bytes > zm->bytes_unread)
216     bytes = zm->bytes_unread;
217
218   bytes_read  = decompressors[zm->compression].read (zm, buf, bytes);
219   if ( bytes_read < 0)
220     return bytes_read;
221
222   zm->crc = crc32_update (zm->crc, buf, bytes_read);
223
224   zm->bytes_unread -= bytes_read;
225
226   return bytes_read;
227 }
228
229
230 /*
231   Read a local file header from ZR and add it to ZR's internal array.
232   Returns a pointer to the member read.  This pointer belongs to ZR.
233   If the caller wishes to control it, she should ref it with 
234   zip_member_ref.
235 */
236 static struct zip_member *
237 zip_header_read_next (struct zip_reader *zr)
238 {
239   struct zip_member *zm = xzalloc (sizeof *zm);
240
241   uint16_t v, nlen, extralen;
242   uint16_t gp, time, date;
243   
244   uint16_t clen, diskstart, iattr;
245   uint32_t eattr;
246   uint16_t comp_type;
247
248   ds_clear (zr->errs);
249
250   if ( ! check_magic (zr->fr, MAGIC_SOCD, zr->errs))
251     return NULL;
252
253   get_u16 (zr->fr, &v);
254
255   get_u16 (zr->fr, &v);
256   get_u16 (zr->fr, &gp);
257   get_u16 (zr->fr, &comp_type);
258
259   zm->compression = comp_code (zm, comp_type);
260
261   get_u16 (zr->fr, &time);
262   get_u16 (zr->fr, &date);
263   get_u32 (zr->fr, &zm->expected_crc);
264   get_u32 (zr->fr, &zm->comp_size);
265   get_u32 (zr->fr, &zm->ucomp_size);
266   get_u16 (zr->fr, &nlen);
267   get_u16 (zr->fr, &extralen);
268   get_u16 (zr->fr, &clen);
269   get_u16 (zr->fr, &diskstart);
270   get_u16 (zr->fr, &iattr);
271   get_u32 (zr->fr, &eattr);
272   get_u32 (zr->fr, &zm->offset);
273
274   zm->name = xzalloc (nlen + 1);
275   get_bytes (zr->fr, zm->name, nlen);
276
277   skip_bytes (zr->fr, extralen);
278   
279   zr->members[zr->nm++] = zm;
280
281   zm->fp = fopen (zr->filename, "r");
282   zm->ref_cnt = 1;
283   zm->errs = zr->errs;
284
285   return zm;
286 }
287
288
289 /* Create a reader from the zip called FILENAME */
290 struct zip_reader *
291 zip_reader_create (const char *filename, struct string *errs)
292 {
293   uint16_t disknum, total_members;
294   off_t offset = 0;
295   uint32_t central_dir_start, central_dir_length;
296
297   struct zip_reader *zr = xzalloc (sizeof *zr);
298   zr->errs = errs;
299   if ( zr->errs)
300     ds_init_empty (zr->errs);
301
302   zr->nm = 0;
303
304   zr->fr = fopen (filename, "r");
305   if (NULL == zr->fr)
306     {
307       ds_put_cstr (zr->errs, strerror (errno));
308       free (zr);
309       return NULL;
310     }
311
312   if ( ! check_magic (zr->fr, MAGIC_LHDR, zr->errs))
313     {
314       fclose (zr->fr);
315       free (zr);
316       return NULL;
317     }
318
319   if ( ! find_eocd (zr->fr, &offset))
320     {
321       ds_put_format (zr->errs, _("Cannot find central directory"));
322       fclose (zr->fr);
323       free (zr);
324       return NULL;
325     }
326
327   if ( 0 != fseeko (zr->fr, offset, SEEK_SET))
328     {
329       const char *mm = strerror (errno);
330       ds_put_format (zr->errs, _("Failed to seek to end of central directory record: %s"), mm);
331       fclose (zr->fr);
332       free (zr);
333       return NULL;
334     }
335
336
337   if ( ! check_magic (zr->fr, MAGIC_EOCD, zr->errs))
338     {
339       fclose (zr->fr);
340       free (zr);
341       return NULL;
342     }
343   
344   get_u16 (zr->fr, &disknum);
345   get_u16 (zr->fr, &disknum);
346
347   get_u16 (zr->fr, &zr->n_members);
348   get_u16 (zr->fr, &total_members);
349
350   get_u32 (zr->fr, &central_dir_length);
351   get_u32 (zr->fr, &central_dir_start);
352
353   if ( 0 != fseeko (zr->fr, central_dir_start, SEEK_SET))
354     {
355       const char *mm = strerror (errno);
356       ds_put_format (zr->errs, _("Failed to seek to central directory: %s"), mm);
357       fclose (zr->fr);
358       free (zr);
359       return NULL;
360     }
361
362   zr->members = xcalloc (zr->n_members, sizeof (*zr->members));
363   memset (zr->members, 0, zr->n_members * sizeof (*zr->members));
364
365   zr->filename = strdup (filename);
366
367   return zr;
368 }
369
370
371
372 /* Return the member called MEMBER from the reader ZR  */
373 struct zip_member *
374 zip_member_open (struct zip_reader *zr, const char *member)
375 {
376   uint16_t v, nlen, extra_len;
377   uint16_t gp, comp_type, time, date;
378   uint32_t ucomp_size, comp_size;
379   
380   uint32_t crc;
381   bool new_member = false;
382   char *name = NULL;
383
384   int i;
385   struct zip_member *zm = NULL;
386
387   if ( zr == NULL)
388     return NULL;
389
390   for (i = 0; i < zr->n_members; ++i)
391   {
392     zm = zr->members[i];
393
394     if (zm == NULL)
395       {
396         zm = zr->members[i] = zip_header_read_next (zr);
397         new_member = true;
398       }
399     if (zm && 0 == strcmp (zm->name, member))
400       break;
401     else
402       zm = NULL;
403   }
404   
405   if ( zm == NULL)
406     return NULL;
407
408   if ( 0 != fseeko (zm->fp, zm->offset, SEEK_SET))
409     {
410       const char *mm = strerror (errno);
411       ds_put_format (zm->errs, _("Failed to seek to start of member `%s': %s"), zm->name, mm);
412       return NULL;
413     }
414
415   if ( ! check_magic (zm->fp, MAGIC_LHDR, zr->errs))
416     {
417       return NULL;
418     }
419
420   get_u16 (zm->fp, &v);
421   get_u16 (zm->fp, &gp);
422   get_u16 (zm->fp, &comp_type);
423   zm->compression = comp_code (zm, comp_type);
424   get_u16 (zm->fp, &time);
425   get_u16 (zm->fp, &date);
426   get_u32 (zm->fp, &crc);
427   get_u32 (zm->fp, &comp_size);
428
429   get_u32 (zm->fp, &ucomp_size);
430   get_u16 (zm->fp, &nlen);
431   get_u16 (zm->fp, &extra_len);
432
433   name = xzalloc (nlen + 1);
434
435   get_bytes (zm->fp, name, nlen);
436
437   skip_bytes (zm->fp, extra_len);
438
439   if (strcmp (name, zm->name) != 0)
440     {
441       ds_put_format (zm->errs,
442                      _("Name mismatch in zip archive. Central directory says `%s'; local file header says `%s'"),
443                      zm->name, name);
444       free (name);
445       free (zm);
446       return NULL;
447     }
448
449   free (name);
450
451   zm->bytes_unread = zm->ucomp_size;
452   
453   if ( !new_member)
454     decompressors[zm->compression].finish (zm);
455
456   if (!decompressors[zm->compression].init (zm) )
457     return NULL;
458
459   return zm;
460 }
461
462 void
463 zip_member_ref (struct zip_member *zm)
464 {
465   zm->ref_cnt++;
466 }
467
468
469
470
471 void
472 zip_member_unref (struct zip_member *zm)
473 {
474   if ( zm == NULL)
475     return;
476
477   if (--zm->ref_cnt == 0)
478     {
479       decompressors[zm->compression].finish (zm);
480       if (zm->fp)
481         fclose (zm->fp);
482       free (zm->name);
483       free (zm);
484     }
485 }
486
487
488 \f
489
490 static bool probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off);
491
492
493 /* Search for something that looks like the End Of Central Directory in FP.
494    If found, the offset of the record will be placed in OFF.
495    Returns true if found false otherwise.
496 */
497 static bool
498 find_eocd (FILE *fp, off_t *off)
499 {
500   off_t start, stop;
501   const uint32_t magic = MAGIC_EOCD;
502   bool found = false;
503
504   /* The magic cannot be more than 22 bytes from the end of the file, 
505      because that is the minimum length of the EndOfCentralDirectory
506      record.
507    */
508   if ( 0 > fseeko (fp, -22, SEEK_END))
509     {
510       return false;
511     }
512   start = ftello (fp);
513   stop = start + sizeof (magic);
514   do 
515     {
516       found = probe_magic (fp, magic, start, stop, off);
517       /* FIXME: For extra confidence lookup the directory start record here*/
518       if ( start == 0)
519         break;
520       stop = start + sizeof (magic);
521       start >>= 1;
522     }
523   while (!found );
524
525   return found;
526 }
527
528
529 /*
530   Search FP for MAGIC starting at START and reaching until STOP.
531   Returns true iff MAGIC is found.  False otherwise.
532   OFF receives the location of the magic.
533 */
534 static bool
535 probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off)
536 {
537   int i;
538   int state = 0;
539   unsigned char seq[4];
540   unsigned char byte;
541
542   if ( 0 > fseeko (fp, start, SEEK_SET))
543     {
544       return -1;
545     }
546
547   for (i = 0; i < 4 ; ++i)
548     {
549       seq[i] = (magic >> i * 8) & 0xFF;
550     }
551
552   do
553     {
554       fread (&byte, 1, 1, fp);
555
556       if ( byte == seq[state])
557         state++;
558       else
559         state = 0;
560       
561       if ( state == 4)
562         {
563           *off = ftello (fp) - 4;
564           return true;
565         }
566       start++;
567       if ( start >= stop)
568         break;
569     }
570   while (!feof (fp));
571
572   return false;
573 }
574