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