1 /* zip.c -- Zip manipulation
2 Version 2.8.1, December 1, 2018
3 part of the MiniZip project
4
5 Copyright (C) 2010-2018 Nathan Moinvaziri
6 https://github.com/nmoinvaz/minizip
7 Copyright (C) 2009-2010 Mathias Svensson
8 Modifications for Zip64 support
9 http://result42.com
10 Copyright (C) 2007-2008 Even Rouault
11 Modifications of Unzip for Zip64
12 Copyright (C) 1998-2010 Gilles Vollant
13 https://www.winimage.com/zLibDll/minizip.html
14
15 This program is distributed under the terms of the same license as zlib.
16 See the accompanying LICENSE file for the full text of the license.
17 */
18
19
20 #include "mz.h"
21 #include "mz_crypt.h"
22 #include "mz_strm.h"
23 #ifdef HAVE_BZIP2
24 # include "mz_strm_bzip.h"
25 #endif
26 #ifdef HAVE_LIBCOMP
27 # include "mz_strm_libcomp.h"
28 #endif
29 #ifdef HAVE_LZMA
30 # include "mz_strm_lzma.h"
31 #endif
32 #include "mz_strm_mem.h"
33 #ifdef HAVE_PKCRYPT
34 # include "mz_strm_pkcrypt.h"
35 #endif
36 #ifdef HAVE_WZAES
37 # include "mz_strm_wzaes.h"
38 #endif
39 #ifdef HAVE_ZLIB
40 # include "mz_strm_zlib.h"
41 #endif
42
43 #include "mz_zip.h"
44
45 #include <ctype.h> /* tolower */
46 #include <stdio.h> /* snprintf */
47
48 #if defined(_MSC_VER) && (_MSC_VER < 1900)
49 # define snprintf _snprintf
50 #endif
51
52 /***************************************************************************/
53
54 #define MZ_ZIP_MAGIC_LOCALHEADER (0x04034b50)
55 #define MZ_ZIP_MAGIC_CENTRALHEADER (0x02014b50)
56 #define MZ_ZIP_MAGIC_ENDHEADER (0x06054b50)
57 #define MZ_ZIP_MAGIC_ENDHEADERU8 { 0x50, 0x4b, 0x05, 0x06 }
58 #define MZ_ZIP_MAGIC_ENDHEADER64 (0x06064b50)
59 #define MZ_ZIP_MAGIC_ENDLOCHEADER64 (0x07064b50)
60 #define MZ_ZIP_MAGIC_DATADESCRIPTOR (0x08074b50)
61 #define MZ_ZIP_MAGIC_DATADESCRIPTORU8 { 0x50, 0x4b, 0x07, 0x08 }
62
63 #define MZ_ZIP_SIZE_LD_ITEM (30)
64 #define MZ_ZIP_SIZE_CD_ITEM (46)
65 #define MZ_ZIP_SIZE_CD_LOCATOR64 (20)
66
67 /***************************************************************************/
68
69 typedef struct mz_zip_s
70 {
71 mz_zip_file file_info;
72 mz_zip_file local_file_info;
73
74 void *stream; /* main stream */
75 void *cd_stream; /* pointer to the stream with the cd */
76 void *cd_mem_stream; /* memory stream for central directory */
77 void *compress_stream; /* compression stream */
78 void *crypt_stream; /* encryption stream */
79 void *file_info_stream; /* memory stream for storing file info */
80 void *local_file_info_stream; /* memory stream for storing local file info */
81
82 int32_t open_mode;
83 uint8_t recover;
84
85 uint32_t disk_number_with_cd; /* number of the disk with the central dir */
86 int64_t disk_offset_shift; /* correction for zips that have wrong offset start of cd */
87
88 int64_t cd_start_pos; /* pos of the first file in the central dir stream */
89 int64_t cd_current_pos; /* pos of the current file in the central dir */
90 int64_t cd_offset; /* offset of start of central directory */
91 int64_t cd_size; /* size of the central directory */
92
93 uint8_t entry_scanned; /* entry header information read ok */
94 uint8_t entry_opened; /* entry is open for read/write */
95 uint8_t entry_raw; /* entry opened with raw mode */
96 uint32_t entry_crc32; /* entry crc32 */
97
98 uint64_t number_entry;
99
100 uint16_t version_madeby;
101 char *comment;
102 } mz_zip;
103
104 /***************************************************************************/
105
106 #if 0
107 # define mz_zip_print printf
108 #else
109 # define mz_zip_print(fmt,...)
110 #endif
111
112 /***************************************************************************/
113
114 /* Locate the end of central directory */
mz_zip_search_eocd(void * stream,int64_t * central_pos)115 static int32_t mz_zip_search_eocd(void *stream, int64_t *central_pos)
116 {
117 int64_t file_size = 0;
118 int64_t max_back = 0;
119 uint8_t find[4] = MZ_ZIP_MAGIC_ENDHEADERU8;
120 int32_t err = MZ_OK;
121
122 err = mz_stream_seek(stream, 0, MZ_SEEK_END);
123 if (err != MZ_OK)
124 return err;
125
126 file_size = mz_stream_tell(stream);
127
128 /* Maximum seek is size of global comment + extra */
129 max_back = UINT16_MAX + 128;
130 if (max_back > file_size)
131 max_back = file_size;
132
133 return mz_stream_find_reverse(stream, (const void *)find, sizeof(find), max_back, central_pos);
134 }
135
136 /* Locate the end of central directory 64 of a zip file */
mz_zip_search_zip64_eocd(void * stream,const int64_t end_central_offset,int64_t * central_pos)137 static int32_t mz_zip_search_zip64_eocd(void *stream, const int64_t end_central_offset, int64_t *central_pos)
138 {
139 int64_t offset = 0;
140 uint32_t value32 = 0;
141 int32_t err = MZ_OK;
142
143
144 *central_pos = 0;
145
146 /* Zip64 end of central directory locator */
147 err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_SEEK_SET);
148 /* Read locator signature */
149 if (err == MZ_OK)
150 {
151 err = mz_stream_read_uint32(stream, &value32);
152 if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
153 err = MZ_FORMAT_ERROR;
154 }
155 /* Number of the disk with the start of the zip64 end of central directory */
156 if (err == MZ_OK)
157 err = mz_stream_read_uint32(stream, &value32);
158 /* Relative offset of the zip64 end of central directory record8 */
159 if (err == MZ_OK)
160 err = mz_stream_read_uint64(stream, (uint64_t *)&offset);
161 /* Total number of disks */
162 if (err == MZ_OK)
163 err = mz_stream_read_uint32(stream, &value32);
164 /* Goto end of central directory record */
165 if (err == MZ_OK)
166 err = mz_stream_seek(stream, (int64_t)offset, MZ_SEEK_SET);
167 /* The signature */
168 if (err == MZ_OK)
169 {
170 err = mz_stream_read_uint32(stream, &value32);
171 if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
172 err = MZ_FORMAT_ERROR;
173 }
174
175 if (err == MZ_OK)
176 *central_pos = offset;
177
178 return err;
179 }
180
181 /* Get info about the current file in the zip file */
mz_zip_entry_read_header(void * stream,uint8_t local,mz_zip_file * file_info,void * file_extra_stream)182 static int32_t mz_zip_entry_read_header(void *stream, uint8_t local, mz_zip_file *file_info, void *file_extra_stream)
183 {
184 uint64_t ntfs_time = 0;
185 uint32_t reserved = 0;
186 uint32_t magic = 0;
187 uint32_t dos_date = 0;
188 uint32_t field_pos = 0;
189 uint16_t field_type = 0;
190 uint16_t field_length = 0;
191 uint32_t field_length_read = 0;
192 uint16_t ntfs_attrib_id = 0;
193 uint16_t ntfs_attrib_size = 0;
194 uint16_t value16 = 0;
195 uint32_t value32 = 0;
196 int64_t extrafield_pos = 0;
197 int64_t comment_pos = 0;
198 int32_t err = MZ_OK;
199
200
201 memset(file_info, 0, sizeof(mz_zip_file));
202
203 /* Check the magic */
204 err = mz_stream_read_uint32(stream, &magic);
205 if (err == MZ_END_OF_STREAM)
206 err = MZ_END_OF_LIST;
207 else if (magic == MZ_ZIP_MAGIC_ENDHEADER || magic == MZ_ZIP_MAGIC_ENDHEADER64)
208 err = MZ_END_OF_LIST;
209 else if ((local) && (magic != MZ_ZIP_MAGIC_LOCALHEADER))
210 err = MZ_FORMAT_ERROR;
211 else if ((!local) && (magic != MZ_ZIP_MAGIC_CENTRALHEADER))
212 err = MZ_FORMAT_ERROR;
213
214 /* Read header fields */
215 if (err == MZ_OK)
216 {
217 if (!local)
218 err = mz_stream_read_uint16(stream, &file_info->version_madeby);
219 if (err == MZ_OK)
220 err = mz_stream_read_uint16(stream, &file_info->version_needed);
221 if (err == MZ_OK)
222 err = mz_stream_read_uint16(stream, &file_info->flag);
223 if (err == MZ_OK)
224 err = mz_stream_read_uint16(stream, &file_info->compression_method);
225 if (err == MZ_OK)
226 {
227 err = mz_stream_read_uint32(stream, &dos_date);
228 file_info->modified_date = mz_zip_dosdate_to_time_t(dos_date);
229 }
230 if (err == MZ_OK)
231 err = mz_stream_read_uint32(stream, &file_info->crc);
232 if (err == MZ_OK)
233 {
234 err = mz_stream_read_uint32(stream, &value32);
235 file_info->compressed_size = value32;
236 }
237 if (err == MZ_OK)
238 {
239 err = mz_stream_read_uint32(stream, &value32);
240 file_info->uncompressed_size = value32;
241 }
242 if (err == MZ_OK)
243 err = mz_stream_read_uint16(stream, &file_info->filename_size);
244 if (err == MZ_OK)
245 err = mz_stream_read_uint16(stream, &file_info->extrafield_size);
246 if (!local)
247 {
248 if (err == MZ_OK)
249 err = mz_stream_read_uint16(stream, &file_info->comment_size);
250 if (err == MZ_OK)
251 {
252 err = mz_stream_read_uint16(stream, &value16);
253 file_info->disk_number = value16;
254 }
255 if (err == MZ_OK)
256 err = mz_stream_read_uint16(stream, &file_info->internal_fa);
257 if (err == MZ_OK)
258 err = mz_stream_read_uint32(stream, &file_info->external_fa);
259 if (err == MZ_OK)
260 {
261 err = mz_stream_read_uint32(stream, &value32);
262 file_info->disk_offset = value32;
263 }
264 }
265 }
266
267 if (err == MZ_OK)
268 err = mz_stream_seek(file_extra_stream, 0, MZ_SEEK_SET);
269
270 /* Copy variable length data to memory stream for later retrieval */
271 if ((err == MZ_OK) && (file_info->filename_size > 0))
272 err = mz_stream_copy(file_extra_stream, stream, file_info->filename_size);
273 mz_stream_write_uint8(file_extra_stream, 0);
274 extrafield_pos = mz_stream_tell(file_extra_stream);
275
276 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
277 err = mz_stream_copy(file_extra_stream, stream, file_info->extrafield_size);
278 mz_stream_write_uint8(file_extra_stream, 0);
279
280 comment_pos = mz_stream_tell(file_extra_stream);
281 if ((err == MZ_OK) && (file_info->comment_size > 0))
282 err = mz_stream_copy(file_extra_stream, stream, file_info->comment_size);
283 mz_stream_write_uint8(file_extra_stream, 0);
284
285 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
286 {
287 /* Seek to and parse the extra field */
288 err = mz_stream_seek(file_extra_stream, extrafield_pos, MZ_SEEK_SET);
289
290 while ((err == MZ_OK) && (field_pos + 4 <= file_info->extrafield_size))
291 {
292 err = mz_zip_extrafield_read(file_extra_stream, &field_type, &field_length);
293 if (err != MZ_OK)
294 break;
295 field_pos += 4;
296
297 /* Don't allow field length to exceed size of remaining extrafield */
298 if (field_length > (file_info->extrafield_size - field_pos))
299 field_length = (uint16_t)(file_info->extrafield_size - field_pos);
300
301 /* Read ZIP64 extra field */
302 if ((field_type == MZ_ZIP_EXTENSION_ZIP64) && (field_length >= 8))
303 {
304 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
305 {
306 err = mz_stream_read_int64(file_extra_stream, &file_info->uncompressed_size);
307 if (file_info->uncompressed_size < 0)
308 err = MZ_FORMAT_ERROR;
309 }
310 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
311 {
312 err = mz_stream_read_int64(file_extra_stream, &file_info->compressed_size);
313 if (file_info->compressed_size < 0)
314 err = MZ_FORMAT_ERROR;
315 }
316 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
317 {
318 err = mz_stream_read_int64(file_extra_stream, &file_info->disk_offset);
319 if (file_info->disk_offset < 0)
320 err = MZ_FORMAT_ERROR;
321 }
322 if ((err == MZ_OK) && (file_info->disk_number == UINT16_MAX))
323 err = mz_stream_read_uint32(file_extra_stream, &file_info->disk_number);
324 }
325 /* Read NTFS extra field */
326 else if ((field_type == MZ_ZIP_EXTENSION_NTFS) && (field_length > 4))
327 {
328 if (err == MZ_OK)
329 err = mz_stream_read_uint32(file_extra_stream, &reserved);
330 field_length_read = 4;
331
332 while ((err == MZ_OK) && (field_length_read + 4 <= field_length))
333 {
334 err = mz_stream_read_uint16(file_extra_stream, &ntfs_attrib_id);
335 if (err == MZ_OK)
336 err = mz_stream_read_uint16(file_extra_stream, &ntfs_attrib_size);
337 field_length_read += 4;
338
339 if ((err == MZ_OK) && (ntfs_attrib_id == 0x01) && (ntfs_attrib_size == 24))
340 {
341 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
342 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->modified_date);
343
344 if (err == MZ_OK)
345 {
346 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
347 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->accessed_date);
348 }
349 if (err == MZ_OK)
350 {
351 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
352 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->creation_date);
353 }
354 }
355 else if ((err == MZ_OK) && (field_length_read + ntfs_attrib_size <= field_length))
356 {
357 err = mz_stream_seek(file_extra_stream, ntfs_attrib_size, MZ_SEEK_CUR);
358 }
359
360 field_length_read += ntfs_attrib_size;
361 }
362 }
363 /* Read UNIX1 extra field */
364 else if ((field_type == MZ_ZIP_EXTENSION_UNIX1) && (field_length >= 12))
365 {
366 if (err == MZ_OK && file_info->accessed_date == 0)
367 {
368 err = mz_stream_read_uint32(file_extra_stream, &value32);
369 if (err == MZ_OK)
370 file_info->accessed_date = value32;
371 }
372 if (err == MZ_OK && file_info->modified_date == 0)
373 {
374 err = mz_stream_read_uint32(file_extra_stream, &value32);
375 if (err == MZ_OK)
376 file_info->modified_date = value32;
377 }
378 if (err == MZ_OK)
379 err = mz_stream_read_uint16(file_extra_stream, &value16); /* User id */
380 if (err == MZ_OK)
381 err = mz_stream_read_uint16(file_extra_stream, &value16); /* Group id */
382
383 /* Skip variable data */
384 mz_stream_seek(file_extra_stream, field_length - 12, MZ_SEEK_CUR);
385 }
386 #ifdef HAVE_WZAES
387 /* Read AES extra field */
388 else if ((field_type == MZ_ZIP_EXTENSION_AES) && (field_length == 7))
389 {
390 uint8_t value8 = 0;
391 /* Verify version info */
392 err = mz_stream_read_uint16(file_extra_stream, &value16);
393 /* Support AE-1 and AE-2 */
394 if (value16 != 1 && value16 != 2)
395 err = MZ_FORMAT_ERROR;
396 file_info->aes_version = value16;
397 if (err == MZ_OK)
398 err = mz_stream_read_uint8(file_extra_stream, &value8);
399 if ((char)value8 != 'A')
400 err = MZ_FORMAT_ERROR;
401 if (err == MZ_OK)
402 err = mz_stream_read_uint8(file_extra_stream, &value8);
403 if ((char)value8 != 'E')
404 err = MZ_FORMAT_ERROR;
405 /* Get AES encryption strength and actual compression method */
406 if (err == MZ_OK)
407 {
408 err = mz_stream_read_uint8(file_extra_stream, &value8);
409 file_info->aes_encryption_mode = value8;
410 }
411 if (err == MZ_OK)
412 {
413 err = mz_stream_read_uint16(file_extra_stream, &value16);
414 file_info->compression_method = value16;
415 }
416 }
417 #endif
418 else if (field_length > 0)
419 {
420 err = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
421 }
422
423 field_pos += field_length;
424 }
425 }
426
427 /* Get pointers to variable length data */
428 mz_stream_mem_get_buffer(file_extra_stream, (const void **)&file_info->filename);
429 mz_stream_mem_get_buffer_at(file_extra_stream, extrafield_pos, (const void **)&file_info->extrafield);
430 mz_stream_mem_get_buffer_at(file_extra_stream, comment_pos, (const void **)&file_info->comment);
431
432 /* Set to empty string just in-case */
433 if (file_info->filename == NULL)
434 file_info->filename = "";
435 if (file_info->extrafield == NULL)
436 file_info->extrafield_size = 0;
437 if (file_info->comment == NULL)
438 file_info->comment = "";
439
440 if (err == MZ_OK)
441 {
442 mz_zip_print("Zip - Entry - Read header - %s (local %"PRId8")\n",
443 file_info->filename, local);
444 mz_zip_print("Zip - Entry - Read header compress (ucs %"PRId64" cs %"PRId64" crc 0x%08"PRIx32")\n",
445 file_info->uncompressed_size, file_info->compressed_size, file_info->crc);
446 if (!local)
447 {
448 mz_zip_print("Zip - Entry - Read header disk (disk %"PRIu32" offset %"PRId64")\n",
449 file_info->disk_number, file_info->disk_offset);
450 }
451 mz_zip_print("Zip - Entry - Read header variable (fnl %"PRId32" efs %"PRId32" cms %"PRId32")\n",
452 file_info->filename_size, file_info->extrafield_size, file_info->comment_size);
453 }
454
455 return err;
456 }
457
mz_zip_entry_read_descriptor(void * stream,uint8_t zip64,uint32_t * crc32,int64_t * compressed_size,int64_t * uncompressed_size)458 static int32_t mz_zip_entry_read_descriptor(void *stream, uint8_t zip64, uint32_t *crc32, int64_t *compressed_size, int64_t *uncompressed_size)
459 {
460 uint32_t value32 = 0;
461 int64_t value64 = 0;
462 int32_t err = MZ_OK;
463
464
465 err = mz_stream_read_uint32(stream, &value32);
466 if (value32 != MZ_ZIP_MAGIC_DATADESCRIPTOR)
467 err = MZ_FORMAT_ERROR;
468 if (err == MZ_OK)
469 err = mz_stream_read_uint32(stream, &value32);
470 if ((err == MZ_OK) && (crc32 != NULL))
471 *crc32 = value32;
472 if (err == MZ_OK)
473 {
474 /* If zip 64 extension is enabled then read as 8 byte */
475 if (!zip64)
476 {
477 err = mz_stream_read_uint32(stream, &value32);
478 value64 = value32;
479 }
480 else
481 {
482 err = mz_stream_read_int64(stream, &value64);
483 if (value64 < 0)
484 err = MZ_FORMAT_ERROR;
485 }
486 if ((err == MZ_OK) && (compressed_size != NULL))
487 *compressed_size = value64;
488 }
489 if (err == MZ_OK)
490 {
491 if (!zip64)
492 {
493 err = mz_stream_read_uint32(stream, &value32);
494 value64 = value32;
495 }
496 else
497 {
498 err = mz_stream_read_int64(stream, &value64);
499 if (value64 < 0)
500 err = MZ_FORMAT_ERROR;
501 }
502 if ((err == MZ_OK) && (uncompressed_size != NULL))
503 *uncompressed_size = value64;
504 }
505
506 return err;
507 }
508
mz_zip_entry_write_header(void * stream,uint8_t local,mz_zip_file * file_info)509 static int32_t mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
510 {
511 uint64_t ntfs_time = 0;
512 uint32_t reserved = 0;
513 uint32_t dos_date = 0;
514 uint16_t extrafield_size = 0;
515 uint16_t field_type = 0;
516 uint16_t field_length = 0;
517 uint16_t field_length_zip64 = 0;
518 uint16_t field_length_ntfs = 0;
519 uint16_t field_length_aes = 0;
520 uint16_t filename_size = 0;
521 uint16_t filename_length = 0;
522 uint16_t version_needed = 0;
523 int32_t comment_size = 0;
524 int32_t err = MZ_OK;
525 int32_t err_mem = MZ_OK;
526 uint8_t zip64 = 0;
527 uint8_t skip_aes = 0;
528 uint8_t mask = 0;
529 uint8_t write_end_slash = 0;
530 const char *filename = NULL;
531 char masked_name[64];
532 void *file_extra_stream = NULL;
533
534 if (file_info == NULL)
535 return MZ_PARAM_ERROR;
536
537 if ((local) && (file_info->flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO))
538 mask = 1;
539
540 /* Calculate extra field sizes */
541 if (file_info->uncompressed_size >= UINT32_MAX)
542 field_length_zip64 += 8;
543 if (file_info->compressed_size >= UINT32_MAX)
544 field_length_zip64 += 8;
545 if (file_info->disk_offset >= UINT32_MAX)
546 field_length_zip64 += 8;
547
548 if (file_info->zip64 == MZ_ZIP64_AUTO)
549 {
550 /* If uncompressed size is unknown, assume zip64 for 64-bit data descriptors */
551 zip64 = (local && file_info->uncompressed_size == 0) || (field_length_zip64 > 0);
552 }
553 else if (file_info->zip64 == MZ_ZIP64_FORCE)
554 {
555 zip64 = 1;
556 }
557 else if (file_info->zip64 == MZ_ZIP64_DISABLE)
558 {
559 /* Zip64 extension is required to zip file */
560 if (field_length_zip64 > 0)
561 return MZ_PARAM_ERROR;
562 }
563
564 if (zip64)
565 {
566 extrafield_size += 4;
567 extrafield_size += field_length_zip64;
568 }
569
570 /* Calculate extra field size and check for duplicates */
571 if (file_info->extrafield_size > 0)
572 {
573 mz_stream_mem_create(&file_extra_stream);
574 mz_stream_mem_set_buffer(file_extra_stream, (void *)file_info->extrafield,
575 file_info->extrafield_size);
576
577 do
578 {
579 err_mem = mz_stream_read_uint16(file_extra_stream, &field_type);
580 if (err_mem == MZ_OK)
581 err_mem = mz_stream_read_uint16(file_extra_stream, &field_length);
582 if (err_mem != MZ_OK)
583 break;
584
585 /* Prefer incoming aes extensions over ours */
586 if (field_type == MZ_ZIP_EXTENSION_AES)
587 skip_aes = 1;
588
589 /* Prefer our zip64, ntfs extension over incoming */
590 if (field_type != MZ_ZIP_EXTENSION_ZIP64 && field_type != MZ_ZIP_EXTENSION_NTFS)
591 extrafield_size += 4 + field_length;
592
593 if (err_mem == MZ_OK)
594 err_mem = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
595 }
596 while (err_mem == MZ_OK);
597 }
598
599 #ifdef HAVE_WZAES
600 if (!skip_aes)
601 {
602 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
603 {
604 field_length_aes = 1 + 1 + 1 + 2 + 2;
605 extrafield_size += 4 + field_length_aes;
606 }
607 }
608 #else
609 MZ_UNUSED(field_length_aes);
610 MZ_UNUSED(skip_aes);
611 #endif
612 /* NTFS timestamps */
613 if ((file_info->modified_date != 0) &&
614 (file_info->accessed_date != 0) &&
615 (file_info->creation_date != 0) && (!mask))
616 {
617 field_length_ntfs = 8 + 8 + 8 + 4 + 2 + 2;
618 extrafield_size += 4 + field_length_ntfs;
619 }
620
621 if (local)
622 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
623 else
624 {
625 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
626 if (err == MZ_OK)
627 err = mz_stream_write_uint16(stream, file_info->version_madeby);
628 }
629
630 /* Calculate version needed to extract */
631 if (err == MZ_OK)
632 {
633 version_needed = file_info->version_needed;
634 if (version_needed == 0)
635 {
636 version_needed = 20;
637 if (zip64)
638 version_needed = 45;
639 #ifdef HAVE_WZAES
640 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
641 version_needed = 51;
642 #endif
643 #ifdef HAVE_LZMA
644 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
645 version_needed = 63;
646 #endif
647 }
648 err = mz_stream_write_uint16(stream, version_needed);
649 }
650 if (err == MZ_OK)
651 err = mz_stream_write_uint16(stream, file_info->flag);
652 if (err == MZ_OK)
653 {
654 #ifdef HAVE_WZAES
655 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
656 err = mz_stream_write_uint16(stream, MZ_COMPRESS_METHOD_AES);
657 else
658 #endif
659 err = mz_stream_write_uint16(stream, file_info->compression_method);
660 }
661 if (err == MZ_OK)
662 {
663 if (file_info->modified_date != 0 && !mask)
664 dos_date = mz_zip_time_t_to_dos_date(file_info->modified_date);
665 err = mz_stream_write_uint32(stream, dos_date);
666 }
667
668 if (err == MZ_OK)
669 {
670 if (mask)
671 err = mz_stream_write_uint32(stream, 0);
672 else
673 err = mz_stream_write_uint32(stream, file_info->crc); /* crc */
674 }
675 if (err == MZ_OK)
676 {
677 if (file_info->compressed_size >= UINT32_MAX) /* compr size */
678 err = mz_stream_write_uint32(stream, UINT32_MAX);
679 else
680 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
681 }
682 if (err == MZ_OK)
683 {
684 if (file_info->uncompressed_size >= UINT32_MAX) /* uncompr size */
685 err = mz_stream_write_uint32(stream, UINT32_MAX);
686 else if (mask)
687 err = mz_stream_write_uint32(stream, 0);
688 else
689 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
690 }
691
692 if (mask)
693 {
694 snprintf(masked_name, sizeof(masked_name), "%"PRIx32"_%"PRIx64,
695 file_info->disk_number, file_info->disk_offset);
696 filename = masked_name;
697 }
698 else
699 {
700 filename = file_info->filename;
701 }
702
703 filename_length = (uint16_t)strlen(filename);
704 filename_size += filename_length;
705
706 if ((mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK) &&
707 ((filename[filename_length - 1] != '/') && (filename[filename_length - 1] != '\\')))
708 {
709 filename_size += 1;
710 write_end_slash = 1;
711 }
712
713 if (err == MZ_OK)
714 err = mz_stream_write_uint16(stream, filename_size);
715 if (err == MZ_OK)
716 err = mz_stream_write_uint16(stream, extrafield_size);
717
718 if (!local)
719 {
720 if (file_info->comment != NULL)
721 {
722 comment_size = (int32_t)strlen(file_info->comment);
723 if (comment_size > UINT16_MAX)
724 comment_size = UINT16_MAX;
725 }
726 if (err == MZ_OK)
727 err = mz_stream_write_uint16(stream, (uint16_t)comment_size);
728 if (err == MZ_OK)
729 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_number);
730 if (err == MZ_OK)
731 err = mz_stream_write_uint16(stream, file_info->internal_fa);
732 if (err == MZ_OK)
733 err = mz_stream_write_uint32(stream, file_info->external_fa);
734 if (err == MZ_OK)
735 {
736 if (file_info->disk_offset >= UINT32_MAX)
737 err = mz_stream_write_uint32(stream, UINT32_MAX);
738 else
739 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
740 }
741 }
742
743 if (err == MZ_OK)
744 {
745 if (mz_stream_write(stream, filename, filename_length) != filename_length)
746 err = MZ_WRITE_ERROR;
747
748 /* Ensure that directories have a slash appended to them for compatibility */
749 if (err == MZ_OK && write_end_slash)
750 err = mz_stream_write_uint8(stream, '/');
751 }
752
753 if (file_info->extrafield_size > 0)
754 {
755 err_mem = mz_stream_mem_seek(file_extra_stream, 0, MZ_SEEK_SET);
756 while (err == MZ_OK && err_mem == MZ_OK)
757 {
758 err_mem = mz_stream_read_uint16(file_extra_stream, &field_type);
759 if (err_mem == MZ_OK)
760 err_mem = mz_stream_read_uint16(file_extra_stream, &field_length);
761 if (err_mem != MZ_OK)
762 break;
763
764 /* Prefer our zip 64, ntfs extensions over incoming */
765 if (field_type == MZ_ZIP_EXTENSION_ZIP64 || field_type == MZ_ZIP_EXTENSION_NTFS)
766 {
767 err_mem = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
768 continue;
769 }
770
771 err = mz_stream_write_uint16(stream, field_type);
772 if (err == MZ_OK)
773 err = mz_stream_write_uint16(stream, field_length);
774 if (err == MZ_OK)
775 err = mz_stream_copy(stream, file_extra_stream, field_length);
776 }
777
778 mz_stream_mem_delete(&file_extra_stream);
779 }
780
781 /* Write ZIP64 extra field */
782 if ((err == MZ_OK) && (zip64))
783 {
784 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_ZIP64, field_length_zip64);
785 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
786 {
787 if (mask)
788 err = mz_stream_write_int64(stream, 0);
789 else
790 err = mz_stream_write_int64(stream, file_info->uncompressed_size);
791 }
792 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
793 err = mz_stream_write_int64(stream, file_info->compressed_size);
794 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
795 err = mz_stream_write_int64(stream, file_info->disk_offset);
796 }
797 /* Write NTFS extra field */
798 if ((err == MZ_OK) && (field_length_ntfs > 0))
799 {
800 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_NTFS, field_length_ntfs);
801 if (err == MZ_OK)
802 err = mz_stream_write_uint32(stream, reserved);
803 if (err == MZ_OK)
804 err = mz_stream_write_uint16(stream, 0x01);
805 if (err == MZ_OK)
806 err = mz_stream_write_uint16(stream, field_length_ntfs - 8);
807 if (err == MZ_OK)
808 {
809 mz_zip_unix_to_ntfs_time(file_info->modified_date, &ntfs_time);
810 err = mz_stream_write_uint64(stream, ntfs_time);
811 }
812 if (err == MZ_OK)
813 {
814 mz_zip_unix_to_ntfs_time(file_info->accessed_date, &ntfs_time);
815 err = mz_stream_write_uint64(stream, ntfs_time);
816 }
817 if (err == MZ_OK)
818 {
819 mz_zip_unix_to_ntfs_time(file_info->creation_date, &ntfs_time);
820 err = mz_stream_write_uint64(stream, ntfs_time);
821 }
822 }
823 #ifdef HAVE_WZAES
824 /* Write AES extra field */
825 if ((err == MZ_OK) && (!skip_aes) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
826 {
827 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_AES, field_length_aes);
828 if (err == MZ_OK)
829 err = mz_stream_write_uint16(stream, file_info->aes_version);
830 if (err == MZ_OK)
831 err = mz_stream_write_uint8(stream, 'A');
832 if (err == MZ_OK)
833 err = mz_stream_write_uint8(stream, 'E');
834 if (err == MZ_OK)
835 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
836 if (err == MZ_OK)
837 err = mz_stream_write_uint16(stream, file_info->compression_method);
838 }
839 #endif
840 if ((err == MZ_OK) && (!local) && (file_info->comment != NULL))
841 {
842 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != file_info->comment_size)
843 err = MZ_WRITE_ERROR;
844 }
845
846 return err;
847 }
848
mz_zip_entry_write_descriptor(void * stream,uint8_t zip64,uint32_t crc32,int64_t compressed_size,int64_t uncompressed_size)849 static int32_t mz_zip_entry_write_descriptor(void *stream, uint8_t zip64, uint32_t crc32, int64_t compressed_size, int64_t uncompressed_size)
850 {
851 int32_t err = MZ_OK;
852
853 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
854 if (err == MZ_OK)
855 err = mz_stream_write_uint32(stream, crc32);
856
857 /* Store data descriptor as 8 bytes if zip 64 extension enabled */
858 if (err == MZ_OK)
859 {
860 /* Zip 64 extension is enabled when uncompressed size is > UINT32_MAX */
861 if (!zip64)
862 err = mz_stream_write_uint32(stream, (uint32_t)compressed_size);
863 else
864 err = mz_stream_write_int64(stream, compressed_size);
865 }
866 if (err == MZ_OK)
867 {
868 if (!zip64)
869 err = mz_stream_write_uint32(stream, (uint32_t)uncompressed_size);
870 else
871 err = mz_stream_write_int64(stream, uncompressed_size);
872 }
873
874 return err;
875 }
876
mz_zip_read_cd(void * handle)877 static int32_t mz_zip_read_cd(void *handle)
878 {
879 mz_zip *zip = (mz_zip *)handle;
880 uint64_t number_entry_cd64 = 0;
881 uint64_t number_entry = 0;
882 uint64_t number_entry_cd = 0;
883 int64_t eocd_pos = 0;
884 int64_t eocd_pos64 = 0;
885 int64_t value64i = 0;
886 uint16_t value16 = 0;
887 uint32_t value32 = 0;
888 uint64_t value64 = 0;
889 uint16_t comment_size = 0;
890 int32_t comment_read = 0;
891 int32_t err = MZ_OK;
892
893
894 if (zip == NULL)
895 return MZ_PARAM_ERROR;
896
897 /* Read and cache central directory records */
898 err = mz_zip_search_eocd(zip->stream, &eocd_pos);
899 if (err == MZ_OK)
900 {
901 /* The signature, already checked */
902 err = mz_stream_read_uint32(zip->stream, &value32);
903 /* Number of this disk */
904 if (err == MZ_OK)
905 err = mz_stream_read_uint16(zip->stream, &value16);
906 /* Number of the disk with the start of the central directory */
907 if (err == MZ_OK)
908 err = mz_stream_read_uint16(zip->stream, &value16);
909 zip->disk_number_with_cd = value16;
910 /* Total number of entries in the central dir on this disk */
911 if (err == MZ_OK)
912 err = mz_stream_read_uint16(zip->stream, &value16);
913 zip->number_entry = value16;
914 /* Total number of entries in the central dir */
915 if (err == MZ_OK)
916 err = mz_stream_read_uint16(zip->stream, &value16);
917 number_entry_cd = value16;
918 if (number_entry_cd != zip->number_entry)
919 err = MZ_FORMAT_ERROR;
920 /* Size of the central directory */
921 if (err == MZ_OK)
922 err = mz_stream_read_uint32(zip->stream, &value32);
923 if (err == MZ_OK)
924 zip->cd_size = value32;
925 /* Offset of start of central directory with respect to the starting disk number */
926 if (err == MZ_OK)
927 err = mz_stream_read_uint32(zip->stream, &value32);
928 if (err == MZ_OK)
929 zip->cd_offset = value32;
930 /* Zip file global comment length */
931 if (err == MZ_OK)
932 err = mz_stream_read_uint16(zip->stream, &comment_size);
933 if ((err == MZ_OK) && (comment_size > 0))
934 {
935 zip->comment = (char *)MZ_ALLOC(comment_size + 1);
936 if (zip->comment != NULL)
937 {
938 comment_read = mz_stream_read(zip->stream, zip->comment, comment_size);
939 /* Don't fail if incorrect comment length read, not critical */
940 if (comment_read < 0)
941 comment_read = 0;
942 zip->comment[comment_read] = 0;
943 }
944 }
945
946 if ((err == MZ_OK) && ((number_entry_cd == UINT16_MAX) || (zip->cd_offset == UINT32_MAX)))
947 {
948 /* Format should be Zip64, as the central directory or file size is too large */
949 if (mz_zip_search_zip64_eocd(zip->stream, eocd_pos, &eocd_pos64) == MZ_OK)
950 {
951 eocd_pos = eocd_pos64;
952
953 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
954 /* The signature, already checked */
955 if (err == MZ_OK)
956 err = mz_stream_read_uint32(zip->stream, &value32);
957 /* Size of zip64 end of central directory record */
958 if (err == MZ_OK)
959 err = mz_stream_read_uint64(zip->stream, &value64);
960 /* Version made by */
961 if (err == MZ_OK)
962 err = mz_stream_read_uint16(zip->stream, &zip->version_madeby);
963 /* Version needed to extract */
964 if (err == MZ_OK)
965 err = mz_stream_read_uint16(zip->stream, &value16);
966 /* Number of this disk */
967 if (err == MZ_OK)
968 err = mz_stream_read_uint32(zip->stream, &value32);
969 /* Number of the disk with the start of the central directory */
970 if (err == MZ_OK)
971 err = mz_stream_read_uint32(zip->stream, &zip->disk_number_with_cd);
972 /* Total number of entries in the central directory on this disk */
973 if (err == MZ_OK)
974 err = mz_stream_read_uint64(zip->stream, &number_entry);
975 /* Total number of entries in the central directory */
976 if (err == MZ_OK)
977 err = mz_stream_read_uint64(zip->stream, &number_entry_cd64);
978 if (number_entry == UINT32_MAX)
979 zip->number_entry = number_entry_cd64;
980 /* Size of the central directory */
981 if (err == MZ_OK)
982 {
983 err = mz_stream_read_int64(zip->stream, &zip->cd_size);
984 if (zip->cd_size < 0)
985 err = MZ_FORMAT_ERROR;
986 }
987 /* Offset of start of central directory with respect to the starting disk number */
988 if (err == MZ_OK)
989 {
990 err = mz_stream_read_int64(zip->stream, &zip->cd_offset);
991 if (zip->cd_offset < 0)
992 err = MZ_FORMAT_ERROR;
993 }
994 }
995 else if ((zip->number_entry == UINT16_MAX) || (number_entry_cd != zip->number_entry) ||
996 (zip->cd_size == UINT16_MAX) || (zip->cd_offset == UINT32_MAX))
997 {
998 err = MZ_FORMAT_ERROR;
999 }
1000 }
1001 }
1002
1003 if (err == MZ_OK)
1004 {
1005 mz_zip_print("Zip - Read cd (disk %"PRId32" entries %"PRId64" offset %"PRId64" size %"PRId64")\n",
1006 zip->disk_number_with_cd, zip->number_entry, zip->cd_offset, zip->cd_size);
1007
1008 /* Verify central directory signature exists at offset */
1009 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
1010 if (err == MZ_OK)
1011 err = mz_stream_read_uint32(zip->stream, &value32);
1012 if (value32 != MZ_ZIP_MAGIC_CENTRALHEADER)
1013 {
1014 /* If not found attempt to seek backward to find it */
1015 err = mz_stream_seek(zip->stream, eocd_pos - zip->cd_size, MZ_SEEK_SET);
1016 if (err == MZ_OK)
1017 err = mz_stream_read_uint32(zip->stream, &value32);
1018 if (value32 == MZ_ZIP_MAGIC_CENTRALHEADER)
1019 {
1020 /* If found compensate for incorrect locations */
1021 value64i = zip->cd_offset;
1022 zip->cd_offset = eocd_pos - zip->cd_size;
1023 /* Assume disk has prepended data */
1024 zip->disk_offset_shift = zip->cd_offset - value64i;
1025 }
1026 }
1027 }
1028
1029 if (err == MZ_OK)
1030 {
1031 if (eocd_pos < zip->cd_offset)
1032 {
1033 /* End of central dir should always come after central dir */
1034 err = MZ_FORMAT_ERROR;
1035 }
1036 else if (eocd_pos < zip->cd_offset + zip->cd_size)
1037 {
1038 /* Truncate size of cd if incorrect size or offset provided */
1039 zip->cd_size = eocd_pos - zip->cd_offset;
1040 }
1041 }
1042
1043 return err;
1044 }
1045
mz_zip_write_cd(void * handle)1046 static int32_t mz_zip_write_cd(void *handle)
1047 {
1048 mz_zip *zip = (mz_zip *)handle;
1049 int64_t zip64_eocd_pos_inzip = 0;
1050 int64_t disk_number = 0;
1051 int64_t disk_size = 0;
1052 int32_t comment_size = 0;
1053 int32_t err = MZ_OK;
1054
1055
1056 if (zip == NULL)
1057 return MZ_PARAM_ERROR;
1058
1059 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
1060 zip->disk_number_with_cd = (uint32_t)disk_number;
1061 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_SIZE, &disk_size) == MZ_OK && disk_size > 0)
1062 zip->disk_number_with_cd += 1;
1063 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1064
1065 zip->cd_offset = mz_stream_tell(zip->stream);
1066 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_END);
1067 zip->cd_size = (uint32_t)mz_stream_tell(zip->cd_mem_stream);
1068 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_SET);
1069
1070 err = mz_stream_copy(zip->stream, zip->cd_mem_stream, (int32_t)zip->cd_size);
1071
1072 mz_zip_print("Zip - Write cd (disk %"PRId32" entries %"PRId64" offset %"PRId64" size %"PRId64")\n",
1073 zip->disk_number_with_cd, zip->number_entry, zip->cd_offset, zip->cd_size);
1074
1075 /* Write the ZIP64 central directory header */
1076 if (zip->cd_offset >= UINT32_MAX || zip->number_entry > UINT16_MAX)
1077 {
1078 zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
1079
1080 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
1081
1082 /* Size of this 'zip64 end of central directory' */
1083 if (err == MZ_OK)
1084 err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
1085 /* Version made by */
1086 if (err == MZ_OK)
1087 err = mz_stream_write_uint16(zip->stream, zip->version_madeby);
1088 /* Version needed */
1089 if (err == MZ_OK)
1090 err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
1091 /* Number of this disk */
1092 if (err == MZ_OK)
1093 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
1094 /* Number of the disk with the start of the central directory */
1095 if (err == MZ_OK)
1096 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
1097 /* Total number of entries in the central dir on this disk */
1098 if (err == MZ_OK)
1099 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
1100 /* Total number of entries in the central dir */
1101 if (err == MZ_OK)
1102 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
1103 /* Size of the central directory */
1104 if (err == MZ_OK)
1105 err = mz_stream_write_int64(zip->stream, zip->cd_size);
1106 /* Offset of start of central directory with respect to the starting disk number */
1107 if (err == MZ_OK)
1108 err = mz_stream_write_int64(zip->stream, zip->cd_offset);
1109 if (err == MZ_OK)
1110 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
1111
1112 /* Number of the disk with the start of the central directory */
1113 if (err == MZ_OK)
1114 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
1115 /* Relative offset to the end of zip64 central directory */
1116 if (err == MZ_OK)
1117 err = mz_stream_write_int64(zip->stream, zip64_eocd_pos_inzip);
1118 /* Number of the disk with the start of the central directory */
1119 if (err == MZ_OK)
1120 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd + 1);
1121 }
1122
1123 /* Write the central directory header */
1124
1125 /* Signature */
1126 if (err == MZ_OK)
1127 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
1128 /* Number of this disk */
1129 if (err == MZ_OK)
1130 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
1131 /* Number of the disk with the start of the central directory */
1132 if (err == MZ_OK)
1133 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
1134 /* Total number of entries in the central dir on this disk */
1135 if (err == MZ_OK)
1136 {
1137 if (zip->number_entry >= UINT16_MAX)
1138 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
1139 else
1140 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
1141 }
1142 /* Total number of entries in the central dir */
1143 if (err == MZ_OK)
1144 {
1145 if (zip->number_entry >= UINT16_MAX)
1146 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
1147 else
1148 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
1149 }
1150 /* Size of the central directory */
1151 if (err == MZ_OK)
1152 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_size);
1153 /* Offset of start of central directory with respect to the starting disk number */
1154 if (err == MZ_OK)
1155 {
1156 if (zip->cd_offset >= UINT32_MAX)
1157 err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
1158 else
1159 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_offset);
1160 }
1161
1162 /* Write global comment */
1163 if (zip->comment != NULL)
1164 {
1165 comment_size = (int32_t)strlen(zip->comment);
1166 if (comment_size > UINT16_MAX)
1167 comment_size = UINT16_MAX;
1168 }
1169 if (err == MZ_OK)
1170 err = mz_stream_write_uint16(zip->stream, (uint16_t)comment_size);
1171 if (err == MZ_OK)
1172 {
1173 if (mz_stream_write(zip->stream, zip->comment, comment_size) != comment_size)
1174 err = MZ_READ_ERROR;
1175 }
1176 return err;
1177 }
1178
mz_zip_recover_cd(void * handle)1179 static int32_t mz_zip_recover_cd(void *handle)
1180 {
1181 mz_zip *zip = (mz_zip *)handle;
1182 mz_zip_file local_file_info;
1183 void *local_file_info_stream = NULL;
1184 void *cd_mem_stream = NULL;
1185 uint64_t number_entry = 0;
1186 int64_t descriptor_pos = 0;
1187 int64_t disk_offset = 0;
1188 int64_t disk_number = 0;
1189 int64_t compressed_size = 0;
1190 int64_t uncompressed_size = 0;
1191 uint8_t descriptor_magic[4] = MZ_ZIP_MAGIC_DATADESCRIPTORU8;
1192 uint32_t crc32 = 0;
1193 int32_t disk_number_with_cd = 0;
1194 int32_t err = MZ_OK;
1195 uint8_t zip64 = 0;
1196
1197
1198 mz_zip_print("Zip - Recover cd\n");
1199
1200 mz_zip_get_cd_mem_stream(handle, &cd_mem_stream);
1201
1202 /* Determine if we are on a split disk or not */
1203 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, 0);
1204 if (mz_stream_tell(zip->stream) < 0)
1205 {
1206 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1207 mz_stream_seek(zip->stream, 0, MZ_SEEK_SET);
1208 }
1209 else
1210 disk_number_with_cd = 1;
1211
1212 if (mz_stream_is_open(cd_mem_stream) != MZ_OK)
1213 err = mz_stream_mem_open(cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
1214
1215 mz_stream_mem_create(&local_file_info_stream);
1216 mz_stream_mem_open(local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
1217
1218 while (err == MZ_OK)
1219 {
1220 memset(&local_file_info, 0, sizeof(local_file_info));
1221
1222 /* Get current offset and disk number for central dir record */
1223 disk_offset = mz_stream_tell(zip->stream);
1224 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1225
1226 /* Read local headers */
1227 err = mz_zip_entry_read_header(zip->stream, 1, &local_file_info, local_file_info_stream);
1228
1229 local_file_info.disk_offset = disk_offset;
1230 if (disk_number < 0)
1231 disk_number = 0;
1232 local_file_info.disk_number = (uint32_t)disk_number;
1233
1234 if (err == MZ_OK)
1235 {
1236 if (local_file_info.compressed_size > 0)
1237 {
1238 err = mz_stream_seek(zip->stream, local_file_info.compressed_size, MZ_SEEK_CUR);
1239 }
1240 else if (local_file_info.uncompressed_size > 0)
1241 {
1242 err = mz_stream_find(zip->stream, (const void *)descriptor_magic, sizeof(descriptor_magic),
1243 INT64_MAX, &descriptor_pos);
1244 }
1245 }
1246
1247 /* Read descriptor if it exists so we can get to the next local header */
1248 if ((err == MZ_OK) && (local_file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR))
1249 {
1250 if (mz_zip_extrafield_contains(local_file_info.extrafield,
1251 local_file_info.extrafield_size, MZ_ZIP_EXTENSION_ZIP64, NULL) == MZ_OK)
1252 zip64 = 1;
1253
1254 err = mz_zip_entry_read_descriptor(zip->stream, zip64, &crc32,
1255 &compressed_size, &uncompressed_size);
1256
1257 if (local_file_info.crc == 0)
1258 local_file_info.crc = crc32;
1259 if (local_file_info.compressed_size == 0)
1260 local_file_info.compressed_size = compressed_size;
1261 if (local_file_info.uncompressed_size == 0)
1262 local_file_info.uncompressed_size = uncompressed_size;
1263 }
1264
1265 /* Rewrite central dir with local headers and offsets */
1266 if (err == MZ_OK)
1267 err = mz_zip_entry_write_header(cd_mem_stream, 0, &local_file_info);
1268
1269 if (err == MZ_OK)
1270 number_entry += 1;
1271 }
1272
1273 mz_stream_mem_delete(&local_file_info_stream);
1274
1275 mz_zip_print("Zip - Recover cd complete (cddisk %"PRId32" entries %"PRId64")\n",
1276 disk_number_with_cd, number_entry);
1277
1278 if (number_entry == 0)
1279 return err;
1280
1281 /* Set new upper seek boundary for central dir mem stream */
1282 disk_offset = mz_stream_tell(cd_mem_stream);
1283 mz_stream_mem_set_buffer_limit(cd_mem_stream, (int32_t)disk_offset);
1284
1285 /* Set new central directory info */
1286 mz_zip_set_cd_stream(handle, 0, cd_mem_stream);
1287 mz_zip_set_number_entry(handle, number_entry);
1288 mz_zip_set_disk_number_with_cd(handle, disk_number_with_cd);
1289
1290 return MZ_OK;
1291 }
1292
mz_zip_create(void ** handle)1293 void *mz_zip_create(void **handle)
1294 {
1295 mz_zip *zip = NULL;
1296
1297 zip = (mz_zip *)MZ_ALLOC(sizeof(mz_zip));
1298 if (zip != NULL)
1299 memset(zip, 0, sizeof(mz_zip));
1300 if (handle != NULL)
1301 *handle = zip;
1302
1303 return zip;
1304 }
1305
mz_zip_delete(void ** handle)1306 void mz_zip_delete(void **handle)
1307 {
1308 mz_zip *zip = NULL;
1309 if (handle == NULL)
1310 return;
1311 zip = (mz_zip *)*handle;
1312 if (zip != NULL)
1313 {
1314 MZ_FREE(zip);
1315 }
1316 *handle = NULL;
1317 }
1318
mz_zip_open(void * handle,void * stream,int32_t mode)1319 int32_t mz_zip_open(void *handle, void *stream, int32_t mode)
1320 {
1321 mz_zip *zip = (mz_zip *)handle;
1322 int32_t err = MZ_OK;
1323
1324
1325 if (zip == NULL)
1326 return MZ_PARAM_ERROR;
1327
1328 mz_zip_print("Zip - Open\n");
1329
1330 zip->stream = stream;
1331
1332 mz_stream_mem_create(&zip->cd_mem_stream);
1333
1334 if (mode & MZ_OPEN_MODE_WRITE)
1335 {
1336 mz_stream_mem_open(zip->cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
1337 zip->cd_stream = zip->cd_mem_stream;
1338 }
1339 else
1340 {
1341 zip->cd_stream = stream;
1342 }
1343
1344 if ((mode & MZ_OPEN_MODE_READ) || (mode & MZ_OPEN_MODE_APPEND))
1345 {
1346 if ((mode & MZ_OPEN_MODE_CREATE) == 0)
1347 {
1348 err = mz_zip_read_cd(zip);
1349 if (err != MZ_OK)
1350 {
1351 mz_zip_print("Zip - Error detected reading cd (%"PRId32")", err);
1352 if (zip->recover && mz_zip_recover_cd(zip) == MZ_OK)
1353 err = MZ_OK;
1354 }
1355 }
1356
1357 if ((err == MZ_OK) && (mode & MZ_OPEN_MODE_APPEND))
1358 {
1359 if (zip->cd_size > 0)
1360 {
1361 /* Store central directory in memory */
1362 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
1363 if (err == MZ_OK)
1364 err = mz_stream_copy(zip->cd_mem_stream, zip->stream, (int32_t)zip->cd_size);
1365 if (err == MZ_OK)
1366 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
1367 }
1368 else
1369 {
1370 /* If no central directory, append new zip to end of file */
1371 err = mz_stream_seek(zip->stream, 0, MZ_SEEK_END);
1372 }
1373
1374 if (zip->disk_number_with_cd > 0)
1375 {
1376 /* Move to last disk to begin appending */
1377 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->disk_number_with_cd - 1);
1378 }
1379 }
1380 else
1381 {
1382 zip->cd_start_pos = zip->cd_offset;
1383 }
1384 }
1385
1386 if (err != MZ_OK)
1387 {
1388 mz_zip_close(zip);
1389 return err;
1390 }
1391
1392 /* Memory streams used to store variable length file info data */
1393 mz_stream_mem_create(&zip->file_info_stream);
1394 mz_stream_mem_open(zip->file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
1395
1396 mz_stream_mem_create(&zip->local_file_info_stream);
1397 mz_stream_mem_open(zip->local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
1398
1399 zip->open_mode = mode;
1400
1401 return err;
1402 }
1403
mz_zip_close(void * handle)1404 int32_t mz_zip_close(void *handle)
1405 {
1406 mz_zip *zip = (mz_zip *)handle;
1407 int32_t err = MZ_OK;
1408
1409 if (zip == NULL)
1410 return MZ_PARAM_ERROR;
1411
1412 mz_zip_print("Zip - Close\n");
1413
1414 if (mz_zip_entry_is_open(handle) == MZ_OK)
1415 err = mz_zip_entry_close(handle);
1416
1417 if ((err == MZ_OK) && (zip->open_mode & MZ_OPEN_MODE_WRITE))
1418 err = mz_zip_write_cd(handle);
1419
1420 if (zip->cd_mem_stream != NULL)
1421 {
1422 mz_stream_close(zip->cd_mem_stream);
1423 mz_stream_delete(&zip->cd_mem_stream);
1424 }
1425
1426 if (zip->file_info_stream != NULL)
1427 {
1428 mz_stream_mem_close(zip->file_info_stream);
1429 mz_stream_mem_delete(&zip->file_info_stream);
1430 }
1431 if (zip->local_file_info_stream != NULL)
1432 {
1433 mz_stream_mem_close(zip->local_file_info_stream);
1434 mz_stream_mem_delete(&zip->local_file_info_stream);
1435 }
1436
1437 if (zip->comment)
1438 {
1439 MZ_FREE(zip->comment);
1440 zip->comment = NULL;
1441 }
1442
1443 zip->stream = NULL;
1444 zip->cd_stream = NULL;
1445
1446 return err;
1447 }
1448
mz_zip_get_comment(void * handle,const char ** comment)1449 int32_t mz_zip_get_comment(void *handle, const char **comment)
1450 {
1451 mz_zip *zip = (mz_zip *)handle;
1452 if (zip == NULL || comment == NULL)
1453 return MZ_PARAM_ERROR;
1454 if (zip->comment == NULL)
1455 return MZ_EXIST_ERROR;
1456 *comment = zip->comment;
1457 return MZ_OK;
1458 }
1459
mz_zip_set_comment(void * handle,const char * comment)1460 int32_t mz_zip_set_comment(void *handle, const char *comment)
1461 {
1462 mz_zip *zip = (mz_zip *)handle;
1463 int32_t comment_size = 0;
1464 if (zip == NULL || comment == NULL)
1465 return MZ_PARAM_ERROR;
1466 if (zip->comment != NULL)
1467 MZ_FREE(zip->comment);
1468 comment_size = (int32_t)strlen(comment);
1469 if (comment_size > UINT16_MAX)
1470 return MZ_PARAM_ERROR;
1471 zip->comment = (char *)MZ_ALLOC(comment_size + 1);
1472 if (zip->comment == NULL)
1473 return MZ_MEM_ERROR;
1474 strncpy(zip->comment, comment, comment_size);
1475 zip->comment[comment_size] = 0;
1476 return MZ_OK;
1477 }
1478
mz_zip_get_version_madeby(void * handle,uint16_t * version_madeby)1479 int32_t mz_zip_get_version_madeby(void *handle, uint16_t *version_madeby)
1480 {
1481 mz_zip *zip = (mz_zip *)handle;
1482 if (zip == NULL || version_madeby == NULL)
1483 return MZ_PARAM_ERROR;
1484 *version_madeby = zip->version_madeby;
1485 return MZ_OK;
1486 }
1487
mz_zip_set_version_madeby(void * handle,uint16_t version_madeby)1488 int32_t mz_zip_set_version_madeby(void *handle, uint16_t version_madeby)
1489 {
1490 mz_zip *zip = (mz_zip *)handle;
1491 if (zip == NULL)
1492 return MZ_PARAM_ERROR;
1493 zip->version_madeby = version_madeby;
1494 return MZ_OK;
1495 }
1496
mz_zip_set_recover(void * handle,uint8_t recover)1497 int32_t mz_zip_set_recover(void *handle, uint8_t recover)
1498 {
1499 mz_zip *zip = (mz_zip *)handle;
1500 if (zip == NULL)
1501 return MZ_PARAM_ERROR;
1502 zip->recover = recover;
1503 return MZ_OK;
1504 }
1505
mz_zip_get_stream(void * handle,void ** stream)1506 int32_t mz_zip_get_stream(void *handle, void **stream)
1507 {
1508 mz_zip *zip = (mz_zip *)handle;
1509 if (zip == NULL || stream == NULL)
1510 return MZ_PARAM_ERROR;
1511 *stream = zip->stream;
1512 if (*stream == NULL)
1513 return MZ_EXIST_ERROR;
1514 return MZ_OK;
1515 }
1516
mz_zip_set_cd_stream(void * handle,int64_t cd_start_pos,void * cd_stream)1517 int32_t mz_zip_set_cd_stream(void *handle, int64_t cd_start_pos, void *cd_stream)
1518 {
1519 mz_zip *zip = (mz_zip *)handle;
1520 if (zip == NULL || cd_stream == NULL)
1521 return MZ_PARAM_ERROR;
1522 zip->cd_stream = cd_stream;
1523 zip->cd_start_pos = cd_start_pos;
1524 return MZ_OK;
1525 }
1526
mz_zip_get_cd_mem_stream(void * handle,void ** cd_mem_stream)1527 int32_t mz_zip_get_cd_mem_stream(void *handle, void **cd_mem_stream)
1528 {
1529 mz_zip *zip = (mz_zip *)handle;
1530 if (zip == NULL || cd_mem_stream == NULL)
1531 return MZ_PARAM_ERROR;
1532 *cd_mem_stream = zip->cd_mem_stream;
1533 if (*cd_mem_stream == NULL)
1534 return MZ_EXIST_ERROR;
1535 return MZ_OK;
1536 }
1537
mz_zip_entry_close_int(void * handle)1538 static int32_t mz_zip_entry_close_int(void *handle)
1539 {
1540 mz_zip *zip = (mz_zip *)handle;
1541
1542 if (zip->crypt_stream != NULL)
1543 mz_stream_delete(&zip->crypt_stream);
1544 zip->crypt_stream = NULL;
1545 if (zip->compress_stream != NULL)
1546 mz_stream_delete(&zip->compress_stream);
1547 zip->compress_stream = NULL;
1548
1549 zip->entry_opened = 0;
1550
1551 return MZ_OK;
1552 }
1553
mz_zip_entry_open_int(void * handle,uint8_t raw,int16_t compress_level,const char * password)1554 static int32_t mz_zip_entry_open_int(void *handle, uint8_t raw, int16_t compress_level, const char *password)
1555 {
1556 mz_zip *zip = (mz_zip *)handle;
1557 int64_t max_total_in = 0;
1558 int64_t header_size = 0;
1559 int64_t footer_size = 0;
1560 int32_t err = MZ_OK;
1561 uint8_t use_crypt = 0;
1562
1563 if (zip == NULL)
1564 return MZ_PARAM_ERROR;
1565
1566 switch (zip->file_info.compression_method)
1567 {
1568 case MZ_COMPRESS_METHOD_STORE:
1569 case MZ_COMPRESS_METHOD_DEFLATE:
1570 #ifdef HAVE_BZIP2
1571 case MZ_COMPRESS_METHOD_BZIP2:
1572 #endif
1573 #ifdef HAVE_LZMA
1574 case MZ_COMPRESS_METHOD_LZMA:
1575 #endif
1576 err = MZ_OK;
1577 break;
1578 default:
1579 return MZ_SUPPORT_ERROR;
1580 }
1581
1582 #ifndef HAVE_WZAES
1583 if (zip->file_info.aes_version)
1584 return MZ_SUPPORT_ERROR;
1585 #endif
1586
1587 zip->entry_raw = raw;
1588
1589 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password != NULL))
1590 {
1591 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
1592 {
1593 /* Encrypt only when we are not trying to write raw and password is supplied. */
1594 if (!zip->entry_raw)
1595 use_crypt = 1;
1596 }
1597 else if (zip->open_mode & MZ_OPEN_MODE_READ)
1598 {
1599 /* Decrypt only when password is supplied. Don't error when password */
1600 /* is not supplied as we may want to read the raw encrypted data. */
1601 use_crypt = 1;
1602 }
1603 }
1604
1605 if ((err == MZ_OK) && (use_crypt))
1606 {
1607 #ifdef HAVE_WZAES
1608 if (zip->file_info.aes_version)
1609 {
1610 mz_stream_wzaes_create(&zip->crypt_stream);
1611 mz_stream_wzaes_set_password(zip->crypt_stream, password);
1612 mz_stream_wzaes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
1613 }
1614 else
1615 #endif
1616 {
1617 #ifdef HAVE_PKCRYPT
1618 uint8_t verify1 = 0;
1619 uint8_t verify2 = 0;
1620
1621 /* Info-ZIP modification to ZipCrypto format: */
1622 /* If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time. */
1623
1624 if (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR)
1625 {
1626 uint32_t dos_date = 0;
1627
1628 dos_date = mz_zip_time_t_to_dos_date(zip->file_info.modified_date);
1629
1630 verify1 = (uint8_t)((dos_date >> 16) & 0xff);
1631 verify2 = (uint8_t)((dos_date >> 8) & 0xff);
1632 }
1633 else
1634 {
1635 verify1 = (uint8_t)((zip->file_info.crc >> 16) & 0xff);
1636 verify2 = (uint8_t)((zip->file_info.crc >> 24) & 0xff);
1637 }
1638
1639 mz_stream_pkcrypt_create(&zip->crypt_stream);
1640 mz_stream_pkcrypt_set_password(zip->crypt_stream, password);
1641 mz_stream_pkcrypt_set_verify(zip->crypt_stream, verify1, verify2);
1642 #endif
1643 }
1644 }
1645
1646 if (err == MZ_OK)
1647 {
1648 if (zip->crypt_stream == NULL)
1649 mz_stream_raw_create(&zip->crypt_stream);
1650
1651 mz_stream_set_base(zip->crypt_stream, zip->stream);
1652
1653 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
1654 }
1655
1656 if (err == MZ_OK)
1657 {
1658 if (zip->entry_raw || zip->file_info.compression_method == MZ_COMPRESS_METHOD_STORE)
1659 mz_stream_raw_create(&zip->compress_stream);
1660 #if defined(HAVE_ZLIB) || defined(HAVE_LIBCOMP)
1661 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
1662 mz_stream_zlib_create(&zip->compress_stream);
1663 #endif
1664 #ifdef HAVE_BZIP2
1665 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_BZIP2)
1666 mz_stream_bzip_create(&zip->compress_stream);
1667 #endif
1668 #ifdef HAVE_LZMA
1669 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
1670 mz_stream_lzma_create(&zip->compress_stream);
1671 #endif
1672 else
1673 err = MZ_PARAM_ERROR;
1674 }
1675
1676 if (err == MZ_OK)
1677 {
1678 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
1679 {
1680 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, compress_level);
1681 }
1682 else
1683 {
1684 #ifndef HAVE_LIBCOMP
1685 if (zip->entry_raw || zip->file_info.compression_method == MZ_COMPRESS_METHOD_STORE || zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
1686 #endif
1687 {
1688 max_total_in = zip->file_info.compressed_size;
1689 mz_stream_set_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1690
1691 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_HEADER_SIZE, &header_size) == MZ_OK)
1692 max_total_in -= header_size;
1693 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
1694 max_total_in -= footer_size;
1695
1696 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1697 }
1698 if ((zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA) && (zip->file_info.flag & MZ_ZIP_FLAG_LZMA_EOS_MARKER) == 0)
1699 {
1700 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, zip->file_info.compressed_size);
1701 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT_MAX, zip->file_info.uncompressed_size);
1702 }
1703 }
1704
1705 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1706
1707 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1708 }
1709
1710 if (err == MZ_OK)
1711 {
1712 zip->entry_opened = 1;
1713 zip->entry_crc32 = 0;
1714 }
1715 else
1716 {
1717 mz_zip_entry_close_int(handle);
1718 }
1719
1720 return err;
1721 }
1722
mz_zip_entry_is_open(void * handle)1723 int32_t mz_zip_entry_is_open(void *handle)
1724 {
1725 mz_zip *zip = (mz_zip *)handle;
1726 if (zip == NULL)
1727 return MZ_PARAM_ERROR;
1728 if (zip->entry_opened == 0)
1729 return MZ_EXIST_ERROR;
1730 return MZ_OK;
1731 }
1732
mz_zip_seek_to_local_header(void * handle)1733 static int32_t mz_zip_seek_to_local_header(void *handle)
1734 {
1735 mz_zip *zip = (mz_zip *)handle;
1736
1737
1738 if (zip->file_info.disk_number == zip->disk_number_with_cd)
1739 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1740 else
1741 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_number);
1742
1743 mz_zip_print("Zip - Entry - Seek local (disk %"PRId32" offset %"PRId64")\n",
1744 zip->file_info.disk_number, zip->file_info.disk_offset);
1745
1746 return mz_stream_seek(zip->stream, zip->file_info.disk_offset + zip->disk_offset_shift, MZ_SEEK_SET);
1747 }
1748
mz_zip_entry_read_open(void * handle,uint8_t raw,const char * password)1749 int32_t mz_zip_entry_read_open(void *handle, uint8_t raw, const char *password)
1750 {
1751 mz_zip *zip = (mz_zip *)handle;
1752 int32_t err = MZ_OK;
1753 int32_t err_shift = MZ_OK;
1754
1755 #if defined(MZ_ZIP_NO_ENCRYPTION)
1756 if (password != NULL)
1757 return MZ_SUPPORT_ERROR;
1758 #endif
1759 if (zip == NULL)
1760 return MZ_PARAM_ERROR;
1761 if ((zip->open_mode & MZ_OPEN_MODE_READ) == 0)
1762 return MZ_PARAM_ERROR;
1763 if (zip->entry_scanned == 0)
1764 return MZ_PARAM_ERROR;
1765 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL) && (!raw))
1766 return MZ_PARAM_ERROR;
1767
1768 mz_zip_print("Zip - Entry - Read open (raw %"PRId32")\n", raw);
1769
1770 err = mz_zip_seek_to_local_header(handle);
1771 if (err == MZ_OK)
1772 err = mz_zip_entry_read_header(zip->stream, 1, &zip->local_file_info, zip->local_file_info_stream);
1773
1774 if (err == MZ_FORMAT_ERROR && zip->disk_offset_shift > 0)
1775 {
1776 /* Perhaps we didn't compensated correctly for incorrect cd offset */
1777 err_shift = mz_stream_seek(zip->stream, zip->file_info.disk_offset, MZ_SEEK_SET);
1778 if (err_shift == MZ_OK)
1779 err_shift = mz_zip_entry_read_header(zip->stream, 1, &zip->local_file_info, zip->local_file_info_stream);
1780 if (err_shift == MZ_OK)
1781 {
1782 zip->disk_offset_shift = 0;
1783 err = err_shift;
1784 }
1785 }
1786
1787 #ifdef MZ_ZIP_NO_DECOMPRESSION
1788 if (!raw && zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
1789 err = MZ_SUPPORT_ERROR;
1790 #endif
1791 if (err == MZ_OK)
1792 err = mz_zip_entry_open_int(handle, raw, 0, password);
1793
1794 return err;
1795 }
1796
mz_zip_entry_write_open(void * handle,const mz_zip_file * file_info,int16_t compress_level,uint8_t raw,const char * password)1797 int32_t mz_zip_entry_write_open(void *handle, const mz_zip_file *file_info, int16_t compress_level, uint8_t raw, const char *password)
1798 {
1799 mz_zip *zip = (mz_zip *)handle;
1800 int64_t filename_pos = -1;
1801 int64_t extrafield_pos = 0;
1802 int64_t comment_pos = 0;
1803 int64_t disk_number = 0;
1804 uint8_t is_dir = 0;
1805 int32_t err = MZ_OK;
1806
1807 #if defined(MZ_ZIP_NO_ENCRYPTION)
1808 if (password != NULL)
1809 return MZ_SUPPORT_ERROR;
1810 #endif
1811 if (zip == NULL || file_info == NULL || file_info->filename == NULL)
1812 return MZ_PARAM_ERROR;
1813
1814 if (mz_zip_entry_is_open(handle) == MZ_OK)
1815 {
1816 err = mz_zip_entry_close(handle);
1817 if (err != MZ_OK)
1818 return err;
1819 }
1820
1821 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
1822
1823 mz_zip_print("Zip - Entry - Write open - %s (level %"PRId16" raw %"PRId8")\n",
1824 zip->file_info.filename, compress_level, raw);
1825
1826 mz_stream_seek(zip->file_info_stream, 0, MZ_SEEK_SET);
1827 mz_stream_write(zip->file_info_stream, file_info, sizeof(mz_zip_file));
1828
1829 /* Copy filename, extrafield, and comment internally */
1830 filename_pos = mz_stream_tell(zip->file_info_stream);
1831 if (file_info->filename != NULL)
1832 mz_stream_write(zip->file_info_stream, file_info->filename, (int32_t)strlen(file_info->filename));
1833 mz_stream_write_uint8(zip->file_info_stream, 0);
1834
1835 extrafield_pos = mz_stream_tell(zip->file_info_stream);
1836 if (file_info->extrafield != NULL)
1837 mz_stream_write(zip->file_info_stream, file_info->extrafield, file_info->extrafield_size);
1838 mz_stream_write_uint8(zip->file_info_stream, 0);
1839
1840 comment_pos = mz_stream_tell(zip->file_info_stream);
1841 if (file_info->comment != NULL)
1842 mz_stream_write(zip->file_info_stream, file_info->comment, file_info->comment_size);
1843 mz_stream_write_uint8(zip->file_info_stream, 0);
1844
1845 mz_stream_mem_get_buffer_at(zip->file_info_stream, filename_pos, (const void **)&zip->file_info.filename);
1846 mz_stream_mem_get_buffer_at(zip->file_info_stream, extrafield_pos, (const void **)&zip->file_info.extrafield);
1847 mz_stream_mem_get_buffer_at(zip->file_info_stream, comment_pos, (const void **)&zip->file_info.comment);
1848
1849 if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
1850 {
1851 if ((compress_level == 8) || (compress_level == 9))
1852 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
1853 if (compress_level == 2)
1854 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
1855 if (compress_level == 1)
1856 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1857 }
1858 #ifdef HAVE_LZMA
1859 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
1860 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
1861 #endif
1862
1863 if (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK)
1864 is_dir = 1;
1865
1866 if (!is_dir)
1867 {
1868 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
1869 if (password != NULL)
1870 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
1871 }
1872
1873 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1874 zip->file_info.disk_number = (uint32_t)disk_number;
1875
1876 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
1877 zip->file_info.crc = 0;
1878 zip->file_info.compressed_size = 0;
1879
1880 #ifdef HAVE_WZAES
1881 if (zip->file_info.aes_version && zip->file_info.aes_encryption_mode == 0)
1882 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTION_MODE_256;
1883 #endif
1884
1885 if ((compress_level == 0) || (is_dir))
1886 zip->file_info.compression_method = MZ_COMPRESS_METHOD_STORE;
1887
1888 #ifdef MZ_ZIP_NO_COMPRESSION
1889 if (zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
1890 err = MZ_SUPPORT_ERROR;
1891 #endif
1892 if (err == MZ_OK)
1893 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
1894 if (err == MZ_OK)
1895 err = mz_zip_entry_open_int(handle, raw, compress_level, password);
1896
1897 return err;
1898 }
1899
mz_zip_entry_read(void * handle,void * buf,int32_t len)1900 int32_t mz_zip_entry_read(void *handle, void *buf, int32_t len)
1901 {
1902 mz_zip *zip = (mz_zip *)handle;
1903 int32_t read = 0;
1904
1905 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
1906 return MZ_PARAM_ERROR;
1907 if (UINT_MAX == UINT16_MAX && len > UINT16_MAX) /* zlib limitation */
1908 return MZ_PARAM_ERROR;
1909 if (len == 0)
1910 return MZ_PARAM_ERROR;
1911
1912 if (zip->file_info.compressed_size == 0)
1913 return 0;
1914
1915 /* Read entire entry even if uncompressed_size = 0, otherwise */
1916 /* aes encryption validation will fail if compressed_size > 0 */
1917 read = mz_stream_read(zip->compress_stream, buf, len);
1918 if (read > 0)
1919 zip->entry_crc32 = mz_crypt_crc32_update(zip->entry_crc32, buf, read);
1920
1921 mz_zip_print("Zip - Entry - Read - %"PRId32" (max %"PRId32")\n", read, len);
1922
1923 return read;
1924 }
1925
mz_zip_entry_write(void * handle,const void * buf,int32_t len)1926 int32_t mz_zip_entry_write(void *handle, const void *buf, int32_t len)
1927 {
1928 mz_zip *zip = (mz_zip *)handle;
1929 int32_t written = 0;
1930
1931 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
1932 return MZ_PARAM_ERROR;
1933 written = mz_stream_write(zip->compress_stream, buf, len);
1934 if (written > 0)
1935 zip->entry_crc32 = mz_crypt_crc32_update(zip->entry_crc32, buf, written);
1936
1937 mz_zip_print("Zip - Entry - Write - %"PRId32" (max %"PRId32")\n", written, len);
1938
1939 return written;
1940 }
1941
mz_zip_entry_read_close(void * handle,uint32_t * crc32,int64_t * compressed_size,int64_t * uncompressed_size)1942 int32_t mz_zip_entry_read_close(void *handle, uint32_t *crc32, int64_t *compressed_size,
1943 int64_t *uncompressed_size)
1944 {
1945 mz_zip *zip = (mz_zip *)handle;
1946 int64_t total_in = 0;
1947 int32_t err = MZ_OK;
1948 uint8_t zip64 = 0;
1949
1950 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
1951 return MZ_PARAM_ERROR;
1952
1953 mz_stream_close(zip->compress_stream);
1954
1955 mz_zip_print("Zip - Entry - Read Close\n");
1956
1957 if (crc32 != NULL)
1958 *crc32 = zip->file_info.crc;
1959 if (compressed_size != NULL)
1960 *compressed_size = zip->file_info.compressed_size;
1961 if (uncompressed_size != NULL)
1962 *uncompressed_size = zip->file_info.uncompressed_size;
1963
1964 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in);
1965
1966 if ((zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR) &&
1967 ((zip->file_info.flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO) == 0) &&
1968 (crc32 != NULL || compressed_size != NULL || uncompressed_size != NULL))
1969 {
1970 /* Check to see if data descriptor is zip64 bit format or not */
1971 if (mz_zip_extrafield_contains(zip->local_file_info.extrafield,
1972 zip->local_file_info.extrafield_size, MZ_ZIP_EXTENSION_ZIP64, NULL) == MZ_OK)
1973 zip64 = 1;
1974
1975 err = mz_zip_seek_to_local_header(handle);
1976
1977 /* Seek to end of compressed stream since we might have over-read during compression */
1978 if (err == MZ_OK)
1979 err = mz_stream_seek(zip->stream, MZ_ZIP_SIZE_LD_ITEM +
1980 (int64_t)zip->local_file_info.filename_size +
1981 (int64_t)zip->local_file_info.extrafield_size +
1982 total_in, MZ_SEEK_CUR);
1983
1984 /* Read data descriptor */
1985 if (err == MZ_OK)
1986 err = mz_zip_entry_read_descriptor(zip->stream, zip64,
1987 crc32, compressed_size, uncompressed_size);
1988 }
1989
1990 /* If entire entry was not read verification will fail */
1991 if ((err == MZ_OK) && (total_in > 0) && (!zip->entry_raw))
1992 {
1993 #ifdef HAVE_WZAES
1994 /* AES zip version AE-1 will expect a valid crc as well */
1995 if (zip->file_info.aes_version <= 0x0001)
1996 #endif
1997 {
1998 if (zip->entry_crc32 != zip->file_info.crc)
1999 {
2000 mz_zip_print("Zip - Entry - Crc failed (actual 0x%08"PRIx32" expected 0x%08"PRIx32")\n",
2001 zip->entry_crc32, zip->file_info.crc);
2002
2003 err = MZ_CRC_ERROR;
2004 }
2005 }
2006 }
2007
2008 mz_zip_entry_close_int(handle);
2009
2010 return err;
2011 }
2012
mz_zip_entry_write_close(void * handle,uint32_t crc32,int64_t compressed_size,int64_t uncompressed_size)2013 int32_t mz_zip_entry_write_close(void *handle, uint32_t crc32, int64_t compressed_size,
2014 int64_t uncompressed_size)
2015 {
2016 mz_zip *zip = (mz_zip *)handle;
2017 int32_t err = MZ_OK;
2018 uint8_t zip64 = 0;
2019
2020 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
2021 return MZ_PARAM_ERROR;
2022
2023 mz_stream_close(zip->compress_stream);
2024
2025 if (!zip->entry_raw)
2026 crc32 = zip->entry_crc32;
2027
2028 mz_zip_print("Zip - Entry - Write Close (crc 0x%08"PRIx32" cs %"PRId64" ucs %"PRId64" )\n",
2029 crc32, compressed_size, uncompressed_size);
2030
2031 /* If sizes are not set, then read them from the compression stream */
2032 if (compressed_size < 0)
2033 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, &compressed_size);
2034 if (uncompressed_size < 0)
2035 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN, &uncompressed_size);
2036
2037 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
2038 {
2039 mz_stream_set_base(zip->crypt_stream, zip->stream);
2040 err = mz_stream_close(zip->crypt_stream);
2041
2042 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, &compressed_size);
2043 }
2044
2045 if ((err == MZ_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR))
2046 {
2047 /* Determine if we need to write data descriptor in zip64 format,
2048 if local extrafield was saved with zip64 extrafield */
2049 if (zip->file_info.zip64 == MZ_ZIP64_AUTO)
2050 {
2051 if (zip->file_info.uncompressed_size >= UINT32_MAX)
2052 zip64 = 1;
2053 if (zip->file_info.compressed_size >= UINT32_MAX)
2054 zip64 = 1;
2055 if (zip->file_info.disk_offset >= UINT32_MAX)
2056 zip64 = 1;
2057 else if (zip->file_info.uncompressed_size == 0)
2058 zip64 = 1;
2059 }
2060 else if (zip->file_info.zip64 == MZ_ZIP64_FORCE)
2061 {
2062 zip64 = 1;
2063 }
2064
2065 if (zip->file_info.flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO)
2066 err = mz_zip_entry_write_descriptor(zip->stream,
2067 zip64, 0, compressed_size, 0);
2068 else
2069 err = mz_zip_entry_write_descriptor(zip->stream,
2070 zip64, crc32, compressed_size, uncompressed_size);
2071 }
2072
2073 /* Write file info to central directory */
2074
2075 mz_zip_print("Zip - Entry - Write cd (ucs %"PRId64" cs %"PRId64" crc 0x%08"PRIx32")\n",
2076 uncompressed_size, compressed_size, crc32);
2077
2078 zip->file_info.crc = crc32;
2079 zip->file_info.compressed_size = compressed_size;
2080 zip->file_info.uncompressed_size = uncompressed_size;
2081
2082 if (err == MZ_OK)
2083 err = mz_zip_entry_write_header(zip->cd_mem_stream, 0, &zip->file_info);
2084
2085 zip->number_entry += 1;
2086
2087 mz_zip_entry_close_int(handle);
2088
2089 return err;
2090 }
2091
mz_zip_entry_is_dir(void * handle)2092 int32_t mz_zip_entry_is_dir(void *handle)
2093 {
2094 mz_zip *zip = (mz_zip *)handle;
2095 int32_t filename_length = 0;
2096
2097 if (zip == NULL)
2098 return MZ_PARAM_ERROR;
2099 if (zip->entry_scanned == 0)
2100 return MZ_PARAM_ERROR;
2101 if (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK)
2102 return MZ_OK;
2103
2104 filename_length = (int32_t)strlen(zip->file_info.filename);
2105 if (filename_length > 0)
2106 {
2107 if ((zip->file_info.filename[filename_length - 1] == '/') ||
2108 (zip->file_info.filename[filename_length - 1] == '\\'))
2109 return MZ_OK;
2110 }
2111 return MZ_EXIST_ERROR;
2112 }
2113
mz_zip_entry_get_info(void * handle,mz_zip_file ** file_info)2114 int32_t mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
2115 {
2116 mz_zip *zip = (mz_zip *)handle;
2117
2118 if (zip == NULL)
2119 return MZ_PARAM_ERROR;
2120
2121 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
2122 {
2123 if (!zip->entry_scanned)
2124 return MZ_PARAM_ERROR;
2125 }
2126
2127 *file_info = &zip->file_info;
2128 return MZ_OK;
2129 }
2130
mz_zip_entry_get_local_info(void * handle,mz_zip_file ** local_file_info)2131 int32_t mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
2132 {
2133 mz_zip *zip = (mz_zip *)handle;
2134 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
2135 return MZ_PARAM_ERROR;
2136 *local_file_info = &zip->local_file_info;
2137 return MZ_OK;
2138 }
2139
mz_zip_entry_set_extrafield(void * handle,const uint8_t * extrafield,uint16_t extrafield_size)2140 int32_t mz_zip_entry_set_extrafield(void *handle, const uint8_t *extrafield, uint16_t extrafield_size)
2141 {
2142 mz_zip *zip = (mz_zip *)handle;
2143
2144 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
2145 return MZ_PARAM_ERROR;
2146
2147 zip->file_info.extrafield = extrafield;
2148 zip->file_info.extrafield_size = extrafield_size;
2149 return MZ_OK;
2150 }
2151
mz_zip_entry_close(void * handle)2152 int32_t mz_zip_entry_close(void *handle)
2153 {
2154 return mz_zip_entry_close_raw(handle, UINT64_MAX, 0);
2155 }
2156
mz_zip_entry_close_raw(void * handle,int64_t uncompressed_size,uint32_t crc32)2157 int32_t mz_zip_entry_close_raw(void *handle, int64_t uncompressed_size, uint32_t crc32)
2158 {
2159 mz_zip *zip = (mz_zip *)handle;
2160 int32_t err = MZ_OK;
2161
2162 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
2163 return MZ_PARAM_ERROR;
2164
2165 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
2166 err = mz_zip_entry_write_close(handle, crc32, UINT64_MAX, uncompressed_size);
2167 else
2168 err = mz_zip_entry_read_close(handle, NULL, NULL, NULL);
2169
2170 return err;
2171 }
2172
mz_zip_goto_next_entry_int(void * handle)2173 static int32_t mz_zip_goto_next_entry_int(void *handle)
2174 {
2175 mz_zip *zip = (mz_zip *)handle;
2176 int32_t err = MZ_OK;
2177
2178 if (zip == NULL)
2179 return MZ_PARAM_ERROR;
2180
2181 zip->entry_scanned = 0;
2182
2183 mz_stream_set_prop_int64(zip->cd_stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
2184
2185 err = mz_stream_seek(zip->cd_stream, zip->cd_current_pos, MZ_SEEK_SET);
2186 if (err == MZ_OK)
2187 err = mz_zip_entry_read_header(zip->cd_stream, 0, &zip->file_info, zip->file_info_stream);
2188 if (err == MZ_OK)
2189 zip->entry_scanned = 1;
2190 return err;
2191 }
2192
mz_zip_set_number_entry(void * handle,uint64_t number_entry)2193 int32_t mz_zip_set_number_entry(void *handle, uint64_t number_entry)
2194 {
2195 mz_zip *zip = (mz_zip *)handle;
2196 if (zip == NULL)
2197 return MZ_PARAM_ERROR;
2198 zip->number_entry = number_entry;
2199 return MZ_OK;
2200 }
2201
mz_zip_get_number_entry(void * handle,uint64_t * number_entry)2202 int32_t mz_zip_get_number_entry(void *handle, uint64_t *number_entry)
2203 {
2204 mz_zip *zip = (mz_zip *)handle;
2205 if (zip == NULL || number_entry == NULL)
2206 return MZ_PARAM_ERROR;
2207 *number_entry = zip->number_entry;
2208 return MZ_OK;
2209 }
2210
mz_zip_set_disk_number_with_cd(void * handle,uint32_t disk_number_with_cd)2211 int32_t mz_zip_set_disk_number_with_cd(void *handle, uint32_t disk_number_with_cd)
2212 {
2213 mz_zip *zip = (mz_zip *)handle;
2214 if (zip == NULL)
2215 return MZ_PARAM_ERROR;
2216 zip->disk_number_with_cd = disk_number_with_cd;
2217 return MZ_OK;
2218 }
2219
mz_zip_get_disk_number_with_cd(void * handle,uint32_t * disk_number_with_cd)2220 int32_t mz_zip_get_disk_number_with_cd(void *handle, uint32_t *disk_number_with_cd)
2221 {
2222 mz_zip *zip = (mz_zip *)handle;
2223 if (zip == NULL || disk_number_with_cd == NULL)
2224 return MZ_PARAM_ERROR;
2225 *disk_number_with_cd = zip->disk_number_with_cd;
2226 return MZ_OK;
2227 }
2228
mz_zip_get_entry(void * handle)2229 int64_t mz_zip_get_entry(void *handle)
2230 {
2231 mz_zip *zip = (mz_zip *)handle;
2232
2233 if (zip == NULL)
2234 return MZ_PARAM_ERROR;
2235
2236 return zip->cd_current_pos;
2237 }
2238
mz_zip_goto_entry(void * handle,int64_t cd_pos)2239 int32_t mz_zip_goto_entry(void *handle, int64_t cd_pos)
2240 {
2241 mz_zip *zip = (mz_zip *)handle;
2242
2243 if (zip == NULL)
2244 return MZ_PARAM_ERROR;
2245
2246 if (cd_pos < zip->cd_start_pos || cd_pos > zip->cd_start_pos + zip->cd_size)
2247 return MZ_PARAM_ERROR;
2248
2249 zip->cd_current_pos = cd_pos;
2250
2251 return mz_zip_goto_next_entry_int(handle);
2252 }
2253
mz_zip_goto_first_entry(void * handle)2254 int32_t mz_zip_goto_first_entry(void *handle)
2255 {
2256 mz_zip *zip = (mz_zip *)handle;
2257
2258 if (zip == NULL)
2259 return MZ_PARAM_ERROR;
2260
2261 zip->cd_current_pos = zip->cd_start_pos;
2262
2263 return mz_zip_goto_next_entry_int(handle);
2264 }
2265
mz_zip_goto_next_entry(void * handle)2266 int32_t mz_zip_goto_next_entry(void *handle)
2267 {
2268 mz_zip *zip = (mz_zip *)handle;
2269
2270 if (zip == NULL)
2271 return MZ_PARAM_ERROR;
2272
2273 zip->cd_current_pos += (int64_t)MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
2274 zip->file_info.extrafield_size + zip->file_info.comment_size;
2275
2276 return mz_zip_goto_next_entry_int(handle);
2277 }
2278
mz_zip_locate_entry(void * handle,const char * filename,uint8_t ignore_case)2279 int32_t mz_zip_locate_entry(void *handle, const char *filename, uint8_t ignore_case)
2280 {
2281 mz_zip *zip = (mz_zip *)handle;
2282 int32_t err = MZ_OK;
2283 int32_t result = 0;
2284
2285 if (zip == NULL || filename == NULL)
2286 return MZ_PARAM_ERROR;
2287
2288 /* If we are already on the current entry, no need to search */
2289 if ((zip->entry_scanned) && (zip->file_info.filename != NULL))
2290 {
2291 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
2292 if (result == 0)
2293 return MZ_OK;
2294 }
2295
2296 /* Search all entries starting at the first */
2297 err = mz_zip_goto_first_entry(handle);
2298 while (err == MZ_OK)
2299 {
2300 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
2301 if (result == 0)
2302 return MZ_OK;
2303
2304 err = mz_zip_goto_next_entry(handle);
2305 }
2306
2307 return err;
2308 }
2309
mz_zip_locate_first_entry(void * handle,void * userdata,mz_zip_locate_entry_cb cb)2310 int32_t mz_zip_locate_first_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
2311 {
2312 mz_zip *zip = (mz_zip *)handle;
2313 int32_t err = MZ_OK;
2314 int32_t result = 0;
2315
2316 /* Search first entry looking for match */
2317 err = mz_zip_goto_first_entry(handle);
2318 if (err != MZ_OK)
2319 return err;
2320
2321 result = cb(handle, userdata, &zip->file_info);
2322 if (result == 0)
2323 return MZ_OK;
2324
2325 return mz_zip_locate_next_entry(handle, userdata, cb);
2326 }
2327
mz_zip_locate_next_entry(void * handle,void * userdata,mz_zip_locate_entry_cb cb)2328 int32_t mz_zip_locate_next_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
2329 {
2330 mz_zip *zip = (mz_zip *)handle;
2331 int32_t err = MZ_OK;
2332 int32_t result = 0;
2333
2334 /* Search next entries looking for match */
2335 err = mz_zip_goto_next_entry(handle);
2336 while (err == MZ_OK)
2337 {
2338 result = cb(handle, userdata, &zip->file_info);
2339 if (result == 0)
2340 return MZ_OK;
2341
2342 err = mz_zip_goto_next_entry(handle);
2343 }
2344
2345 return err;
2346 }
2347
2348 /***************************************************************************/
2349
mz_zip_attrib_is_dir(uint32_t attrib,int32_t version_madeby)2350 int32_t mz_zip_attrib_is_dir(uint32_t attrib, int32_t version_madeby)
2351 {
2352 uint32_t posix_attrib = 0;
2353 uint8_t system = MZ_HOST_SYSTEM(version_madeby);
2354 int32_t err = MZ_OK;
2355
2356 err = mz_zip_attrib_convert(system, attrib, MZ_HOST_SYSTEM_UNIX, &posix_attrib);
2357 if (err == MZ_OK)
2358 {
2359 if ((posix_attrib & 0170000) == 0040000) /* S_ISDIR */
2360 return MZ_OK;
2361 }
2362
2363 return MZ_EXIST_ERROR;
2364 }
2365
mz_zip_attrib_convert(uint8_t src_sys,uint32_t src_attrib,uint8_t target_sys,uint32_t * target_attrib)2366 int32_t mz_zip_attrib_convert(uint8_t src_sys, uint32_t src_attrib, uint8_t target_sys, uint32_t *target_attrib)
2367 {
2368 if (target_attrib == NULL)
2369 return MZ_PARAM_ERROR;
2370
2371 *target_attrib = 0;
2372
2373 if ((src_sys == MZ_HOST_SYSTEM_MSDOS) || (src_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
2374 {
2375 if ((target_sys == MZ_HOST_SYSTEM_MSDOS) || (target_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
2376 {
2377 *target_attrib = src_attrib;
2378 return MZ_OK;
2379 }
2380 if ((target_sys == MZ_HOST_SYSTEM_UNIX) || (target_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
2381 return mz_zip_attrib_win32_to_posix(src_attrib, target_attrib);
2382 }
2383 else if ((src_sys == MZ_HOST_SYSTEM_UNIX) || (src_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
2384 {
2385 if ((target_sys == MZ_HOST_SYSTEM_UNIX) || (target_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
2386 {
2387 /* If high bytes are set, it contains unix specific attributes */
2388 if ((src_attrib >> 16) != 0)
2389 src_attrib >>= 16;
2390
2391 *target_attrib = src_attrib;
2392 return MZ_OK;
2393 }
2394 if ((target_sys == MZ_HOST_SYSTEM_MSDOS) || (target_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
2395 return mz_zip_attrib_posix_to_win32(src_attrib, target_attrib);
2396 }
2397
2398 return MZ_SUPPORT_ERROR;
2399 }
2400
mz_zip_attrib_posix_to_win32(uint32_t posix_attrib,uint32_t * win32_attrib)2401 int32_t mz_zip_attrib_posix_to_win32(uint32_t posix_attrib, uint32_t *win32_attrib)
2402 {
2403 if (win32_attrib == NULL)
2404 return MZ_PARAM_ERROR;
2405
2406 *win32_attrib = 0;
2407
2408 /* S_IWUSR | S_IWGRP | S_IWOTH | S_IXUSR | S_IXGRP | S_IXOTH */
2409 if ((posix_attrib & 0000333) == 0 && (posix_attrib & 0000444) != 0)
2410 *win32_attrib |= 0x01; /* FILE_ATTRIBUTE_READONLY */
2411 /* S_IFDIR */
2412 if ((posix_attrib & 0170000) == 0040000)
2413 *win32_attrib |= 0x10; /* FILE_ATTRIBUTE_DIRECTORY */
2414 /* S_IFREG */
2415 else
2416 *win32_attrib |= 0x80; /* FILE_ATTRIBUTE_NORMAL */
2417
2418 return MZ_OK;
2419 }
2420
mz_zip_attrib_win32_to_posix(uint32_t win32_attrib,uint32_t * posix_attrib)2421 int32_t mz_zip_attrib_win32_to_posix(uint32_t win32_attrib, uint32_t *posix_attrib)
2422 {
2423 if (posix_attrib == NULL)
2424 return MZ_PARAM_ERROR;
2425
2426 *posix_attrib = 0000444; /* S_IRUSR | S_IRGRP | S_IROTH */
2427 /* FILE_ATTRIBUTE_READONLY */
2428 if ((win32_attrib & 0x01) == 0)
2429 *posix_attrib |= 0000222; /* S_IWUSR | S_IWGRP | S_IWOTH */
2430 /* FILE_ATTRIBUTE_DIRECTORY */
2431 if ((win32_attrib & 0x10) == 0x10)
2432 *posix_attrib |= 0040111; /* S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH */
2433 else
2434 *posix_attrib |= 0100000; /* S_IFREG */
2435
2436 return MZ_OK;
2437 }
2438
2439 /***************************************************************************/
2440
mz_zip_extrafield_find(void * stream,uint16_t type,uint16_t * length)2441 int32_t mz_zip_extrafield_find(void *stream, uint16_t type, uint16_t *length)
2442 {
2443 int32_t err = MZ_OK;
2444 uint16_t field_type = 0;
2445 uint16_t field_length = 0;
2446
2447 do
2448 {
2449 err = mz_stream_read_uint16(stream, &field_type);
2450 if (err == MZ_OK)
2451 err = mz_stream_read_uint16(stream, &field_length);
2452 if (err != MZ_OK)
2453 break;
2454
2455 if (type == field_type)
2456 {
2457 if (length != NULL)
2458 *length = field_length;
2459 return MZ_OK;
2460 }
2461
2462 err = mz_stream_seek(stream, field_length, MZ_SEEK_CUR);
2463 }
2464 while (err == MZ_OK);
2465
2466 return MZ_EXIST_ERROR;
2467 }
2468
mz_zip_extrafield_contains(const uint8_t * extrafield,int32_t extrafield_size,uint16_t type,uint16_t * length)2469 int32_t mz_zip_extrafield_contains(const uint8_t *extrafield, int32_t extrafield_size,
2470 uint16_t type, uint16_t *length)
2471 {
2472 void *file_extra_stream = NULL;
2473 int32_t err = MZ_OK;
2474
2475 if (extrafield == NULL || extrafield_size == 0)
2476 return MZ_PARAM_ERROR;
2477
2478 mz_stream_mem_create(&file_extra_stream);
2479 mz_stream_mem_set_buffer(file_extra_stream, (void *)extrafield, extrafield_size);
2480
2481 err = mz_zip_extrafield_find(file_extra_stream, type, length);
2482
2483 mz_stream_mem_delete(&file_extra_stream);
2484
2485 return err;
2486 }
2487
mz_zip_extrafield_read(void * stream,uint16_t * type,uint16_t * length)2488 int32_t mz_zip_extrafield_read(void *stream, uint16_t *type, uint16_t *length)
2489 {
2490 int32_t err = MZ_OK;
2491 if (type == NULL || length == NULL)
2492 return MZ_PARAM_ERROR;
2493 err = mz_stream_read_uint16(stream, type);
2494 if (err == MZ_OK)
2495 err = mz_stream_read_uint16(stream, length);
2496 return err;
2497 }
2498
mz_zip_extrafield_write(void * stream,uint16_t type,uint16_t length)2499 int32_t mz_zip_extrafield_write(void *stream, uint16_t type, uint16_t length)
2500 {
2501 int32_t err = MZ_OK;
2502 err = mz_stream_write_uint16(stream, type);
2503 if (err == MZ_OK)
2504 err = mz_stream_write_uint16(stream, length);
2505 return err;
2506 }
2507
2508 /***************************************************************************/
2509
mz_zip_invalid_date(const struct tm * ptm)2510 static int32_t mz_zip_invalid_date(const struct tm *ptm)
2511 {
2512 #define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
2513 return (!datevalue_in_range(0, 127 + 80, ptm->tm_year) || /* 1980-based year, allow 80 extra */
2514 !datevalue_in_range(0, 11, ptm->tm_mon) ||
2515 !datevalue_in_range(1, 31, ptm->tm_mday) ||
2516 !datevalue_in_range(0, 23, ptm->tm_hour) ||
2517 !datevalue_in_range(0, 59, ptm->tm_min) ||
2518 !datevalue_in_range(0, 59, ptm->tm_sec));
2519 #undef datevalue_in_range
2520 }
2521
mz_zip_dosdate_to_raw_tm(uint64_t dos_date,struct tm * ptm)2522 static void mz_zip_dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
2523 {
2524 uint64_t date = (uint64_t)(dos_date >> 16);
2525
2526 ptm->tm_mday = (uint16_t)(date & 0x1f);
2527 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
2528 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
2529 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
2530 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
2531 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
2532 ptm->tm_isdst = -1;
2533 }
2534
mz_zip_dosdate_to_tm(uint64_t dos_date,struct tm * ptm)2535 int32_t mz_zip_dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
2536 {
2537 if (ptm == NULL)
2538 return MZ_PARAM_ERROR;
2539
2540 mz_zip_dosdate_to_raw_tm(dos_date, ptm);
2541
2542 if (mz_zip_invalid_date(ptm))
2543 {
2544 /* Invalid date stored, so don't return it */
2545 memset(ptm, 0, sizeof(struct tm));
2546 return MZ_FORMAT_ERROR;
2547 }
2548 return MZ_OK;
2549 }
2550
mz_zip_dosdate_to_time_t(uint64_t dos_date)2551 time_t mz_zip_dosdate_to_time_t(uint64_t dos_date)
2552 {
2553 struct tm ptm;
2554 mz_zip_dosdate_to_raw_tm(dos_date, &ptm);
2555 return mktime(&ptm);
2556 }
2557
mz_zip_time_t_to_tm(time_t unix_time,struct tm * ptm)2558 int32_t mz_zip_time_t_to_tm(time_t unix_time, struct tm *ptm)
2559 {
2560 struct tm *ltm = NULL;
2561 if (ptm == NULL)
2562 return MZ_PARAM_ERROR;
2563 ltm = localtime(&unix_time); /* Returns a 1900-based year */
2564 if (ltm == NULL)
2565 {
2566 /* Invalid date stored, so don't return it */
2567 memset(ptm, 0, sizeof(struct tm));
2568 return MZ_INTERNAL_ERROR;
2569 }
2570 memcpy(ptm, ltm, sizeof(struct tm));
2571 return MZ_OK;
2572 }
2573
mz_zip_time_t_to_dos_date(time_t unix_time)2574 uint32_t mz_zip_time_t_to_dos_date(time_t unix_time)
2575 {
2576 struct tm ptm;
2577 mz_zip_time_t_to_tm(unix_time, &ptm);
2578 return mz_zip_tm_to_dosdate((const struct tm *)&ptm);
2579 }
2580
mz_zip_tm_to_dosdate(const struct tm * ptm)2581 uint32_t mz_zip_tm_to_dosdate(const struct tm *ptm)
2582 {
2583 struct tm fixed_tm;
2584
2585 /* Years supported: */
2586
2587 /* [00, 79] (assumed to be between 2000 and 2079) */
2588 /* [80, 207] (assumed to be between 1980 and 2107, typical output of old */
2589 /* software that does 'year-1900' to get a double digit year) */
2590 /* [1980, 2107] (due to format limitations, only years 1980-2107 can be stored.) */
2591
2592 memcpy(&fixed_tm, ptm, sizeof(struct tm));
2593 if (fixed_tm.tm_year >= 1980) /* range [1980, 2107] */
2594 fixed_tm.tm_year -= 1980;
2595 else if (fixed_tm.tm_year >= 80) /* range [80, 207] */
2596 fixed_tm.tm_year -= 80;
2597 else /* range [00, 79] */
2598 fixed_tm.tm_year += 20;
2599
2600 if (mz_zip_invalid_date(&fixed_tm))
2601 return 0;
2602
2603 return (((uint32_t)fixed_tm.tm_mday + (32 * ((uint32_t)fixed_tm.tm_mon + 1)) + (512 * (uint32_t)fixed_tm.tm_year)) << 16) |
2604 (((uint32_t)fixed_tm.tm_sec / 2) + (32 * (uint32_t)fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
2605 }
2606
mz_zip_ntfs_to_unix_time(uint64_t ntfs_time,time_t * unix_time)2607 int32_t mz_zip_ntfs_to_unix_time(uint64_t ntfs_time, time_t *unix_time)
2608 {
2609 *unix_time = (time_t)((ntfs_time - 116444736000000000LL) / 10000000);
2610 return MZ_OK;
2611 }
2612
mz_zip_unix_to_ntfs_time(time_t unix_time,uint64_t * ntfs_time)2613 int32_t mz_zip_unix_to_ntfs_time(time_t unix_time, uint64_t *ntfs_time)
2614 {
2615 *ntfs_time = ((uint64_t)unix_time * 10000000) + 116444736000000000LL;
2616 return MZ_OK;
2617 }
2618
2619 /***************************************************************************/
2620
mz_zip_path_compare(const char * path1,const char * path2,uint8_t ignore_case)2621 int32_t mz_zip_path_compare(const char *path1, const char *path2, uint8_t ignore_case)
2622 {
2623 do
2624 {
2625 if ((*path1 == '\\' && *path2 == '/') ||
2626 (*path2 == '\\' && *path1 == '/'))
2627 {
2628 /* Ignore comparison of path slashes */
2629 }
2630 else if (ignore_case)
2631 {
2632 if (tolower(*path1) != tolower(*path2))
2633 break;
2634 }
2635 else if (*path1 != *path2)
2636 {
2637 break;
2638 }
2639
2640 path1 += 1;
2641 path2 += 1;
2642 }
2643 while (*path1 != 0 && *path2 != 0);
2644
2645 if (ignore_case)
2646 return (int32_t)(tolower(*path1) - tolower(*path2));
2647
2648 return (int32_t)(*path1 - *path2);
2649 }
2650
2651 /***************************************************************************/
2652