1 /*
2 * This file is part of the Advance project.
3 *
4 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Andrea Mazzoleni
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "portable.h"
22
23 #include "zip.h"
24 #include "siglock.h"
25 #include "file.h"
26 #include "data.h"
27 #include "lib/endianrw.h"
28
29 #include <zlib.h>
30
31 #include <iostream>
32 #include <iomanip>
33 #include <string>
34 #include <sstream>
35 #include <set>
36
37 using namespace std;
38
39 /**
40 * Enable pendantic checks on the zip integrity.
41 */
42 bool zip::pedantic = false;
43
ecd_compare_sig(const unsigned char * buffer)44 bool ecd_compare_sig(const unsigned char *buffer)
45 {
46 static char ecdsig[] = { 'P', 'K', 0x05, 0x06 };
47 return memcmp(buffer, ecdsig, 4) == 0;
48 }
49
50 /**
51 * Locate end-of-central-dir sig in buffer and return offset
52 * \param offset Resulting offset of cent dir start in buffer.
53 * \reutn If the end-of-central-dir is found.
54 */
ecd_find_sig(const unsigned char * buffer,unsigned buflen,unsigned & offset)55 bool ecd_find_sig (const unsigned char *buffer, unsigned buflen, unsigned& offset)
56 {
57 for (int i=buflen-22; i>=0; i--) {
58 if (ecd_compare_sig(buffer+i)) {
59 offset = i;
60 return true;
61 }
62 }
63 return false;
64 }
65
66 #define ECD_READ_BUFFER_SIZE 4096
67
68 /**
69 * Read cent dir and end cent dir data
70 * \param f File to read.
71 * \param length Length of the file.
72 */
cent_read(FILE * f,unsigned length,unsigned char * & data,unsigned & size)73 bool cent_read(FILE* f, unsigned length, unsigned char*& data, unsigned& size)
74 {
75 unsigned buf_length;
76
77 if (length <= ECD_READ_BUFFER_SIZE) {
78 buf_length = length;
79 } else {
80 // align the read
81 buf_length = length - ((length - ECD_READ_BUFFER_SIZE) & ~(ECD_READ_BUFFER_SIZE-1));
82 }
83
84 while (true) {
85 if (buf_length > length)
86 buf_length = length;
87
88 if (fseek(f, length - buf_length, SEEK_SET) != 0) {
89 return false;
90 }
91
92 // allocate buffer
93 unsigned char* buf = data_alloc(buf_length);
94 assert(buf);
95
96 if (fread(buf, buf_length, 1, f) != 1) {
97 data_free(buf);
98 return false;
99 }
100
101 unsigned offset = 0;
102 if (ecd_find_sig(buf, buf_length, offset)) {
103 unsigned start_of_cent_dir = le_uint32_read(buf + offset + ZIP_EO_offset_to_start_of_cent_dir);
104 unsigned buf_pos = length - buf_length;
105
106 if (start_of_cent_dir >= length) {
107 data_free(buf);
108 return false;
109 }
110
111 size = length - start_of_cent_dir;
112
113 data = data_alloc(size);
114 assert(data);
115
116 if (buf_pos <= start_of_cent_dir) {
117 memcpy(data, buf + (start_of_cent_dir - buf_pos), size);
118 data_free(buf);
119 } else {
120 data_free(buf);
121
122 if (fseek(f, start_of_cent_dir, SEEK_SET) != 0) {
123 data_free(data);
124 data = 0;
125 return false;
126 }
127
128 if (fread(data, size, 1, f) != 1) {
129 data_free(data);
130 data = 0;
131 return false;
132 }
133 }
134 return true;
135 }
136
137 data_free(buf);
138
139 if (buf_length < 8 * ECD_READ_BUFFER_SIZE && buf_length < length) {
140 // grow buffer
141 buf_length += ECD_READ_BUFFER_SIZE;
142 } else {
143 // aborting
144 return false;
145 }
146 }
147 }
148
149 /** Code used for disk entry. */
150 #define ZIP_UNIQUE_DISK 0
151
152 /** Convert time_t to zip format. */
time2zip(time_t tod,unsigned & date,unsigned & time)153 void time2zip(time_t tod, unsigned& date, unsigned& time)
154 {
155 struct tm* tm = gmtime(&tod);
156 assert(tm);
157 unsigned day = tm->tm_mday; // 1-31
158 unsigned mon = tm->tm_mon + 1; // 1-12
159 unsigned year = tm->tm_year - 80; // since 1980
160 date = (day & 0x1F) | ((mon & 0xF) << 5) | ((year & 0x7F) << 9);
161 unsigned sec = tm->tm_sec / 2; // 0-29, double to get real seconds
162 unsigned min = tm->tm_min; // 0-59
163 unsigned hour = tm->tm_hour; // 0-23
164 time = (sec & 0x1F) | ((min & 0x3F) << 5) | ((hour & 0x1F) << 11);
165 }
166
167 /** Convert zip time to to time_t. */
zip2time(unsigned date,unsigned time)168 time_t zip2time(unsigned date, unsigned time)
169 {
170 struct tm tm;
171 // reset all entry
172 memset(&tm, 0, sizeof(tm));
173 // set know entry
174 tm.tm_mday = date & 0x1F; // 1-31
175 tm.tm_mon = ((date >> 5) & 0xF) - 1; // 0-11
176 tm.tm_year = ((date >> 9) & 0x7f) + 80; // since 1900
177 tm.tm_sec = (time & 0x1F) * 2; // 0-59
178 tm.tm_min = (time >> 5) & 0x3F; // 0-59
179 tm.tm_hour = (time >> 11) & 0x1F; // 0-23
180 return mktime(&tm);
181 }
182
zip_entry(const zip & Aparent)183 zip_entry::zip_entry(const zip& Aparent)
184 {
185 memset(&info, 0xFF, sizeof(info));
186
187 parent_name = Aparent.file_get();
188
189 info.filename_length = 0;
190 file_name = 0;
191
192 info.local_extra_field_length = 0;
193 local_extra_field = 0;
194
195 info.central_extra_field_length = 0;
196 central_extra_field = 0;
197
198 info.file_comment_length = 0;
199 file_comment = 0;
200
201 info.compressed_size = 0;
202 data = 0;
203 }
204
zip_entry(const zip_entry & A)205 zip_entry::zip_entry(const zip_entry& A)
206 {
207 info = A.info;
208 parent_name = A.parent_name;
209 file_name = data_dup(A.file_name, info.filename_length);
210 local_extra_field = data_dup(A.local_extra_field, info.local_extra_field_length);
211 central_extra_field = data_dup(A.central_extra_field, info.central_extra_field_length);
212 file_comment = data_dup(A.file_comment, info.file_comment_length);
213 data = data_dup(A.data, A.info.compressed_size);
214 }
215
~zip_entry()216 zip_entry::~zip_entry()
217 {
218 data_free(file_name);
219 data_free(local_extra_field);
220 data_free(central_extra_field);
221 data_free(file_comment);
222 data_free(data);
223 }
224
method_get() const225 zip_entry::method_t zip_entry::method_get() const
226 {
227 switch (info.compression_method) {
228 case ZIP_METHOD_STORE :
229 return store;
230 case ZIP_METHOD_SHRUNK :
231 return shrunk;
232 case ZIP_METHOD_REDUCE1 :
233 return reduce1;
234 case ZIP_METHOD_REDUCE2 :
235 return reduce2;
236 case ZIP_METHOD_REDUCE3 :
237 return reduce3;
238 case ZIP_METHOD_REDUCE4 :
239 return reduce4;
240 case ZIP_METHOD_IMPLODE :
241 switch (info.general_purpose_bit_flag & ZIP_GEN_FLAGS_IMPLODE_MASK) {
242 case ZIP_GEN_FLAGS_IMPLODE_4KD2T :
243 return implode_4kdict_2tree;
244 case ZIP_GEN_FLAGS_IMPLODE_8KD2T :
245 return implode_8kdict_2tree;
246 case ZIP_GEN_FLAGS_IMPLODE_4KD3T :
247 return implode_4kdict_3tree;
248 case ZIP_GEN_FLAGS_IMPLODE_8KD3T :
249 return implode_8kdict_3tree;
250 }
251 return unknown;
252 case ZIP_METHOD_DEFLATE :
253 switch (info.general_purpose_bit_flag & ZIP_GEN_FLAGS_DEFLATE_MASK) {
254 case ZIP_GEN_FLAGS_DEFLATE_NORMAL :
255 return deflate6;
256 case ZIP_GEN_FLAGS_DEFLATE_MAXIMUM :
257 return deflate9;
258 case ZIP_GEN_FLAGS_DEFLATE_FAST :
259 return deflate3;
260 case ZIP_GEN_FLAGS_DEFLATE_SUPERFAST :
261 return deflate1;
262 }
263 return unknown;
264 case ZIP_METHOD_BZIP2 :
265 return bzip2;
266 case ZIP_METHOD_LZMA :
267 return lzma;
268 }
269
270 return unknown;
271 }
272
is_text() const273 bool zip_entry::is_text() const
274 {
275 return (info.internal_file_attrib & ZIP_INT_ATTR_TEXT) != 0;
276 }
277
compressed_seek(FILE * f) const278 void zip_entry::compressed_seek(FILE* f) const
279 {
280 // seek to local header
281 if (fseek(f, offset_get(), SEEK_SET) != 0) {
282 throw error_invalid() << "Failed seek " << parentname_get();
283 }
284
285 // read local header
286 unsigned char buf[ZIP_LO_FIXED];
287 if (fread(buf, ZIP_LO_FIXED, 1, f) != 1) {
288 throw error() << "Failed read " << parentname_get();
289 }
290
291 check_local(buf);
292
293 // use the local extra_field_length. It may be different than the
294 // central directory version in some zips.
295 unsigned local_extra_field_length = le_uint16_read(buf+ZIP_LO_extra_field_length);
296
297 // seek to data
298 if (fseek(f, info.filename_length + local_extra_field_length, SEEK_CUR) != 0) {
299 throw error_invalid() << "Failed seek " << parentname_get();
300 }
301 }
302
compressed_read(unsigned char * outdata) const303 void zip_entry::compressed_read(unsigned char* outdata) const
304 {
305 if (data) {
306 memcpy(outdata, data, compressed_size_get());
307 } else {
308 FILE* f = fopen(parentname_get().c_str(), "rb");
309 if (!f) {
310 throw error() << "Failed open for reading " << parentname_get();
311 }
312
313 try {
314 compressed_seek(f);
315
316 if (compressed_size_get() > 0) {
317 if (fread(outdata, compressed_size_get(), 1, f) != 1) {
318 throw error() << "Failed read " << parentname_get();
319 }
320 }
321
322 } catch (...) {
323 fclose(f);
324 throw;
325 }
326
327 fclose(f);
328 }
329 }
330
time_get() const331 time_t zip_entry::time_get() const
332 {
333 return zip2time(info.last_mod_file_date, info.last_mod_file_time);
334 }
335
time_set(time_t tod)336 void zip_entry::time_set(time_t tod)
337 {
338 time2zip(tod, info.last_mod_file_date, info.last_mod_file_time);
339 }
340
set(method_t method,const string & Aname,const unsigned char * compdata,unsigned compsize,unsigned size,unsigned crc,unsigned date,unsigned time,bool is_text)341 void zip_entry::set(method_t method, const string& Aname, const unsigned char* compdata, unsigned compsize, unsigned size, unsigned crc, unsigned date, unsigned time, bool is_text)
342 {
343 info.version_needed_to_extract = 20; // version 2.0
344 info.os_needed_to_extract = 0;
345 info.version_made_by = 20; // version 2.0
346 info.host_os = 0;
347 info.general_purpose_bit_flag = 0; // default
348 info.last_mod_file_date = date;
349 info.last_mod_file_time = time;
350 info.crc32 = crc;
351 info.compressed_size = compsize;
352 info.uncompressed_size = size;
353 info.internal_file_attrib = is_text ? ZIP_INT_ATTR_TEXT : 0;
354 info.external_file_attrib = 0;
355
356 switch (method) {
357 case store :
358 if (size != compsize) {
359 throw error_invalid() << "Zip entry size mismatch";
360 }
361 info.compression_method = ZIP_METHOD_STORE;
362 info.version_needed_to_extract = 10; // Version 1.0
363 break;
364 case shrunk :
365 info.compression_method = ZIP_METHOD_SHRUNK;
366 break;
367 case reduce1 :
368 info.compression_method = ZIP_METHOD_REDUCE1;
369 break;
370 case reduce2 :
371 info.compression_method = ZIP_METHOD_REDUCE2;
372 break;
373 case reduce3 :
374 info.compression_method = ZIP_METHOD_REDUCE3;
375 break;
376 case reduce4 :
377 info.compression_method = ZIP_METHOD_REDUCE4;
378 break;
379 case deflate1 :
380 case deflate2 :
381 info.compression_method = ZIP_METHOD_DEFLATE;
382 info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_DEFLATE_SUPERFAST;
383 break;
384 case deflate3 :
385 case deflate4 :
386 case deflate5 :
387 info.compression_method = ZIP_METHOD_DEFLATE;
388 info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_DEFLATE_FAST;
389 break;
390 case deflate6 :
391 case deflate7 :
392 case deflate8 :
393 info.compression_method = ZIP_METHOD_DEFLATE;
394 info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_DEFLATE_NORMAL;
395 break;
396 case deflate9 :
397 info.compression_method = ZIP_METHOD_DEFLATE;
398 info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_DEFLATE_MAXIMUM;
399 break;
400 case implode_4kdict_2tree :
401 info.compression_method = ZIP_METHOD_IMPLODE;
402 info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_IMPLODE_4KD2T;
403 break;
404 case implode_8kdict_2tree :
405 info.compression_method = ZIP_METHOD_IMPLODE;
406 info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_IMPLODE_8KD2T;
407 break;
408 case implode_4kdict_3tree :
409 info.compression_method = ZIP_METHOD_IMPLODE;
410 info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_IMPLODE_4KD3T;
411 break;
412 case implode_8kdict_3tree :
413 info.compression_method = ZIP_METHOD_IMPLODE;
414 info.general_purpose_bit_flag |= ZIP_GEN_FLAGS_IMPLODE_8KD3T;
415 break;
416 case bzip2 :
417 info.compression_method = ZIP_METHOD_BZIP2;
418 break;
419 case lzma :
420 info.compression_method = ZIP_METHOD_LZMA;
421 break;
422 default :
423 throw error_invalid() << "Compression method not supported";
424 }
425
426 data_free(data);
427 info.compressed_size = compsize;
428 data = data_dup(compdata, info.compressed_size);
429
430 name_set(Aname);
431
432 data_free(local_extra_field);
433 info.local_extra_field_length = 0;
434 local_extra_field = 0;
435
436 data_free(central_extra_field);
437 info.central_extra_field_length = 0;
438 central_extra_field = 0;
439
440 data_free(file_comment);
441 info.file_comment_length = 0;
442 file_comment = 0;
443 }
444
name_set(const string & Aname)445 void zip_entry::name_set(const string& Aname)
446 {
447 data_free(file_name);
448 info.filename_length = Aname.length();
449 file_name = data_alloc(info.filename_length);
450 memcpy(file_name, Aname.c_str(), info.filename_length);
451 }
452
name_get() const453 string zip_entry::name_get() const
454 {
455 return string(file_name, file_name + info.filename_length);
456 }
457
458 /** Check central directory entry. */
check_cent(const unsigned char * buf,unsigned buf_size) const459 void zip_entry::check_cent(const unsigned char* buf, unsigned buf_size) const
460 {
461 if (buf_size < ZIP_CO_FIXED) {
462 throw error_invalid() << "Invalid central directory data";
463 }
464 // check signature
465 if (le_uint32_read(buf+ZIP_CO_central_file_header_signature) != ZIP_C_signature) {
466 throw error_invalid() << "Invalid central directory signature";
467 }
468 // check filename_length > 0, can't exist a file without a name
469 if (le_uint16_read(buf+ZIP_CO_filename_length) == 0) {
470 throw error_invalid() << "Empty filename in central directory";
471 }
472 }
473
474 /** Check local file header comparing with internal information. */
check_local(const unsigned char * buf) const475 void zip_entry::check_local(const unsigned char* buf) const
476 {
477 if (le_uint32_read(buf+ZIP_LO_local_file_header_signature) != ZIP_L_signature) {
478 throw error_invalid() << "Invalid signature in local header";
479 }
480 if (info.general_purpose_bit_flag != le_uint16_read(buf+ZIP_LO_general_purpose_bit_flag)) {
481 throw error_invalid() << "Invalid local purpose bit flag " << info.general_purpose_bit_flag << "/" << le_uint16_read(buf+ZIP_LO_general_purpose_bit_flag);
482 }
483 if (info.compression_method != le_uint16_read(buf+ZIP_LO_compression_method)) {
484 throw error_invalid() << "Invalid method on local header";
485 }
486 if ((le_uint16_read(buf+ZIP_LO_general_purpose_bit_flag) & ZIP_GEN_FLAGS_DEFLATE_ZERO) != 0) {
487 if (zip::pedantic) {
488 if (le_uint32_read(buf+ZIP_LO_crc32) != 0) {
489 throw error_invalid() << "Not zero crc on local header " << le_uint32_read(buf+ZIP_LO_crc32);
490 }
491 if (le_uint32_read(buf+ZIP_LO_compressed_size) != 0) {
492 throw error_invalid() << "Not zero compressed size in local header " << le_uint32_read(buf+ZIP_LO_compressed_size);
493 }
494 if (le_uint32_read(buf+ZIP_LO_uncompressed_size) != 0) {
495 throw error_invalid() << "Not zero uncompressed size in local header " << le_uint32_read(buf+ZIP_LO_uncompressed_size);
496 }
497 } else {
498 // allow the entries to have the correct value instead of 0
499 if (le_uint32_read(buf+ZIP_LO_crc32) != 0 && info.crc32 != le_uint32_read(buf+ZIP_LO_crc32)) {
500 throw error_invalid() << "Not zero crc on local header " << le_uint32_read(buf+ZIP_LO_crc32);
501 }
502 if (le_uint32_read(buf+ZIP_LO_compressed_size) != 0 && info.compressed_size != le_uint32_read(buf+ZIP_LO_compressed_size)) {
503 throw error_invalid() << "Not zero compressed size in local header " << le_uint32_read(buf+ZIP_LO_compressed_size);
504 }
505 if (le_uint32_read(buf+ZIP_LO_uncompressed_size) != 0 && info.uncompressed_size != le_uint32_read(buf+ZIP_LO_uncompressed_size)) {
506 throw error_invalid() << "Not zero uncompressed size in local header " << le_uint32_read(buf+ZIP_LO_uncompressed_size);
507 }
508 }
509 } else {
510 if (info.crc32 != le_uint32_read(buf+ZIP_LO_crc32)) {
511 throw error_invalid() << "Invalid crc on local header " << info.crc32 << "/" << le_uint32_read(buf+ZIP_LO_crc32);
512 }
513 if (info.compressed_size != le_uint32_read(buf+ZIP_LO_compressed_size)) {
514 throw error_invalid() << "Invalid compressed size in local header " << info.compressed_size << "/" << le_uint32_read(buf+ZIP_LO_compressed_size);
515 }
516 if (info.uncompressed_size != le_uint32_read(buf+ZIP_LO_uncompressed_size)) {
517 throw error_invalid() << "Invalid uncompressed size in local header " << info.uncompressed_size << "/" << le_uint32_read(buf+ZIP_LO_uncompressed_size);
518 }
519 }
520 if (info.filename_length != le_uint16_read(buf+ZIP_LO_filename_length)) {
521 throw error_invalid() << "Invalid filename in local header";
522 }
523 if (info.local_extra_field_length != le_uint16_read(buf+ZIP_LO_extra_field_length)
524 && info.local_extra_field_length != 0 // the .zip generated with the info-zip program have the extra field only on the local header
525 ) {
526 throw error_invalid() << "Invalid extra field length in local header " << info.local_extra_field_length << "/" << le_uint16_read(buf+ZIP_LO_extra_field_length);
527 }
528 }
529
check_descriptor(const unsigned char * buf) const530 void zip_entry::check_descriptor(const unsigned char* buf) const
531 {
532 if (0x08074b50 != le_uint32_read(buf+ZIP_DO_header_signature)) {
533 throw error_invalid() << "Invalid header signature on data descriptor " << le_uint32_read(buf+ZIP_DO_crc32);
534 }
535 if (info.crc32 != le_uint32_read(buf+ZIP_DO_crc32)) {
536 throw error_invalid() << "Invalid crc on data descriptor " << info.crc32 << "/" << le_uint32_read(buf+ZIP_DO_crc32);
537 }
538 if (info.compressed_size != le_uint32_read(buf+ZIP_DO_compressed_size)
539 // allow a 0 size, GNU unzip also allow it
540 && 0 != le_uint32_read(buf+ZIP_DO_compressed_size)) {
541 throw error_invalid() << "Invalid compressed size in data descriptor " << info.compressed_size << "/" << le_uint32_read(buf+ZIP_DO_compressed_size);
542 }
543 if (info.uncompressed_size != le_uint32_read(buf+ZIP_DO_uncompressed_size)
544 // allow a 0 size, GNU unzip also allow it
545 && 0 != le_uint32_read(buf+ZIP_DO_uncompressed_size)) {
546 throw error_invalid() << "Invalid uncompressed size in data descriptor " << info.uncompressed_size << "/" << le_uint32_read(buf+ZIP_DO_uncompressed_size);
547 }
548 }
549
550 /** Unload compressed/uncomressed data. */
unload()551 void zip_entry::unload()
552 {
553 data_free(data);
554 data = 0;
555 }
556
557 /**
558 * Load local file header.
559 * \param buf Fixed size local header.
560 * \param f File seeked after the fixed size local header.
561 */
load_local(const unsigned char * buf,FILE * f,unsigned size)562 void zip_entry::load_local(const unsigned char* buf, FILE* f, unsigned size)
563 {
564 check_local(buf);
565
566 // use the local extra_field_length. It may be different than the
567 // central directory version in some zips.
568 unsigned local_extra_field_length = le_uint16_read(buf+ZIP_LO_extra_field_length);
569
570 if (size < info.filename_length + local_extra_field_length) {
571 throw error_invalid() << "Overflow of filename";
572 }
573 size -= info.filename_length + local_extra_field_length;
574
575 // skip filename and extra field
576 if (fseek(f, info.filename_length + local_extra_field_length, SEEK_CUR) != 0) {
577 throw error_invalid() << "Failed seek";
578 }
579
580 data_free(data);
581 data = data_alloc(info.compressed_size);
582
583 if (size < info.compressed_size) {
584 throw error_invalid() << "Overflow of compressed data";
585 }
586 size -= info.compressed_size;
587
588 try {
589 if (info.compressed_size > 0) {
590 if (fread(data, info.compressed_size, 1, f) != 1) {
591 throw error() << "Failed read";
592 }
593 }
594 } catch (...) {
595 data_free(data);
596 data = 0;
597 throw;
598 }
599
600 // load the data descriptor
601 if ((le_uint16_read(buf+ZIP_LO_general_purpose_bit_flag) & ZIP_GEN_FLAGS_DEFLATE_ZERO) != 0) {
602 unsigned char data_desc[ZIP_DO_FIXED];
603 unsigned offset;
604
605 // handle the case of the ZIP_DO_header_signature missing
606 if (size == ZIP_DO_FIXED - 4) {
607 le_uint32_write(data_desc+ZIP_DO_header_signature, 0x08074b50);
608 offset = ZIP_DO_crc32;
609 } else {
610 offset = 0;
611 }
612
613 if (size < ZIP_DO_FIXED - offset) {
614 throw error_invalid() << "Overflow of data descriptor";
615 }
616
617 if (fread(data_desc + offset, ZIP_DO_FIXED - offset, 1, f) != 1) {
618 throw error() << "Failed read";
619 }
620 size -= ZIP_DO_FIXED - offset;
621
622 check_descriptor(data_desc);
623 }
624 }
625
626 /**
627 * Save local file header.
628 * \param f File seeked at correct position.
629 */
save_local(FILE * f)630 void zip_entry::save_local(FILE* f)
631 {
632 long offset = ftell(f);
633
634 if (offset<0)
635 throw error() << "Failed tell";
636
637 info.relative_offset_of_local_header = offset;
638
639 // write header
640 unsigned char buf[ZIP_LO_FIXED];
641 le_uint32_write(buf+ZIP_LO_local_file_header_signature, ZIP_L_signature);
642 le_uint8_write(buf+ZIP_LO_version_needed_to_extract, info.version_needed_to_extract);
643 le_uint8_write(buf+ZIP_LO_os_needed_to_extract, info.os_needed_to_extract);
644 // clear the "data descriptor" bit
645 le_uint16_write(buf+ZIP_LO_general_purpose_bit_flag, info.general_purpose_bit_flag & ~ZIP_GEN_FLAGS_DEFLATE_ZERO);
646 le_uint16_write(buf+ZIP_LO_compression_method, info.compression_method);
647 le_uint16_write(buf+ZIP_LO_last_mod_file_time, info.last_mod_file_time);
648 le_uint16_write(buf+ZIP_LO_last_mod_file_date, info.last_mod_file_date);
649 le_uint32_write(buf+ZIP_LO_crc32, info.crc32);
650 le_uint32_write(buf+ZIP_LO_compressed_size, info.compressed_size);
651 le_uint32_write(buf+ZIP_LO_uncompressed_size, info.uncompressed_size);
652 le_uint16_write(buf+ZIP_LO_filename_length, info.filename_length);
653 le_uint16_write(buf+ZIP_LO_extra_field_length, info.local_extra_field_length);
654
655 if (fwrite(buf, ZIP_LO_FIXED, 1, f) != 1) {
656 throw error() << "Failed write";
657 }
658
659 // write filename
660 if (fwrite(file_name, info.filename_length, 1, f) != 1) {
661 throw error() << "Failed write";
662 }
663
664 // write the extra field
665 if (info.local_extra_field_length && fwrite(local_extra_field, info.local_extra_field_length, 1, f) != 1) {
666 throw error() << "Failed write";
667 }
668
669 // write data, directories don't have data
670 if (info.compressed_size) {
671 assert(data);
672
673 if (fwrite(data, info.compressed_size, 1, f) != 1) {
674 throw error() << "Failed write";
675 }
676 }
677 }
678
679 /**
680 * Load cent dir.
681 * \param buf Fixed size cent dir.
682 * \param f File seeked after the fixed size cent dir.
683 */
load_cent(const unsigned char * buf,unsigned buf_size,unsigned & skip)684 void zip_entry::load_cent(const unsigned char* buf, unsigned buf_size, unsigned& skip)
685 {
686 const unsigned char* o_buf = buf;
687
688 check_cent(buf, buf_size);
689
690 // read header
691 info.version_made_by = le_uint8_read(buf+ZIP_CO_version_made_by);
692 info.host_os = le_uint8_read(buf+ZIP_CO_host_os);
693 info.version_needed_to_extract = le_uint8_read(buf+ZIP_CO_version_needed_to_extract);
694 info.os_needed_to_extract = le_uint8_read(buf+ZIP_CO_os_needed_to_extract);
695 info.general_purpose_bit_flag = le_uint16_read(buf+ZIP_CO_general_purpose_bit_flag);
696 info.compression_method = le_uint16_read(buf+ZIP_CO_compression_method);
697 info.last_mod_file_time = le_uint16_read(buf+ZIP_CO_last_mod_file_time);
698 info.last_mod_file_date = le_uint16_read(buf+ZIP_CO_last_mod_file_date);
699 info.crc32 = le_uint32_read(buf+ZIP_CO_crc32);
700 info.compressed_size = le_uint32_read(buf+ZIP_CO_compressed_size);
701 info.uncompressed_size = le_uint32_read(buf+ZIP_CO_uncompressed_size);
702 info.filename_length = le_uint16_read(buf+ZIP_CO_filename_length);
703 info.central_extra_field_length = le_uint16_read(buf+ZIP_CO_extra_field_length);
704 info.file_comment_length = le_uint16_read(buf+ZIP_CO_file_comment_length);
705 info.internal_file_attrib = le_uint16_read(buf+ZIP_CO_internal_file_attrib);
706 info.external_file_attrib = le_uint32_read(buf+ZIP_CO_external_file_attrib);
707 info.relative_offset_of_local_header = le_uint32_read(buf+ZIP_CO_relative_offset_of_local_header);
708 buf += ZIP_CO_FIXED;
709
710 if (buf_size < info.filename_length
711 || buf_size < info.central_extra_field_length
712 || buf_size < info.file_comment_length
713 || buf_size < ZIP_CO_FIXED + info.filename_length + info.central_extra_field_length + info.file_comment_length
714 ) {
715 throw error_invalid() << "Invalid central directory data";
716 }
717
718 // read filename
719 data_free(file_name);
720 file_name = data_alloc(info.filename_length);
721 memcpy(file_name, buf, info.filename_length);
722 buf += info.filename_length;
723
724 // read extra field
725 data_free(central_extra_field);
726 central_extra_field = data_dup(buf, info.central_extra_field_length);
727 buf += info.central_extra_field_length;
728
729 // read comment
730 data_free(file_comment);
731 file_comment = data_dup(buf, info.file_comment_length);
732 buf += info.file_comment_length;
733
734 skip = buf - o_buf;
735 }
736
737 /**
738 * Save cent dir.
739 * \param f File seeked at correct position.
740 */
save_cent(FILE * f)741 void zip_entry::save_cent(FILE* f)
742 {
743 unsigned char buf[ZIP_CO_FIXED];
744
745 le_uint32_write(buf+ZIP_CO_central_file_header_signature, ZIP_C_signature);
746 le_uint8_write(buf+ZIP_CO_version_made_by, info.version_made_by);
747 le_uint8_write(buf+ZIP_CO_host_os, info.host_os);
748 le_uint8_write(buf+ZIP_CO_version_needed_to_extract, info.version_needed_to_extract);
749 le_uint8_write(buf+ZIP_CO_os_needed_to_extract, info.os_needed_to_extract);
750 // clear the "data descriptor" bit
751 le_uint16_write(buf+ZIP_CO_general_purpose_bit_flag, info.general_purpose_bit_flag & ~ZIP_GEN_FLAGS_DEFLATE_ZERO);
752 le_uint16_write(buf+ZIP_CO_compression_method, info.compression_method);
753 le_uint16_write(buf+ZIP_CO_last_mod_file_time, info.last_mod_file_time);
754 le_uint16_write(buf+ZIP_CO_last_mod_file_date, info.last_mod_file_date);
755 le_uint32_write(buf+ZIP_CO_crc32, info.crc32);
756 le_uint32_write(buf+ZIP_CO_compressed_size, info.compressed_size);
757 le_uint32_write(buf+ZIP_CO_uncompressed_size, info.uncompressed_size);
758 le_uint16_write(buf+ZIP_CO_filename_length, info.filename_length);
759 le_uint16_write(buf+ZIP_CO_extra_field_length, info.central_extra_field_length);
760 le_uint16_write(buf+ZIP_CO_file_comment_length, info.file_comment_length);
761 le_uint16_write(buf+ZIP_CO_disk_number_start, ZIP_UNIQUE_DISK);
762 le_uint16_write(buf+ZIP_CO_internal_file_attrib, info.internal_file_attrib);
763 le_uint32_write(buf+ZIP_CO_external_file_attrib, info.external_file_attrib);
764 le_uint32_write(buf+ZIP_CO_relative_offset_of_local_header, info.relative_offset_of_local_header);
765
766 if (fwrite(buf, ZIP_CO_FIXED, 1, f) != 1) {
767 throw error() << "Failed write";
768 }
769
770 // write filename
771 if (fwrite(file_name, info.filename_length, 1, f) != 1) {
772 throw error() << "Failed write";
773 }
774
775 // write extra field
776 if (info.central_extra_field_length && fwrite(central_extra_field, info.central_extra_field_length, 1, f) != 1) {
777 throw error() << "Failed write";
778 }
779
780 // write comment
781 if (info.file_comment_length && fwrite(file_comment, info.file_comment_length, 1, f) != 1) {
782 throw error() << "Failed write";
783 }
784 }
785
zip(const std::string & Apath)786 zip::zip(const std::string& Apath) : path(Apath)
787 {
788 flag.open = false;
789 flag.read = false;
790 flag.modify = false;
791 zipfile_comment = 0;
792 }
793
zip(const zip & A)794 zip::zip(const zip& A) : map(A.map), path(A.path)
795 {
796 flag = A.flag;
797 info = A.info;
798 zipfile_comment = data_dup(A.zipfile_comment, A.info.zipfile_comment_length);
799 }
800
~zip()801 zip::~zip()
802 {
803 if (is_open())
804 close();
805 }
806
create()807 void zip::create()
808 {
809 assert(!flag.open);
810
811 info.offset_to_start_of_cent_dir = 0;
812 info.zipfile_comment_length = 0;
813 data_free(zipfile_comment);
814 zipfile_comment = 0;
815
816 flag.read = true;
817 flag.open = true;
818 flag.modify = false;
819 }
820
open()821 void zip::open()
822 {
823 assert(!flag.open);
824
825 struct stat s;
826 if (stat(path.c_str(), &s) != 0) {
827 if (errno != ENOENT)
828 throw error() << "Failed stat";
829
830 // create the file if it's missing
831 create();
832 return;
833 }
834
835 unsigned length = s.st_size;
836
837 // open file
838 FILE* f = fopen(path.c_str(), "rb");
839 if (!f)
840 throw error() << "Failed open for reading";
841
842 unsigned char* data = 0;
843 unsigned data_size = 0;
844
845 try {
846 if (!cent_read(f, length, data, data_size))
847 throw error_invalid() << "Failed read end of central directory";
848 } catch (...) {
849 fclose(f);
850 throw;
851 }
852
853 fclose(f);
854
855 // position in data
856 unsigned data_pos = 0;
857
858 try {
859 // central dir
860 while (le_uint32_read(data+data_pos) == ZIP_C_signature) {
861
862 iterator i = map.insert(map.end(), zip_entry(path));
863
864 unsigned skip = 0;
865 try {
866 i->load_cent(data + data_pos, data_size - data_pos, skip);
867 } catch (...) {
868 map.erase(i);
869 throw;
870 }
871
872 data_pos += skip;
873 }
874
875 // end of central dir
876 if (le_uint32_read(data+data_pos) != ZIP_E_signature)
877 throw error_invalid() << "Invalid end of central dir signature";
878
879 info.offset_to_start_of_cent_dir = le_uint32_read(data+data_pos+ZIP_EO_offset_to_start_of_cent_dir);
880 info.zipfile_comment_length = le_uint16_read(data+data_pos+ZIP_EO_zipfile_comment_length);
881 data_pos += ZIP_EO_FIXED;
882
883 if (info.offset_to_start_of_cent_dir != length - data_size)
884 throw error_invalid() << "Invalid end of central directory start address";
885
886 // comment
887 data_free(zipfile_comment);
888 zipfile_comment = data_dup(data+data_pos, info.zipfile_comment_length);
889 data_pos += info.zipfile_comment_length;
890
891 } catch (...) {
892 data_free(data);
893 throw;
894 }
895
896 // delete cent data
897 data_free(data);
898
899 if (pedantic) {
900 // don't accept garbage at the end of file
901 if (data_pos != data_size)
902 throw error_invalid() << data_size - data_pos << " unused bytes at the end of the central directory";
903 }
904
905 flag.open = true;
906 flag.read = false;
907 flag.modify = false;
908 }
909
910 /**
911 * Close a zip file.
912 */
close()913 void zip::close()
914 {
915 // deinizialize
916 flag.open = false;
917 flag.read = false;
918 flag.modify = false;
919 data_free(zipfile_comment);
920 zipfile_comment = 0;
921 path = "";
922 map.erase(map.begin(), map.end());
923 }
924
925 /**
926 * Discarge compressed data read.
927 * \note You cannot call unload() if zip is modified, you must call reopen().
928 */
unload()929 void zip::unload()
930 {
931 assert(flag.open && flag.read && !flag.modify);
932
933 for(iterator i=begin();i!=end();++i)
934 i->unload();
935
936 flag.read = false;
937 }
938
939 /**
940 * Reopen from zip on disk, lose any modify.
941 * \note Equivalent close and open.
942 */
reopen()943 void zip::reopen()
944 {
945 assert(flag.open);
946
947 close();
948 open();
949 }
950
951 /**
952 * Load a zip file.
953 */
load()954 void zip::load()
955 {
956 assert(flag.open && !flag.read);
957
958 flag.modify = false;
959
960 FILE* f = fopen(path.c_str(), "rb");
961 if (!f)
962 throw error() << "Failed open for reading";
963
964 try {
965 long offset = 0;
966 unsigned count = 0;
967
968 while (static_cast<unsigned long>(offset) < info.offset_to_start_of_cent_dir) {
969 unsigned char buf[ZIP_LO_FIXED];
970
971 // search the next item, assume entries in random order
972 iterator next;
973 bool next_set = false;
974 for(iterator i=begin();i!=end();++i) {
975 if (i->offset_get() >= static_cast<unsigned long>(offset)) {
976 if (!next_set || i->offset_get() < next->offset_get()) {
977 next_set = true;
978 next = i;
979 }
980 }
981 }
982
983 // if not found exit
984 if (!next_set) {
985 if (pedantic)
986 throw error_invalid() << info.offset_to_start_of_cent_dir - static_cast<unsigned long>(offset) << " unused bytes after the last local header at offset " << offset;
987 else
988 break;
989 }
990
991 // check for invalid start
992 if (next->offset_get() >= info.offset_to_start_of_cent_dir) {
993 throw error_invalid() << "Overflow in central directory";
994 }
995
996 // check for a data hole
997 if (next->offset_get() > static_cast<unsigned long>(offset)) {
998 if (pedantic)
999 throw error_invalid() << next->offset_get() - static_cast<unsigned long>(offset) << " unused bytes at offset " << offset;
1000 else {
1001 // set the correct position
1002 if (fseek(f, next->offset_get(), SEEK_SET) != 0)
1003 throw error() << "Failed fseek";
1004 offset = next->offset_get();
1005 }
1006 }
1007
1008 // search the next item, assume entries in random order
1009 iterator next_next;
1010 bool next_next_set = false;
1011 for(iterator i=begin();i!=end();++i) {
1012 if (i->offset_get() > static_cast<unsigned long>(offset)) {
1013 if (!next_next_set || i->offset_get() < next_next->offset_get()) {
1014 next_next_set = true;
1015 next_next = i;
1016 }
1017 }
1018 }
1019
1020 unsigned long end_offset;
1021 if (next_next_set)
1022 end_offset = next_next->offset_get();
1023 else
1024 end_offset = info.offset_to_start_of_cent_dir;
1025
1026 if (end_offset < next->offset_get() + ZIP_LO_FIXED) {
1027 throw error_invalid() << "Invalid local header size at offset " << offset;
1028 }
1029
1030 if (fread(buf, ZIP_LO_FIXED, 1, f) != 1)
1031 throw error() << "Failed read";
1032
1033 next->load_local(buf, f, end_offset - next->offset_get() - ZIP_LO_FIXED);
1034
1035 ++count;
1036
1037 offset = ftell(f);
1038 if (offset < 0)
1039 throw error() << "Failed tell";
1040 }
1041
1042 if (static_cast<unsigned long>(offset) != info.offset_to_start_of_cent_dir) {
1043 if (pedantic)
1044 throw error_invalid() << "Invalid central directory start";
1045 }
1046
1047 if (count != size())
1048 throw error_invalid() << "Invalid central directory, expected " << size() << " local headers, got " << count;
1049
1050 } catch (...) {
1051 fclose(f);
1052 throw;
1053 }
1054
1055 fclose(f);
1056
1057 flag.read = true;
1058 }
1059
size_not_zero() const1060 unsigned zip::size_not_zero() const
1061 {
1062 unsigned count = 0;
1063
1064 // check if it contains at least one file with size > 0 (directories are excluded)
1065 for(const_iterator i=begin();i!=end();++i) {
1066 if (i->uncompressed_size_get() > 0) {
1067 ++count;
1068 }
1069 }
1070
1071 return count;
1072 }
1073
1074 /**
1075 * Save a zip file.
1076 */
save()1077 void zip::save()
1078 {
1079 assert(flag.open && flag.read);
1080
1081 flag.modify = false;
1082
1083 if (!empty()) {
1084 // prevent external signal
1085 sig_auto_lock sal;
1086
1087 // temp name of the saved file
1088 string save_path = file_temp(path);
1089
1090 FILE* f = fopen(save_path.c_str(), "wb");
1091 if (!f)
1092 throw error() << "Failed open for writing of " << save_path;
1093
1094 try {
1095 // write local header
1096 for(iterator i=begin();i!=end();++i)
1097 i->save_local(f);
1098
1099 long cent_offset = ftell(f);
1100 if (cent_offset<0)
1101 throw error() << "Failed tell";
1102
1103 // new cent start
1104 info.offset_to_start_of_cent_dir = cent_offset;
1105
1106 // write cent dir
1107 for(iterator i=begin();i!=end();++i)
1108 i->save_cent(f);
1109
1110 long end_cent_offset = ftell(f);
1111 if (end_cent_offset<0)
1112 throw error() << "Failed tell";
1113
1114 // write end of cent dir
1115 unsigned char buf[ZIP_EO_FIXED];
1116 le_uint32_write(buf+ZIP_EO_end_of_central_dir_signature, ZIP_E_signature);
1117 le_uint16_write(buf+ZIP_EO_number_of_this_disk, ZIP_UNIQUE_DISK);
1118 le_uint16_write(buf+ZIP_EO_number_of_disk_start_cent_dir, ZIP_UNIQUE_DISK);
1119 le_uint16_write(buf+ZIP_EO_total_entries_cent_dir_this_disk, size());
1120 le_uint16_write(buf+ZIP_EO_total_entries_cent_dir, size());
1121 le_uint32_write(buf+ZIP_EO_size_of_cent_dir, end_cent_offset - cent_offset);
1122 le_uint32_write(buf+ZIP_EO_offset_to_start_of_cent_dir, cent_offset);
1123 le_uint16_write(buf+ZIP_EO_zipfile_comment_length, info.zipfile_comment_length);
1124
1125 if (fwrite(buf, ZIP_EO_FIXED, 1, f) != 1)
1126 throw error() << "Failed write";
1127
1128 // write comment
1129 if (info.zipfile_comment_length && fwrite(zipfile_comment, info.zipfile_comment_length, 1, f) != 1)
1130 throw error() << "Failed write";
1131
1132 } catch (...) {
1133 fclose(f);
1134 remove(save_path.c_str());
1135 throw;
1136 }
1137
1138 fclose(f);
1139
1140 // delete the file if exists
1141 if (access(path.c_str(), F_OK) == 0) {
1142 if (remove(path.c_str()) != 0) {
1143 remove(save_path.c_str());
1144 throw error() << "Failed delete of " << path;
1145 }
1146 }
1147
1148 // rename the new version with the correct name
1149 if (::rename(save_path.c_str(), path.c_str()) != 0) {
1150 throw error() << "Failed rename of " << save_path << " to " << path;
1151 }
1152
1153 } else {
1154 // reset the cent start
1155 info.offset_to_start_of_cent_dir = 0;
1156
1157 // delete the file if exists
1158 if (access(path.c_str(), F_OK) == 0) {
1159 if (remove(path.c_str()) != 0)
1160 throw error() << "Failed delete of " << path;
1161 }
1162 }
1163 }
1164
1165 /**
1166 * Remove a compressed file.
1167 */
erase(iterator i)1168 void zip::erase(iterator i)
1169 {
1170 assert(flag.read);
1171
1172 flag.modify = true;
1173
1174 map.erase(i);
1175 }
1176
1177 /**
1178 * Rename a compressed file.
1179 * \note No filename overwrite check.
1180 */
rename(iterator i,const string & Aname)1181 void zip::rename(iterator i, const string& Aname)
1182 {
1183 assert(flag.read);
1184
1185 flag.modify = true;
1186
1187 i->name_set(Aname);
1188 }
1189
1190 /**
1191 * Insert a zip entry.
1192 */
insert(const zip_entry & A,const string & Aname)1193 zip::iterator zip::insert(const zip_entry& A, const string& Aname)
1194 {
1195 iterator i;
1196
1197 assert(flag.read);
1198
1199 unsigned char* data = data_alloc(A.compressed_size_get());
1200 assert(data);
1201
1202 try {
1203 A.compressed_read(data);
1204
1205 i = map.insert(map.end(), zip_entry(path));
1206
1207 try {
1208 i->set(A.method_get(), Aname, data, A.compressed_size_get(), A.uncompressed_size_get(), A.crc_get(), A.zipdate_get(), A.ziptime_get(), A.is_text());
1209 } catch (...) {
1210 map.erase(i);
1211 throw;
1212 }
1213
1214 flag.modify = true;
1215
1216 } catch (...) {
1217 data_free(data);
1218 throw;
1219 }
1220
1221 data_free(data);
1222
1223 return i;
1224 }
1225
1226 /**
1227 * Add data to a zip file.
1228 * The data is deflated or stored. No filename overwrite check.
1229 */
insert_uncompressed(const string & Aname,const unsigned char * data,unsigned size,unsigned crc,time_t tod,bool is_text)1230 zip::iterator zip::insert_uncompressed(const string& Aname, const unsigned char* data, unsigned size, unsigned crc, time_t tod, bool is_text)
1231 {
1232 iterator i;
1233
1234 assert(flag.read);
1235 assert(crc == crc32(0, (const unsigned char*)data, size));
1236
1237 unsigned date = 0;
1238 unsigned time = 0;
1239 time2zip(tod, date, time);
1240
1241 i = map.insert(map.end(), zip_entry(path));
1242
1243 try {
1244 i->set(zip_entry::store, Aname, data, size, size, crc, date, time, is_text);
1245 } catch (...) {
1246 map.erase(i);
1247 throw;
1248 }
1249
1250 flag.modify = true;
1251
1252 return i;
1253 }
1254
1255