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