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