1 #include "unzip.h"
2
3 #include <stdlib.h>
4 #include <string.h>
5 #include <ctype.h>
6 #include <assert.h>
7
8 #include <zlib.h>
9
10 /* public globals */
11 //int gUnzipQuiet = 0; /* flag controls error messages */
12
13 #define ERROR_CORRUPT "The zipfile seems to be corrupt, please check it"
14 #define ERROR_FILESYSTEM "Your filesystem seems to be corrupt, please check it"
15 #define ERROR_UNSUPPORTED "The format of this zipfile is not supported, please recompress it"
16
17 #define INFLATE_INPUT_BUFFER_MAX 16384
18 #ifndef MIN
19 #define MIN(x,y) ((x)<(y)?(x):(y))
20 #endif
21
22
23 // notaz
24 #if 1 //def __DEBUG_PRINT
25 #define logerror printf
26 #define errormsg(str1,def,fname) printf("%s: " #def ": " str1 "\n", fname);
27 #else
28 #define logerror(x...)
29 #define errormsg(x...)
30 #endif
31
32 /* Print a error message */
33 //void errormsg(const char* extmsg, const char* usermsg, const char* zipname) {
34 /* Output to the user with no internal detail */
35 // if (!gUnzipQuiet)
36 // printf("Error in zipfile %s\n%s\n", zipname, usermsg);
37 /* Output to log file with all informations */
38 // logerror("Error in zipfile %s: %s\n", zipname, extmsg);
39 // printf("Error in zipfile %s: %s\n", zipname, extmsg);
40 //}
41
42 /* -------------------------------------------------------------------------
43 Unzip support
44 ------------------------------------------------------------------------- */
45
46 /* Use these to avoid structure padding and byte-ordering problems */
read_word(char * buf)47 static UINT16 read_word (char *buf) {
48 unsigned char *ubuf = (unsigned char *) buf;
49
50 return ((UINT16)ubuf[1] << 8) | (UINT16)ubuf[0];
51 }
52
53 /* Use these to avoid structure padding and byte-ordering problems */
read_dword(char * buf)54 static UINT32 read_dword (char *buf) {
55 unsigned char *ubuf = (unsigned char *) buf;
56
57 return ((UINT32)ubuf[3] << 24) | ((UINT32)ubuf[2] << 16) | ((UINT32)ubuf[1] << 8) | (UINT32)ubuf[0];
58 }
59
60 /* Locate end-of-central-dir sig in buffer and return offset
61 out:
62 *offset offset of cent dir start in buffer
63 return:
64 ==0 not found
65 !=0 found, *offset valid
66 */
ecd_find_sig(char * buffer,int buflen,int * offset)67 static int ecd_find_sig (char *buffer, int buflen, int *offset)
68 {
69 static char ecdsig[] = { 'P', 'K', 0x05, 0x06 };
70 int i;
71 for (i=buflen-22; i>=0; i--) {
72 if (memcmp(buffer+i, ecdsig, 4) == 0) {
73 *offset = i;
74 return 1;
75 }
76 }
77 return 0;
78 }
79
80 /* Read ecd data in zip structure
81 in:
82 zip->fp, zip->length zip file
83 out:
84 zip->ecd, zip->ecd_length ecd data
85 */
ecd_read(ZIP * zip)86 static int ecd_read(ZIP* zip) {
87 char* buf;
88 int buf_length = 1024; /* initial buffer length */
89
90 while (1) {
91 int offset;
92
93 if (buf_length > zip->length)
94 buf_length = zip->length;
95
96 if (fseek(zip->fp, zip->length - buf_length, SEEK_SET) != 0) {
97 return -1;
98 }
99
100 /* allocate buffer */
101 buf = (char*)malloc( buf_length );
102 if (!buf) {
103 return -1;
104 }
105
106 if (fread( buf, buf_length, 1, zip->fp ) != 1) {
107 free(buf);
108 return -1;
109 }
110
111 if (ecd_find_sig(buf, buf_length, &offset)) {
112 zip->ecd_length = buf_length - offset;
113
114 zip->ecd = (char*)malloc( zip->ecd_length );
115 if (!zip->ecd) {
116 free(buf);
117 return -1;
118 }
119
120 memcpy(zip->ecd, buf + offset, zip->ecd_length);
121
122 free(buf);
123 return 0;
124 }
125
126 free(buf);
127
128 if (buf_length < zip->length) {
129 /* double buffer */
130 buf_length = 2*buf_length;
131
132 logerror("Retry reading of zip ecd for %d bytes\n",buf_length);
133
134 } else {
135 return -1;
136 }
137 }
138 }
139
140 /* offsets in end of central directory structure */
141 #define ZIPESIG 0x00
142 #define ZIPEDSK 0x04
143 #define ZIPECEN 0x06
144 #define ZIPENUM 0x08
145 #define ZIPECENN 0x0a
146 #define ZIPECSZ 0x0c
147 #define ZIPEOFST 0x10
148 #define ZIPECOML 0x14
149 #define ZIPECOM 0x16
150
151 /* offsets in central directory entry structure */
152 #define ZIPCENSIG 0x0
153 #define ZIPCVER 0x4
154 #define ZIPCOS 0x5
155 #define ZIPCVXT 0x6
156 #define ZIPCEXOS 0x7
157 #define ZIPCFLG 0x8
158 #define ZIPCMTHD 0xa
159 #define ZIPCTIM 0xc
160 #define ZIPCDAT 0xe
161 #define ZIPCCRC 0x10
162 #define ZIPCSIZ 0x14
163 #define ZIPCUNC 0x18
164 #define ZIPCFNL 0x1c
165 #define ZIPCXTL 0x1e
166 #define ZIPCCML 0x20
167 #define ZIPDSK 0x22
168 #define ZIPINT 0x24
169 #define ZIPEXT 0x26
170 #define ZIPOFST 0x2a
171 #define ZIPCFN 0x2e
172
173 /* offsets in local file header structure */
174 #define ZIPLOCSIG 0x00
175 #define ZIPVER 0x04
176 #define ZIPGENFLG 0x06
177 #define ZIPMTHD 0x08
178 #define ZIPTIME 0x0a
179 #define ZIPDATE 0x0c
180 #define ZIPCRC 0x0e
181 #define ZIPSIZE 0x12
182 #define ZIPUNCMP 0x16
183 #define ZIPFNLN 0x1a
184 #define ZIPXTRALN 0x1c
185 #define ZIPNAME 0x1e
186
187 /* Opens a zip stream for reading
188 return:
189 !=0 success, zip stream
190 ==0 error
191 */
openzip(const char * zipfile)192 ZIP* openzip(const char* zipfile) {
193 /* allocate */
194 ZIP* zip = (ZIP*)malloc( sizeof(ZIP) );
195 if (!zip) {
196 return 0;
197 }
198
199 /* open */
200 zip->fp = fopen(zipfile, "rb");
201 if (!zip->fp) {
202 errormsg ("Opening for reading", ERROR_FILESYSTEM, zipfile);
203 free(zip);
204 return 0;
205 }
206
207 /* go to end */
208 if (fseek(zip->fp, 0L, SEEK_END) != 0) {
209 errormsg ("Seeking to end", ERROR_FILESYSTEM, zipfile);
210 fclose(zip->fp);
211 free(zip);
212 return 0;
213 }
214
215 /* get length */
216 zip->length = ftell(zip->fp);
217 if (zip->length < 0) {
218 errormsg ("Get file size", ERROR_FILESYSTEM, zipfile);
219 fclose(zip->fp);
220 free(zip);
221 return 0;
222 }
223 if (zip->length == 0) {
224 errormsg ("Empty file", ERROR_CORRUPT, zipfile);
225 fclose(zip->fp);
226 free(zip);
227 return 0;
228 }
229
230 /* read ecd data */
231 if (ecd_read(zip)!=0) {
232 errormsg ("Reading ECD (end of central directory)", ERROR_CORRUPT, zipfile);
233 fclose(zip->fp);
234 free(zip);
235 return 0;
236 }
237
238 /* compile ecd info */
239 zip->end_of_cent_dir_sig = read_dword (zip->ecd+ZIPESIG);
240 zip->number_of_this_disk = read_word (zip->ecd+ZIPEDSK);
241 zip->number_of_disk_start_cent_dir = read_word (zip->ecd+ZIPECEN);
242 zip->total_entries_cent_dir_this_disk = read_word (zip->ecd+ZIPENUM);
243 zip->total_entries_cent_dir = read_word (zip->ecd+ZIPECENN);
244 zip->size_of_cent_dir = read_dword (zip->ecd+ZIPECSZ);
245 zip->offset_to_start_of_cent_dir = read_dword (zip->ecd+ZIPEOFST);
246 zip->zipfile_comment_length = read_word (zip->ecd+ZIPECOML);
247 zip->zipfile_comment = zip->ecd+ZIPECOM;
248
249 /* verify that we can work with this zipfile (no disk spanning allowed) */
250 if ((zip->number_of_this_disk != zip->number_of_disk_start_cent_dir) ||
251 (zip->total_entries_cent_dir_this_disk != zip->total_entries_cent_dir) ||
252 (zip->total_entries_cent_dir < 1)) {
253 errormsg("Cannot span disks", ERROR_UNSUPPORTED, zipfile);
254 free(zip->ecd);
255 fclose(zip->fp);
256 free(zip);
257 return 0;
258 }
259
260 if (fseek(zip->fp, zip->offset_to_start_of_cent_dir, SEEK_SET)!=0) {
261 errormsg ("Seeking to central directory", ERROR_CORRUPT, zipfile);
262 free(zip->ecd);
263 fclose(zip->fp);
264 free(zip);
265 return 0;
266 }
267
268 /* read from start of central directory */
269 zip->cd = (char*)malloc( zip->size_of_cent_dir );
270 if (!zip->cd) {
271 free(zip->ecd);
272 fclose(zip->fp);
273 free(zip);
274 return 0;
275 }
276
277 if (fread(zip->cd, zip->size_of_cent_dir, 1, zip->fp)!=1) {
278 errormsg ("Reading central directory", ERROR_CORRUPT, zipfile);
279 free(zip->cd);
280 free(zip->ecd);
281 fclose(zip->fp);
282 free(zip);
283 return 0;
284 }
285
286 /* reset ent */
287 zip->ent.name = 0;
288
289 /* rewind */
290 zip->cd_pos = 0;
291
292 /* file name */
293 zip->zip = (char*)malloc(strlen(zipfile)+1);
294 if (!zip->zip) {
295 free(zip->cd);
296 free(zip->ecd);
297 fclose(zip->fp);
298 free(zip);
299 return 0;
300 }
301 strcpy(zip->zip, zipfile);
302
303 return zip;
304 }
305
306 /* Reads the current entry from a zip stream
307 in:
308 zip opened zip
309 return:
310 !=0 success
311 ==0 error
312 */
readzip(ZIP * zip)313 struct zipent* readzip(ZIP* zip) {
314
315 /* end of directory */
316 if (zip->cd_pos >= zip->size_of_cent_dir)
317 return 0;
318
319 /* compile zipent info */
320 zip->ent.cent_file_header_sig = read_dword (zip->cd+zip->cd_pos+ZIPCENSIG);
321 zip->ent.version_made_by = *(zip->cd+zip->cd_pos+ZIPCVER);
322 zip->ent.host_os = *(zip->cd+zip->cd_pos+ZIPCOS);
323 zip->ent.version_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCVXT);
324 zip->ent.os_needed_to_extract = *(zip->cd+zip->cd_pos+ZIPCEXOS);
325 zip->ent.general_purpose_bit_flag = read_word (zip->cd+zip->cd_pos+ZIPCFLG);
326 zip->ent.compression_method = read_word (zip->cd+zip->cd_pos+ZIPCMTHD);
327 zip->ent.last_mod_file_time = read_word (zip->cd+zip->cd_pos+ZIPCTIM);
328 zip->ent.last_mod_file_date = read_word (zip->cd+zip->cd_pos+ZIPCDAT);
329 zip->ent.crc32 = read_dword (zip->cd+zip->cd_pos+ZIPCCRC);
330 zip->ent.compressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCSIZ);
331 zip->ent.uncompressed_size = read_dword (zip->cd+zip->cd_pos+ZIPCUNC);
332 zip->ent.filename_length = read_word (zip->cd+zip->cd_pos+ZIPCFNL);
333 zip->ent.extra_field_length = read_word (zip->cd+zip->cd_pos+ZIPCXTL);
334 zip->ent.file_comment_length = read_word (zip->cd+zip->cd_pos+ZIPCCML);
335 zip->ent.disk_number_start = read_word (zip->cd+zip->cd_pos+ZIPDSK);
336 zip->ent.internal_file_attrib = read_word (zip->cd+zip->cd_pos+ZIPINT);
337 zip->ent.external_file_attrib = read_dword (zip->cd+zip->cd_pos+ZIPEXT);
338 zip->ent.offset_lcl_hdr_frm_frst_disk = read_dword (zip->cd+zip->cd_pos+ZIPOFST);
339
340 /* check to see if filename length is illegally long (past the size of this directory
341 entry) */
342 if (zip->cd_pos + ZIPCFN + zip->ent.filename_length > zip->size_of_cent_dir)
343 {
344 errormsg("Invalid filename length in directory", ERROR_CORRUPT,zip->zip);
345 return 0;
346 }
347
348 /* copy filename */
349 free(zip->ent.name);
350 zip->ent.name = (char*)malloc(zip->ent.filename_length + 1);
351 memcpy(zip->ent.name, zip->cd+zip->cd_pos+ZIPCFN, zip->ent.filename_length);
352 zip->ent.name[zip->ent.filename_length] = 0;
353
354 /* skip to next entry in central dir */
355 zip->cd_pos += ZIPCFN + zip->ent.filename_length + zip->ent.extra_field_length + zip->ent.file_comment_length;
356
357 return &zip->ent;
358 }
359
360 /* Closes a zip stream */
closezip(ZIP * zip)361 void closezip(ZIP* zip) {
362 /* release all */
363 free(zip->ent.name);
364 free(zip->cd);
365 free(zip->ecd);
366 /* only if not suspended */
367 if (zip->fp)
368 fclose(zip->fp);
369 free(zip->zip);
370 free(zip);
371 }
372
373 /* Suspend access to a zip file (release file handler)
374 in:
375 zip opened zip
376 note:
377 A suspended zip is automatically reopened at first call of
378 readuncompressd() or readcompressed() functions
379 */
suspendzip(ZIP * zip)380 void suspendzip(ZIP* zip) {
381 if (zip->fp) {
382 fclose(zip->fp);
383 zip->fp = 0;
384 }
385 }
386
387 /* Revive a suspended zip file (reopen file handler)
388 in:
389 zip suspended zip
390 return:
391 zip success
392 ==0 error (zip must be closed with closezip)
393 */
revivezip(ZIP * zip)394 static ZIP* revivezip(ZIP* zip) {
395 if (!zip->fp) {
396 zip->fp = fopen(zip->zip, "rb");
397 if (!zip->fp) {
398 return 0;
399 }
400 }
401 return zip;
402
403 }
404
405 /* Reset a zip stream to the first entry
406 in:
407 zip opened zip
408 note:
409 ZIP file must be opened and not suspended
410 */
rewindzip(ZIP * zip)411 void rewindzip(ZIP* zip) {
412 zip->cd_pos = 0;
413 }
414
415 /* Seek zip->fp to compressed data
416 return:
417 ==0 success
418 <0 error
419 */
seekcompresszip(ZIP * zip,struct zipent * ent)420 int seekcompresszip(ZIP* zip, struct zipent* ent) {
421 char buf[ZIPNAME];
422 long offset;
423
424 if (!zip->fp) {
425 if (!revivezip(zip))
426 return -1;
427 }
428
429 if (fseek(zip->fp, ent->offset_lcl_hdr_frm_frst_disk, SEEK_SET)!=0) {
430 errormsg ("Seeking to header", ERROR_CORRUPT, zip->zip);
431 return -1;
432 }
433
434 if (fread(buf, ZIPNAME, 1, zip->fp)!=1) {
435 errormsg ("Reading header", ERROR_CORRUPT, zip->zip);
436 return -1;
437 }
438
439 {
440 UINT16 filename_length = read_word (buf+ZIPFNLN);
441 UINT16 extra_field_length = read_word (buf+ZIPXTRALN);
442
443 /* calculate offset to data and fseek() there */
444 offset = ent->offset_lcl_hdr_frm_frst_disk + ZIPNAME + filename_length + extra_field_length;
445
446 if (fseek(zip->fp, offset, SEEK_SET) != 0) {
447 errormsg ("Seeking to compressed data", ERROR_CORRUPT, zip->zip);
448 return -1;
449 }
450
451 }
452
453 return 0;
454 }
455
456 /* Inflate a file
457 in:
458 in_file stream to inflate
459 in_size size of the compressed data to read
460 out_size size of decompressed data
461 out:
462 out_data buffer for decompressed data
463 return:
464 ==0 ok
465
466 990525 rewritten for use with zlib MLR
467 */
inflate_file(FILE * in_file,unsigned in_size,unsigned char * out_data,unsigned out_size)468 static int inflate_file(FILE* in_file, unsigned in_size, unsigned char* out_data, unsigned out_size)
469 {
470 int err;
471 unsigned char* in_buffer;
472 z_stream d_stream; /* decompression stream */
473
474 d_stream.zalloc = 0;
475 d_stream.zfree = 0;
476 d_stream.opaque = 0;
477
478 d_stream.next_in = 0;
479 d_stream.avail_in = 0;
480 d_stream.next_out = out_data;
481 d_stream.avail_out = out_size;
482
483 err = inflateInit2(&d_stream, -MAX_WBITS);
484 /* windowBits is passed < 0 to tell that there is no zlib header.
485 * Note that in this case inflate *requires* an extra "dummy" byte
486 * after the compressed stream in order to complete decompression and
487 * return Z_STREAM_END.
488 */
489 if (err != Z_OK)
490 {
491 logerror("inflateInit error: %d\n", err);
492 return -1;
493 }
494
495 in_buffer = (unsigned char*)malloc(INFLATE_INPUT_BUFFER_MAX+1);
496 if (!in_buffer)
497 return -1;
498
499 for (;;)
500 {
501 if (in_size <= 0)
502 {
503 logerror("inflate error: compressed size too small\n");
504 free (in_buffer);
505 return -1;
506 }
507 d_stream.next_in = in_buffer;
508 d_stream.avail_in = fread (in_buffer, 1, MIN(in_size, INFLATE_INPUT_BUFFER_MAX), in_file);
509 in_size -= d_stream.avail_in;
510 if (in_size == 0)
511 d_stream.avail_in++; /* add dummy byte at end of compressed data */
512
513 err = inflate(&d_stream, Z_NO_FLUSH);
514 if (err == Z_STREAM_END)
515 break;
516 if (err != Z_OK)
517 {
518 logerror("inflate error: %d\n", err);
519 free (in_buffer);
520 return -1;
521 }
522 }
523
524 err = inflateEnd(&d_stream);
525 if (err != Z_OK)
526 {
527 logerror("inflateEnd error: %d\n", err);
528 free (in_buffer);
529 return -1;
530 }
531
532 free (in_buffer);
533
534 if ((d_stream.avail_out > 0) || (in_size > 0))
535 {
536 logerror("zip size mismatch. %i\n", in_size);
537 return -1;
538 }
539
540 return 0;
541 }
542
543 /* Read compressed data
544 out:
545 data compressed data read
546 return:
547 ==0 success
548 <0 error
549 */
readcompresszip(ZIP * zip,struct zipent * ent,char * data)550 int readcompresszip(ZIP* zip, struct zipent* ent, char* data) {
551 int err = seekcompresszip(zip,ent);
552 if (err!=0)
553 return err;
554
555 if (fread(data, ent->compressed_size, 1, zip->fp)!=1) {
556 errormsg ("Reading compressed data", ERROR_CORRUPT, zip->zip);
557 return -1;
558 }
559
560 return 0;
561 }
562
563 /* Read UNcompressed data
564 out:
565 data UNcompressed data
566 return:
567 ==0 success
568 <0 error
569 */
readuncompresszip(ZIP * zip,struct zipent * ent,char * data)570 int readuncompresszip(ZIP* zip, struct zipent* ent, char* data) {
571 if (ent->compression_method == 0x0000) {
572 /* file is not compressed, simply stored */
573
574 /* check if size are equal */
575 if (ent->compressed_size != ent->uncompressed_size) {
576 errormsg("Wrong uncompressed size in store compression", ERROR_CORRUPT,zip->zip);
577 return -3;
578 }
579
580 return readcompresszip(zip,ent,data);
581 } else if (ent->compression_method == 0x0008) {
582 /* file is compressed using "Deflate" method */
583 if (ent->version_needed_to_extract > 0x14) {
584 errormsg("Version too new", ERROR_UNSUPPORTED,zip->zip);
585 return -2;
586 }
587
588 if (ent->os_needed_to_extract != 0x00) {
589 errormsg("OS not supported", ERROR_UNSUPPORTED,zip->zip);
590 return -2;
591 }
592
593 if (ent->disk_number_start != zip->number_of_this_disk) {
594 errormsg("Cannot span disks", ERROR_UNSUPPORTED,zip->zip);
595 return -2;
596 }
597
598 /* read compressed data */
599 if (seekcompresszip(zip,ent)!=0) {
600 return -1;
601 }
602
603 /* configure inflate */
604 if (inflate_file( zip->fp, ent->compressed_size, (unsigned char*)data, ent->uncompressed_size))
605 {
606 errormsg("Inflating compressed data", ERROR_CORRUPT, zip->zip);
607 return -3;
608 }
609
610 return 0;
611 } else {
612 errormsg("Compression method unsupported", ERROR_UNSUPPORTED, zip->zip);
613 return -2;
614 }
615 }
616