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