work on PRINT encoding
[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] = zip_header_read_next (zr);
396     if (zm && 0 == strcmp (zm->name, member))
397       {
398         break;
399       }
400     else
401       {
402         zm = NULL;
403       }
404   }
405   
406   if ( zm == NULL)
407     return NULL;
408
409   if ( 0 != fseeko (zm->fp, zm->offset, SEEK_SET))
410     {
411       const char *mm = strerror (errno);
412       ds_put_format (zm->errs, _("Failed to seek to start of member `%s': %s"), zm->name, mm);
413       return NULL;
414     }
415
416   if ( ! check_magic (zm->fp, MAGIC_LHDR, zr->errs))
417     {
418       return NULL;
419     }
420
421   get_u16 (zm->fp, &v);
422   get_u16 (zm->fp, &gp);
423   get_u16 (zm->fp, &comp_type);
424   zm->compression = comp_code (zm, comp_type);
425   get_u16 (zm->fp, &time);
426   get_u16 (zm->fp, &date);
427   get_u32 (zm->fp, &crc);
428   get_u32 (zm->fp, &comp_size);
429
430   get_u32 (zm->fp, &ucomp_size);
431   get_u16 (zm->fp, &nlen);
432   get_u16 (zm->fp, &extra_len);
433
434   name = calloc (nlen + 1, sizeof (char));
435
436   get_bytes (zm->fp, name, nlen);
437
438   skip_bytes (zm->fp, extra_len);
439
440   if (strcmp (name, zm->name) != 0)
441     {
442       ds_put_format (zm->errs,
443                      _("Name mismatch in zip archive. Central directory says `%s'; local file header says `%s'"),
444                      zm->name, name);
445       free (name);
446       free (zm);
447       return NULL;
448     }
449
450   free (name);
451
452   zm->bytes_unread = zm->ucomp_size;
453
454   if ( !  decompressors[zm->compression].init (zm) )
455     return NULL;
456
457   return zm;
458 }
459
460 void
461 zip_member_ref (struct zip_member *zm)
462 {
463   zm->ref_cnt++;
464 }
465
466
467
468
469 void
470 zip_member_unref (struct zip_member *zm)
471 {
472   if ( zm == NULL)
473     return;
474
475   if (--zm->ref_cnt == 0)
476     {
477       decompressors[zm->compression].finish (zm);
478       if (zm->fp)
479         fclose (zm->fp);
480       free (zm->name);
481       free (zm);
482     }
483 }
484
485
486 \f
487
488 static bool probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off);
489
490
491 /* Search for something that looks like the End Of Central Directory in FP.
492    If found, the offset of the record will be placed in OFF.
493    Returns true if found false otherwise.
494 */
495 static bool
496 find_eocd (FILE *fp, off_t *off)
497 {
498   off_t start, stop;
499   const uint32_t magic = MAGIC_EOCD;
500   bool found = false;
501
502   /* The magic cannot be more than 22 bytes from the end of the file, 
503      because that is the minimum length of the EndOfCentralDirectory
504      record.
505    */
506   if ( 0 > fseeko (fp, -22, SEEK_END))
507     {
508       return false;
509     }
510   start = ftello (fp);
511   stop = start + sizeof (magic);
512   do 
513     {
514       found = probe_magic (fp, magic, start, stop, off);
515       /* FIXME: For extra confidence lookup the directory start record here*/
516       if ( start == 0)
517         break;
518       stop = start + sizeof (magic);
519       start >>= 1;
520     }
521   while (!found );
522
523   return found;
524 }
525
526
527 /*
528   Search FP for MAGIC starting at START and reaching until STOP.
529   Returns true iff MAGIC is found.  False otherwise.
530   OFF receives the location of the magic.
531 */
532 static bool
533 probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off)
534 {
535   int i;
536   int state = 0;
537   unsigned char seq[4];
538   unsigned char byte;
539
540   if ( 0 > fseeko (fp, start, SEEK_SET))
541     {
542       return -1;
543     }
544
545   for (i = 0; i < 4 ; ++i)
546     {
547       seq[i] = (magic >> i * 8) & 0xFF;
548     }
549
550   do
551     {
552       fread (&byte, 1, 1, fp);
553
554       if ( byte == seq[state])
555         state++;
556       else
557         state = 0;
558       
559       if ( state == 4)
560         {
561           *off = ftello (fp) - 4;
562           return true;
563         }
564       start++;
565       if ( start >= stop)
566         break;
567     }
568   while (!feof (fp));
569
570   return false;
571 }
572