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