blob: 09089623e7979bc2582ef5ff4dc7330e090b2392 [file] [log] [blame]
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08001/* zip.c -- Zip manipulation
Nathan Moinvazirib2b082c2018-11-13 15:22:15 -08002 Version 2.7.5, November 13, 2018
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08003 part of the MiniZip project
4
Nathan Moinvazirifee885a2018-01-06 08:49:03 -08005 Copyright (C) 2010-2018 Nathan Moinvaziri
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08006 https://github.com/nmoinvaz/minizip
Nathan Moinvaziri79cfab02018-08-17 12:21:06 -07007 Copyright (C) 2009-2010 Mathias Svensson
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08008 Modifications for Zip64 support
9 http://result42.com
Nathan Moinvaziri79cfab02018-08-17 12:21:06 -070010 Copyright (C) 2007-2008 Even Rouault
11 Modifications of Unzip for Zip64
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080012 Copyright (C) 1998-2010 Gilles Vollant
Viktor Szakats9dea6f22018-10-17 22:39:01 +000013 https://www.winimage.com/zLibDll/minizip.html
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080014
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
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080019#include <stdlib.h>
20#include <stdint.h>
Nathan Moinvaziri85d36c52018-08-31 16:43:41 -070021#include <stdio.h>
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080022#include <string.h>
Nathan Moinvaziri264dc182018-11-13 17:42:13 -080023#include <inttypes.h>
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -070024#include <ctype.h>
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -070025#include <time.h>
Nathan Moinvaziri34eff622018-01-22 09:25:15 -080026#include <limits.h>
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080027
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070028#include "mz.h"
Nathan Moinvaziri5f091882018-10-24 18:06:08 -070029#include "mz_crypt.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080030#include "mz_strm.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080031#ifdef HAVE_BZIP2
32# include "mz_strm_bzip.h"
33#endif
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080034#ifdef HAVE_LZMA
35# include "mz_strm_lzma.h"
36#endif
Nathan Moinvazirib47b41b2018-07-11 09:44:29 -070037#include "mz_strm_mem.h"
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -070038#ifdef HAVE_PKCRYPT
39# include "mz_strm_pkcrypt.h"
40#endif
Nathan Moinvaziri21a31022018-10-24 09:50:16 -070041#ifdef HAVE_AES
42# include "mz_strm_wzaes.h"
43#endif
Nathan Moinvaziri4f6a0e32017-11-10 08:45:14 -080044#ifdef HAVE_ZLIB
45# include "mz_strm_zlib.h"
46#endif
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080047
48#include "mz_zip.h"
49
Nathan Moinvaziric565fa82018-10-19 08:48:33 -070050#if defined(_MSC_VER) && _MSC_VER < 1900
51# define snprintf _snprintf
52#endif
53
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080054/***************************************************************************/
55
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070056#define MZ_ZIP_MAGIC_LOCALHEADER (0x04034b50)
57#define MZ_ZIP_MAGIC_CENTRALHEADER (0x02014b50)
58#define MZ_ZIP_MAGIC_ENDHEADER (0x06054b50)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -080059#define MZ_ZIP_MAGIC_ENDHEADERU8 { 0x50, 0x4b, 0x05, 0x06 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070060#define MZ_ZIP_MAGIC_ENDHEADER64 (0x06064b50)
61#define MZ_ZIP_MAGIC_ENDLOCHEADER64 (0x07064b50)
62#define MZ_ZIP_MAGIC_DATADESCRIPTOR (0x08074b50)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -080063#define MZ_ZIP_MAGIC_DATADESCRIPTORU8 { 0x50, 0x4b, 0x07, 0x08 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070064
Nathan Moinvaziri638f31f2018-08-27 08:17:16 -070065#define MZ_ZIP_SIZE_LD_ITEM (32)
66#define MZ_ZIP_SIZE_CD_ITEM (46)
67#define MZ_ZIP_SIZE_CD_LOCATOR64 (20)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080068
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080069/***************************************************************************/
70
71typedef struct mz_zip_s
72{
73 mz_zip_file file_info;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070074 mz_zip_file local_file_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080075
76 void *stream; // main stream
Nathan Moinvaziricda36002017-10-21 09:37:18 -070077 void *cd_stream; // pointer to the stream with the cd
78 void *cd_mem_stream; // memory stream for central directory
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080079 void *compress_stream; // compression stream
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080080 void *crypt_stream; // encryption stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070081 void *file_info_stream; // memory stream for storing file info
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -070082 void *local_file_info_stream; // memory stream for storing local file info
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080083
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070084 int32_t open_mode;
Nathan Moinvaziridd103f42018-11-13 14:50:32 -080085 uint8_t recover;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070086
Nathan Moinvazirifd039e32017-10-22 14:40:39 -070087 uint32_t disk_number_with_cd; // number of the disk with the central dir
Nathan Moinvaziri413822a2018-10-20 09:45:07 -070088 int64_t disk_offset_shift; // correction for zips that have wrong offset start of cd
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070089
Nathan Moinvaziri413822a2018-10-20 09:45:07 -070090 int64_t cd_start_pos; // pos of the first file in the central dir stream
91 int64_t cd_current_pos; // pos of the current file in the central dir
92 int64_t cd_offset; // offset of start of central directory
93 int64_t cd_size; // size of the central directory
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070094
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -070095 uint8_t entry_scanned; // entry header information read ok
96 uint8_t entry_opened; // entry is open for read/write
97 uint8_t entry_raw; // entry opened with raw mode
Nathan Moinvazirie63d2312018-11-03 19:45:41 -070098 uint32_t entry_crc32; // entry crc32
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070099
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700100 uint64_t number_entry;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700101
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700102 uint16_t version_madeby;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700103 char *comment;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800104} mz_zip;
105
106/***************************************************************************/
107
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800108#if 0
109# define mz_zip_print printf
110#else
111# define mz_zip_print(fmt,...)
112#endif
113
114/***************************************************************************/
115
Nathan Moinvaziri286a8172018-11-08 14:56:14 -0800116// Locate the end of central directory
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700117static int32_t mz_zip_search_eocd(void *stream, int64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800118{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700119 int64_t file_size = 0;
Nathan Moinvaziri286a8172018-11-08 14:56:14 -0800120 int64_t max_back = 0;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800121 uint8_t find[4] = MZ_ZIP_MAGIC_ENDHEADERU8;
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700122 int32_t err = MZ_OK;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700123
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700124 err = mz_stream_seek(stream, 0, MZ_SEEK_END);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800125
126 file_size = mz_stream_tell(stream);
Nathan Moinvaziri286a8172018-11-08 14:56:14 -0800127
128 // maximum seek is size of global comment + extra
129 max_back = UINT16_MAX + 128;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800130 if (max_back > file_size)
131 max_back = file_size;
132
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800133 return mz_stream_find_reverse(stream, (const void *)find, sizeof(find), max_back, central_pos);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800134}
135
Nathan Moinvazirie824da82018-07-26 17:53:15 -0700136// Locate the end of central directory 64 of a zip file
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700137static int32_t mz_zip_search_zip64_eocd(void *stream, const int64_t end_central_offset, int64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800138{
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700139 int64_t offset = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800140 uint32_t value32 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700141 int32_t err = MZ_OK;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700142
143
144 *central_pos = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800145
146 // Zip64 end of central directory locator
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700147 err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800148 // Read locator signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700149 if (err == MZ_OK)
150 {
151 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700152 if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700153 err = MZ_FORMAT_ERROR;
154 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800155 // Number of the disk with the start of the zip64 end of central directory
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700156 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)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700160 err = mz_stream_read_uint64(stream, (uint64_t *)&offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800161 // Total number of disks
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700162 if (err == MZ_OK)
163 err = mz_stream_read_uint32(stream, &value32);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800164 // Goto end of central directory record
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700165 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700166 err = mz_stream_seek(stream, (int64_t)offset, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800167 // The signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700168 if (err == MZ_OK)
169 {
170 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700171 if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700172 err = MZ_FORMAT_ERROR;
173 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800174
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700175 if (err == MZ_OK)
176 *central_pos = offset;
177
178 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800179}
180
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700181// Get info about the current file in the zip file
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700182static int32_t mz_zip_entry_read_header(void *stream, uint8_t local, mz_zip_file *file_info, void *file_extra_stream)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700183{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700184 uint64_t ntfs_time = 0;
185 uint32_t reserved = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700186 uint32_t magic = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700187 uint32_t dos_date = 0;
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800188 uint32_t field_pos = 0;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700189 uint16_t field_type = 0;
190 uint16_t field_length = 0;
191 uint32_t field_length_read = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700192 uint16_t ntfs_attrib_id = 0;
193 uint16_t ntfs_attrib_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700194 uint16_t value16 = 0;
195 uint32_t value32 = 0;
Nathan Moinvazirifeb86c52018-11-05 11:40:21 -0800196 int64_t extrafield_pos = -1;
Nathan Moinvazirifeb86c52018-11-05 11:40:21 -0800197 int64_t comment_pos = -1;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700198 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700199
200
201 memset(file_info, 0, sizeof(mz_zip_file));
202
203 // Check the magic
204 err = mz_stream_read_uint32(stream, &magic);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700205 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)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700208 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;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000213
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700214 // 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)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700226 {
227 err = mz_stream_read_uint32(stream, &dos_date);
228 file_info->modified_date = mz_zip_dosdate_to_time_t(dos_date);
229 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700230 if (err == MZ_OK)
231 err = mz_stream_read_uint32(stream, &file_info->crc);
232 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700233 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700234 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700235 file_info->compressed_size = value32;
236 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700237 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700238 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700239 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700240 file_info->uncompressed_size = value32;
241 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700242 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)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700251 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700252 err = mz_stream_read_uint16(stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700253 file_info->disk_number = value16;
254 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700255 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)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700260 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700261 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700262 file_info->disk_offset = value32;
263 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700264 }
265 }
266
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700267 if (err == MZ_OK)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800268 err = mz_stream_seek(file_extra_stream, 0, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700269
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800270 // Copy variable length data to memory stream for later retrieval
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700271 if ((err == MZ_OK) && (file_info->filename_size > 0))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700272 err = mz_stream_copy(file_extra_stream, stream, file_info->filename_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800273 mz_stream_write_uint8(file_extra_stream, 0);
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800274 extrafield_pos = (int64_t)file_info->filename_size + 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700275
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800276 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
277 err = mz_stream_copy(file_extra_stream, stream, file_info->extrafield_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800278 mz_stream_write_uint8(file_extra_stream, 0);
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800279
280 comment_pos = extrafield_pos + (int64_t)file_info->extrafield_size + 1;
281 if ((err == MZ_OK) && (file_info->comment_size > 0))
282 err = mz_stream_copy(file_extra_stream, stream, file_info->comment_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800283 mz_stream_write_uint8(file_extra_stream, 0);
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800284
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700285 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
286 {
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800287 // Seek to and parse the extra field
288 err = mz_stream_seek(file_extra_stream, extrafield_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700289
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800290 while ((err == MZ_OK) && (field_pos + 4 <= file_info->extrafield_size))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700291 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700292 err = mz_zip_extrafield_read(file_extra_stream, &field_type, &field_length);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800293 field_pos += 4;
Nathan Moinvazirifeb86c52018-11-05 11:40:21 -0800294
295 // Don't allow field length to exceed size of remaining extrafield
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800296 if (field_length > (file_info->extrafield_size - field_pos))
Nathan Moinvaziri1d6d1832018-11-10 09:03:55 -0800297 field_length = (uint16_t)(file_info->extrafield_size - field_pos);
Nathan Moinvazirifeb86c52018-11-05 11:40:21 -0800298
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700299 // Read ZIP64 extra field
Nathan Moinvaziri761d28e2018-11-07 06:47:15 -0800300 if ((field_type == MZ_ZIP_EXTENSION_ZIP64) && (field_length >= 8))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700301 {
302 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700303 err = mz_stream_read_int64(file_extra_stream, &file_info->uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700304 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700305 err = mz_stream_read_int64(file_extra_stream, &file_info->compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700306 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700307 err = mz_stream_read_int64(file_extra_stream, &file_info->disk_offset);
juanii4ae79922018-02-11 14:29:36 -0300308 if ((err == MZ_OK) && (file_info->disk_number == UINT16_MAX))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700309 err = mz_stream_read_uint32(file_extra_stream, &file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700310 }
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700311 // Read NTFS extra field
Nathan Moinvaziri761d28e2018-11-07 06:47:15 -0800312 else if ((field_type == MZ_ZIP_EXTENSION_NTFS) && (field_length > 4))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700313 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700314 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700315 err = mz_stream_read_uint32(file_extra_stream, &reserved);
316 field_length_read = 4;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700317
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800318 while ((err == MZ_OK) && (field_length_read + 4 <= field_length))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700319 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700320 err = mz_stream_read_uint16(file_extra_stream, &ntfs_attrib_id);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700321 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700322 err = mz_stream_read_uint16(file_extra_stream, &ntfs_attrib_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800323 field_length_read += 4;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700324
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700325 if ((err == MZ_OK) && (ntfs_attrib_id == 0x01) && (ntfs_attrib_size == 24))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700326 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700327 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700328 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->modified_date);
329
juanii7063b0e2018-02-11 13:56:21 -0300330 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700331 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700332 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700333 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->accessed_date);
334 }
juanii7063b0e2018-02-11 13:56:21 -0300335 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700336 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700337 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700338 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->creation_date);
339 }
340 }
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800341 else if ((err == MZ_OK) && (field_length_read + ntfs_attrib_size <= field_length))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700342 {
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800343 err = mz_stream_seek(file_extra_stream, ntfs_attrib_size, MZ_SEEK_CUR);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700344 }
345
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800346 field_length_read += ntfs_attrib_size;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700347 }
348 }
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700349 // Read UNIX1 extra field
Nathan Moinvaziri761d28e2018-11-07 06:47:15 -0800350 else if ((field_type == MZ_ZIP_EXTENSION_UNIX1) && (field_length >= 12))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700351 {
352 if (err == MZ_OK && file_info->accessed_date == 0)
353 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700354 err = mz_stream_read_uint32(file_extra_stream, &value32);
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700355 if (err == MZ_OK)
356 file_info->accessed_date = value32;
357 }
358 if (err == MZ_OK && file_info->modified_date == 0)
359 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700360 err = mz_stream_read_uint32(file_extra_stream, &value32);
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700361 if (err == MZ_OK)
362 file_info->modified_date = value32;
363 }
364 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700365 err = mz_stream_read_uint16(file_extra_stream, &value16); // User id
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700366 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700367 err = mz_stream_read_uint16(file_extra_stream, &value16); // Group id
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700368
369 // Skip variable data
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800370 mz_stream_seek(file_extra_stream, field_length - 12, MZ_SEEK_CUR);
Nathan Moinvaziri413822a2018-10-20 09:45:07 -0700371 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700372#ifdef HAVE_AES
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700373 // Read AES extra field
Nathan Moinvaziri761d28e2018-11-07 06:47:15 -0800374 else if ((field_type == MZ_ZIP_EXTENSION_AES) && (field_length == 7))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700375 {
376 uint8_t value8 = 0;
377 // Verify version info
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700378 err = mz_stream_read_uint16(file_extra_stream, &value16);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700379 // Support AE-1 and AE-2
380 if (value16 != 1 && value16 != 2)
381 err = MZ_FORMAT_ERROR;
382 file_info->aes_version = value16;
383 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700384 err = mz_stream_read_uint8(file_extra_stream, &value8);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700385 if ((char)value8 != 'A')
386 err = MZ_FORMAT_ERROR;
387 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700388 err = mz_stream_read_uint8(file_extra_stream, &value8);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700389 if ((char)value8 != 'E')
390 err = MZ_FORMAT_ERROR;
391 // Get AES encryption strength and actual compression method
392 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700393 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700394 err = mz_stream_read_uint8(file_extra_stream, &value8);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700395 file_info->aes_encryption_mode = value8;
396 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700397 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700398 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700399 err = mz_stream_read_uint16(file_extra_stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700400 file_info->compression_method = value16;
401 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700402 }
403#endif
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800404 else if (field_length > 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700405 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700406 err = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700407 }
408
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800409 field_pos += field_length;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700410 }
411 }
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800412
413 // Get pointers to variable length data
414 mz_stream_mem_get_buffer(file_extra_stream, (const void **)&file_info->filename);
415 if (extrafield_pos >= 0)
416 mz_stream_mem_get_buffer_at(file_extra_stream, extrafield_pos, (const void **)&file_info->extrafield);
417 if (comment_pos >= 0)
418 mz_stream_mem_get_buffer_at(file_extra_stream, comment_pos, (const void **)&file_info->comment);
419
420 // Set to empty string just in-case
421 if (file_info->filename == NULL)
422 file_info->filename = "";
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800423 if (file_info->extrafield == NULL)
424 file_info->extrafield_size = 0;
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800425 if (file_info->comment == NULL)
426 file_info->comment = "";
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700427
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800428 if (err == MZ_OK)
429 {
430 mz_zip_print("Zip - Entry - Read header - %s (local %"PRId8")\n",
431 file_info->filename, local);
Nathan Moinvaziri264dc182018-11-13 17:42:13 -0800432 mz_zip_print("Zip - Entry - Read header compress (ucs %"PRId64" cs %"PRId64" crc 0x%08"PRIx32")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800433 file_info->uncompressed_size, file_info->compressed_size, file_info->crc);
434 if (!local)
Nathan Moinvaziri264dc182018-11-13 17:42:13 -0800435 mz_zip_print("Zip - Entry - Read header disk (disk %"PRId32" offset %"PRId64")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800436 file_info->disk_number, file_info->disk_offset);
Nathan Moinvaziri264dc182018-11-13 17:42:13 -0800437 mz_zip_print("Zip - Entry - Read header variable (fnl %"PRId32" efs %"PRId32" cms %"PRId32")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800438 file_info->filename_size, file_info->extrafield_size, file_info->comment_size);
439 }
440
441 return err;
442}
443
444static int32_t mz_zip_entry_read_descriptor(void *stream, uint8_t zip64, uint32_t *crc32, int64_t *compressed_size, int64_t *uncompressed_size)
445{
446 uint32_t value32 = 0;
447 int64_t value64 = 0;
448 int32_t err = MZ_OK;
449
450
451 err = mz_stream_read_uint32(stream, &value32);
452 if (value32 != MZ_ZIP_MAGIC_DATADESCRIPTOR)
453 err = MZ_FORMAT_ERROR;
454 if (err == MZ_OK)
455 err = mz_stream_read_uint32(stream, &value32);
456 if ((err == MZ_OK) && (crc32 != NULL))
457 *crc32 = value32;
458 if (err == MZ_OK)
459 {
460 // If zip 64 extension is enabled then read as 8 byte
461 if (!zip64)
462 {
463 err = mz_stream_read_uint32(stream, &value32);
464 value64 = value32;
465 }
466 else
467 err = mz_stream_read_int64(stream, &value64);
468 if ((err == MZ_OK) && (compressed_size != NULL))
469 *compressed_size = value64;
470 }
471 if (err == MZ_OK)
472 {
473 if (!zip64)
474 {
475 err = mz_stream_read_uint32(stream, &value32);
476 value64 = value32;
477 }
478 else
479 err = mz_stream_read_int64(stream, &value64);
480 if ((err == MZ_OK) && (uncompressed_size != NULL))
481 *uncompressed_size = value64;
482 }
483
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700484 return err;
485}
486
Nathan Moinvazirifa620e42018-10-20 10:37:10 -0700487static int32_t mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700488{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700489 uint64_t ntfs_time = 0;
490 uint32_t reserved = 0;
491 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700492 uint16_t extrafield_size = 0;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700493 uint16_t field_type = 0;
494 uint16_t field_length = 0;
495 uint16_t field_length_zip64 = 0;
496 uint16_t field_length_ntfs = 0;
497 uint16_t field_length_aes = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700498 uint16_t filename_size = 0;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700499 uint16_t filename_length = 0;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700500 uint16_t version_needed = 0;
Nathan Moinvaziridd103f42018-11-13 14:50:32 -0800501 int32_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700502 int32_t err = MZ_OK;
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700503 int32_t err_mem = MZ_OK;
504 uint8_t zip64 = 0;
505 uint8_t skip_aes = 0;
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700506 uint8_t mask = 0;
507 uint8_t write_end_slash = 0;
508 const char *filename = NULL;
Nathan Moinvazirifa620e42018-10-20 10:37:10 -0700509 char masked_name[64];
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700510 void *file_extra_stream = NULL;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700511
512 if (file_info == NULL)
513 return MZ_PARAM_ERROR;
514
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700515 if ((local) && (file_info->flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO))
516 mask = 1;
517
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700518 // Calculate extra field sizes
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700519 if (file_info->uncompressed_size >= UINT32_MAX)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700520 field_length_zip64 += 8;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700521 if (file_info->compressed_size >= UINT32_MAX)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700522 field_length_zip64 += 8;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700523 if (file_info->disk_offset >= UINT32_MAX)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700524 field_length_zip64 += 8;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700525
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700526 if (file_info->zip64 == MZ_ZIP64_AUTO)
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700527 {
528 // If uncompressed size is unknown, assume zip64 for 64-bit data descriptors
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700529 zip64 = (local && file_info->uncompressed_size == 0) || (field_length_zip64 > 0);
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700530 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700531 else if (file_info->zip64 == MZ_ZIP64_FORCE)
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700532 {
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700533 zip64 = 1;
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700534 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700535 else if (file_info->zip64 == MZ_ZIP64_DISABLE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700536 {
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700537 // Zip64 extension is required to zip file
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700538 if (field_length_zip64 > 0)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700539 return MZ_PARAM_ERROR;
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700540 }
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700541
542 if (zip64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700543 {
544 extrafield_size += 4;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700545 extrafield_size += field_length_zip64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700546 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700547
548 // Calculate extra field size and check for duplicates
549 if (file_info->extrafield_size > 0)
550 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700551 mz_stream_mem_create(&file_extra_stream);
Nathan Moinvaziri915c5132018-10-26 20:00:52 -0700552 mz_stream_mem_set_buffer(file_extra_stream, (void *)file_info->extrafield,
553 file_info->extrafield_size);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700554
555 do
556 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700557 err_mem = mz_stream_read_uint16(file_extra_stream, &field_type);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700558 if (err_mem == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700559 err_mem = mz_stream_read_uint16(file_extra_stream, &field_length);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700560 if (err_mem != MZ_OK)
561 break;
562
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700563 // Prefer incoming aes extensions over ours
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700564 if (field_type == MZ_ZIP_EXTENSION_AES)
565 skip_aes = 1;
566
567 // Prefer our zip64, ntfs extension over incoming
568 if (field_type != MZ_ZIP_EXTENSION_ZIP64 && field_type != MZ_ZIP_EXTENSION_NTFS)
569 extrafield_size += 4 + field_length;
570
571 if (err_mem == MZ_OK)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800572 err_mem = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700573 }
574 while (err_mem == MZ_OK);
575 }
576
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700577#ifdef HAVE_AES
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700578 if (!skip_aes)
579 {
580 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri413822a2018-10-20 09:45:07 -0700581 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700582 field_length_aes = 1 + 1 + 1 + 2 + 2;
583 extrafield_size += 4 + field_length_aes;
Nathan Moinvaziri413822a2018-10-20 09:45:07 -0700584 }
585 }
Nathan Moinvaziria2bb08c2018-10-28 13:45:30 -0700586#else
Nathan Moinvaziri4ae469a2018-10-28 15:32:04 -0700587 MZ_UNUSED(skip_aes);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700588#endif
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700589 // NTFS timestamps
Nathan Moinvazirid9e159a2018-02-13 15:30:30 -0800590 if ((file_info->modified_date != 0) &&
591 (file_info->accessed_date != 0) &&
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700592 (file_info->creation_date != 0) && (!mask))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700593 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700594 field_length_ntfs = 8 + 8 + 8 + 4 + 2 + 2;
595 extrafield_size += 4 + field_length_ntfs;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700596 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700597
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700598 if (local)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700599 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700600 else
601 {
602 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
603 if (err == MZ_OK)
604 err = mz_stream_write_uint16(stream, file_info->version_madeby);
605 }
606
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700607 // Calculate version needed to extract
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700608 if (err == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700609 {
610 version_needed = file_info->version_needed;
611 if (version_needed == 0)
612 {
613 version_needed = 20;
614 if (zip64)
615 version_needed = 45;
616#ifdef HAVE_AES
617 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
618 version_needed = 51;
619#endif
620#ifdef HAVE_LZMA
621 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
622 version_needed = 63;
623#endif
624 }
625 err = mz_stream_write_uint16(stream, version_needed);
626 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700627 if (err == MZ_OK)
628 err = mz_stream_write_uint16(stream, file_info->flag);
629 if (err == MZ_OK)
630 {
631#ifdef HAVE_AES
Nathan Moinvaziri413822a2018-10-20 09:45:07 -0700632 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700633 err = mz_stream_write_uint16(stream, MZ_COMPRESS_METHOD_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700634 else
635#endif
636 err = mz_stream_write_uint16(stream, file_info->compression_method);
637 }
638 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700639 {
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700640 if (file_info->modified_date != 0 && !mask)
Nathan Moinvaziri17cdaec2017-10-26 10:18:41 -0700641 dos_date = mz_zip_time_t_to_dos_date(file_info->modified_date);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700642 err = mz_stream_write_uint32(stream, dos_date);
643 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700644
645 if (err == MZ_OK)
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700646 {
647 if (mask)
648 err = mz_stream_write_uint32(stream, 0);
649 else
650 err = mz_stream_write_uint32(stream, file_info->crc); // crc
651 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700652 if (err == MZ_OK)
653 {
654 if (file_info->compressed_size >= UINT32_MAX) // compr size
655 err = mz_stream_write_uint32(stream, UINT32_MAX);
656 else
657 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
658 }
659 if (err == MZ_OK)
660 {
661 if (file_info->uncompressed_size >= UINT32_MAX) // uncompr size
662 err = mz_stream_write_uint32(stream, UINT32_MAX);
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700663 else if (mask)
664 err = mz_stream_write_uint32(stream, 0);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700665 else
666 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
667 }
668
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700669 if (mask)
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700670 {
Nathan Moinvaziri264dc182018-11-13 17:42:13 -0800671 snprintf(masked_name, sizeof(masked_name), "%"PRIx32"_%"PRIx64,
672 file_info->disk_number, file_info->disk_offset);
Nathan Moinvazirifa620e42018-10-20 10:37:10 -0700673 filename = masked_name;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700674 }
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700675 else
676 {
677 filename = file_info->filename;
678
679 if ((mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK) &&
680 ((filename[filename_length - 1] != '/') && (filename[filename_length - 1] != '\\')))
681 {
682 filename_size += 1;
683 write_end_slash = 1;
684 }
685 }
686
687 filename_length = (uint16_t)strlen(filename);
688 filename_size += filename_length;
689
690 if (err == MZ_OK)
691 err = mz_stream_write_uint16(stream, filename_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700692 if (err == MZ_OK)
693 err = mz_stream_write_uint16(stream, extrafield_size);
694
695 if (!local)
696 {
697 if (file_info->comment != NULL)
Nathan Moinvaziridd103f42018-11-13 14:50:32 -0800698 {
699 comment_size = (int32_t)strlen(file_info->comment);
700 if (comment_size > UINT16_MAX)
701 comment_size = UINT16_MAX;
702 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700703 if (err == MZ_OK)
Nathan Moinvaziridd103f42018-11-13 14:50:32 -0800704 err = mz_stream_write_uint16(stream, (uint16_t)comment_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700705 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700706 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700707 if (err == MZ_OK)
708 err = mz_stream_write_uint16(stream, file_info->internal_fa);
709 if (err == MZ_OK)
710 err = mz_stream_write_uint32(stream, file_info->external_fa);
711 if (err == MZ_OK)
712 {
713 if (file_info->disk_offset >= UINT32_MAX)
714 err = mz_stream_write_uint32(stream, UINT32_MAX);
715 else
716 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
717 }
718 }
719
720 if (err == MZ_OK)
721 {
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700722 if (mz_stream_write(stream, filename, filename_length) != filename_length)
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700723 err = MZ_WRITE_ERROR;
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700724
725 // Ensure that directories have a slash appended to them for compatibility
726 if (err == MZ_OK && write_end_slash)
727 err = mz_stream_write_uint8(stream, '/');
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700728 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700729
730 if (file_info->extrafield_size > 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700731 {
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800732 err_mem = mz_stream_mem_seek(file_extra_stream, 0, MZ_SEEK_SET);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700733 while (err == MZ_OK && err_mem == MZ_OK)
734 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700735 err_mem = mz_stream_read_uint16(file_extra_stream, &field_type);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700736 if (err_mem == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700737 err_mem = mz_stream_read_uint16(file_extra_stream, &field_length);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700738 if (err_mem != MZ_OK)
739 break;
740
741 // Prefer our zip 64, ntfs extensions over incoming
742 if (field_type == MZ_ZIP_EXTENSION_ZIP64 || field_type == MZ_ZIP_EXTENSION_NTFS)
743 {
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800744 err_mem = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700745 continue;
746 }
747
748 err = mz_stream_write_uint16(stream, field_type);
749 if (err == MZ_OK)
750 err = mz_stream_write_uint16(stream, field_length);
751 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700752 err = mz_stream_copy(stream, file_extra_stream, field_length);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700753 }
754
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700755 mz_stream_mem_delete(&file_extra_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700756 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700757
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700758 // Write ZIP64 extra field
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700759 if ((err == MZ_OK) && (zip64))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700760 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700761 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_ZIP64, field_length_zip64);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700762 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700763 {
764 if (mask)
765 err = mz_stream_write_int64(stream, 0);
766 else
767 err = mz_stream_write_int64(stream, file_info->uncompressed_size);
768 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700769 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700770 err = mz_stream_write_int64(stream, file_info->compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700771 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700772 err = mz_stream_write_int64(stream, file_info->disk_offset);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700773 }
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700774 // Write NTFS extra field
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700775 if ((err == MZ_OK) && (field_length_ntfs > 0))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700776 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700777 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_NTFS, field_length_ntfs);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700778 if (err == MZ_OK)
779 err = mz_stream_write_uint32(stream, reserved);
780 if (err == MZ_OK)
781 err = mz_stream_write_uint16(stream, 0x01);
782 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700783 err = mz_stream_write_uint16(stream, field_length_ntfs - 8);
juanii3679a3d2018-02-11 13:55:38 -0300784 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700785 {
786 mz_zip_unix_to_ntfs_time(file_info->modified_date, &ntfs_time);
787 err = mz_stream_write_uint64(stream, ntfs_time);
788 }
juanii3679a3d2018-02-11 13:55:38 -0300789 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700790 {
791 mz_zip_unix_to_ntfs_time(file_info->accessed_date, &ntfs_time);
792 err = mz_stream_write_uint64(stream, ntfs_time);
793 }
juanii3679a3d2018-02-11 13:55:38 -0300794 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700795 {
796 mz_zip_unix_to_ntfs_time(file_info->creation_date, &ntfs_time);
797 err = mz_stream_write_uint64(stream, ntfs_time);
798 }
799 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700800#ifdef HAVE_AES
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700801 // Write AES extra field
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700802 if ((err == MZ_OK) && (!skip_aes) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700803 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700804 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_AES, field_length_aes);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700805 if (err == MZ_OK)
806 err = mz_stream_write_uint16(stream, file_info->aes_version);
807 if (err == MZ_OK)
808 err = mz_stream_write_uint8(stream, 'A');
809 if (err == MZ_OK)
810 err = mz_stream_write_uint8(stream, 'E');
811 if (err == MZ_OK)
812 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
813 if (err == MZ_OK)
814 err = mz_stream_write_uint16(stream, file_info->compression_method);
815 }
816#endif
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700817 if ((err == MZ_OK) && (!local) && (file_info->comment != NULL))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700818 {
Nathan Moinvazirica014e62018-08-15 07:31:12 -0700819 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != file_info->comment_size)
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700820 err = MZ_WRITE_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700821 }
822
823 return err;
824}
825
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800826static int32_t mz_zip_entry_write_descriptor(void *stream, uint32_t crc32, int64_t compressed_size, int64_t uncompressed_size)
827{
828 int32_t err = MZ_OK;
829
830 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
831 if (err == MZ_OK)
832 err = mz_stream_write_uint32(stream, crc32);
833
834 // Store data descriptor as 8 bytes if zip 64 extension enabled
835 if (err == MZ_OK)
836 {
837 // Zip 64 extension is enabled when uncompressed size is > UINT32_MAX
838 if (uncompressed_size <= UINT32_MAX)
839 err = mz_stream_write_uint32(stream, (uint32_t)compressed_size);
840 else
841 err = mz_stream_write_int64(stream, compressed_size);
842 }
843 if (err == MZ_OK)
844 {
845 if (uncompressed_size <= UINT32_MAX)
846 err = mz_stream_write_uint32(stream, (uint32_t)uncompressed_size);
847 else
848 err = mz_stream_write_int64(stream, uncompressed_size);
849 }
850
851 return err;
852}
853
854static int32_t mz_zip_read_cd(void *handle)
855{
856 mz_zip *zip = (mz_zip *)handle;
857 uint64_t number_entry_cd64 = 0;
858 uint64_t number_entry = 0;
859 uint64_t number_entry_cd = 0;
860 int64_t eocd_pos = 0;
861 int64_t eocd_pos64 = 0;
862 int64_t value64i = 0;
863 uint16_t value16 = 0;
864 uint32_t value32 = 0;
865 uint64_t value64 = 0;
866 uint16_t comment_size = 0;
867 int32_t comment_read = 0;
868 int32_t err = MZ_OK;
869
870
871 if (zip == NULL)
872 return MZ_PARAM_ERROR;
873
874 // Read and cache central directory records
875 err = mz_zip_search_eocd(zip->stream, &eocd_pos);
876 if (err == MZ_OK)
877 {
878 // The signature, already checked
879 err = mz_stream_read_uint32(zip->stream, &value32);
880 // Number of this disk
881 if (err == MZ_OK)
882 err = mz_stream_read_uint16(zip->stream, &value16);
883 // Number of the disk with the start of the central directory
884 if (err == MZ_OK)
885 err = mz_stream_read_uint16(zip->stream, &value16);
886 zip->disk_number_with_cd = value16;
887 // Total number of entries in the central dir on this disk
888 if (err == MZ_OK)
889 err = mz_stream_read_uint16(zip->stream, &value16);
890 zip->number_entry = value16;
891 // Total number of entries in the central dir
892 if (err == MZ_OK)
893 err = mz_stream_read_uint16(zip->stream, &value16);
894 number_entry_cd = value16;
895 if (number_entry_cd != zip->number_entry)
896 err = MZ_FORMAT_ERROR;
897 // Size of the central directory
898 if (err == MZ_OK)
899 err = mz_stream_read_uint32(zip->stream, &value32);
900 if (err == MZ_OK)
901 zip->cd_size = value32;
902 // Offset of start of central directory with respect to the starting disk number
903 if (err == MZ_OK)
904 err = mz_stream_read_uint32(zip->stream, &value32);
905 if (err == MZ_OK)
906 zip->cd_offset = value32;
907 // Zip file global comment length
908 if (err == MZ_OK)
909 err = mz_stream_read_uint16(zip->stream, &comment_size);
910 if ((err == MZ_OK) && (comment_size > 0))
911 {
912 zip->comment = (char *)MZ_ALLOC(comment_size + 1);
913 if (zip->comment != NULL)
914 {
915 comment_read = mz_stream_read(zip->stream, zip->comment, comment_size);
916 // Don't fail if incorrect comment length read, not critical
917 if (comment_read < 0)
918 comment_read = 0;
919 zip->comment[comment_read] = 0;
920 }
921 }
922
923 if ((err == MZ_OK) && ((number_entry_cd == UINT16_MAX) || (zip->cd_offset == UINT32_MAX)))
924 {
925 // Format should be Zip64, as the central directory or file size is too large
926 if (mz_zip_search_zip64_eocd(zip->stream, eocd_pos, &eocd_pos64) == MZ_OK)
927 {
928 eocd_pos = eocd_pos64;
929
930 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
931 // The signature, already checked
932 if (err == MZ_OK)
933 err = mz_stream_read_uint32(zip->stream, &value32);
934 // Size of zip64 end of central directory record
935 if (err == MZ_OK)
936 err = mz_stream_read_uint64(zip->stream, &value64);
937 // Version made by
938 if (err == MZ_OK)
939 err = mz_stream_read_uint16(zip->stream, &zip->version_madeby);
940 // Version needed to extract
941 if (err == MZ_OK)
942 err = mz_stream_read_uint16(zip->stream, &value16);
943 // Number of this disk
944 if (err == MZ_OK)
945 err = mz_stream_read_uint32(zip->stream, &value32);
946 // Number of the disk with the start of the central directory
947 if (err == MZ_OK)
948 err = mz_stream_read_uint32(zip->stream, &zip->disk_number_with_cd);
949 // Total number of entries in the central directory on this disk
950 if (err == MZ_OK)
951 err = mz_stream_read_uint64(zip->stream, &number_entry);
952 // Total number of entries in the central directory
953 if (err == MZ_OK)
954 err = mz_stream_read_uint64(zip->stream, &number_entry_cd64);
955 if (number_entry == UINT32_MAX)
956 zip->number_entry = number_entry_cd64;
957 // Size of the central directory
958 if (err == MZ_OK)
959 err = mz_stream_read_int64(zip->stream, &zip->cd_size);
960 // Offset of start of central directory with respect to the starting disk number
961 if (err == MZ_OK)
962 err = mz_stream_read_int64(zip->stream, &zip->cd_offset);
963 }
964 else if ((zip->number_entry == UINT16_MAX) || (number_entry_cd != zip->number_entry) ||
965 (zip->cd_size == UINT16_MAX) || (zip->cd_offset == UINT32_MAX))
966 {
967 err = MZ_FORMAT_ERROR;
968 }
969 }
970 }
971
972 if (err == MZ_OK)
973 {
Nathan Moinvaziri264dc182018-11-13 17:42:13 -0800974 mz_zip_print("Zip - Read cd (disk %"PRId32" entries %"PRId64" offset %"PRId64" size %"PRId64")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800975 zip->disk_number_with_cd, zip->number_entry, zip->cd_offset, zip->cd_size);
976
977 // Verify central directory signature exists at offset
978 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
979 if (err == MZ_OK)
980 err = mz_stream_read_uint32(zip->stream, &value32);
981 if (value32 != MZ_ZIP_MAGIC_CENTRALHEADER)
982 {
983 // If not found attempt to seek backward to find it
984 err = mz_stream_seek(zip->stream, eocd_pos - zip->cd_size, MZ_SEEK_SET);
985 if (err == MZ_OK)
986 err = mz_stream_read_uint32(zip->stream, &value32);
987 if (value32 == MZ_ZIP_MAGIC_CENTRALHEADER)
988 {
989 // If found compensate for incorrect locations
990 value64i = zip->cd_offset;
991 zip->cd_offset = eocd_pos - zip->cd_size;
992 // Assume disk has prepended data
993 zip->disk_offset_shift = zip->cd_offset - value64i;
994 }
995 }
996 }
997
998 if (err == MZ_OK)
999 {
1000 if (eocd_pos < zip->cd_offset)
1001 {
1002 // End of central dir should always come after central dir
1003 err = MZ_FORMAT_ERROR;
1004 }
1005 else if (eocd_pos < zip->cd_offset + zip->cd_size)
1006 {
1007 // Truncate size of cd if incorrect size or offset provided
1008 zip->cd_size = eocd_pos - zip->cd_offset;
1009 }
1010 }
1011
1012 return err;
1013}
1014
1015static int32_t mz_zip_write_cd(void *handle)
1016{
1017 mz_zip *zip = (mz_zip *)handle;
1018 int64_t zip64_eocd_pos_inzip = 0;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001019 int64_t disk_number = 0;
1020 int64_t disk_size = 0;
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001021 int32_t comment_size = 0;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001022 int32_t err = MZ_OK;
1023
1024
1025 if (zip == NULL)
1026 return MZ_PARAM_ERROR;
1027
1028 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
1029 zip->disk_number_with_cd = (uint32_t)disk_number;
1030 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_SIZE, &disk_size) == MZ_OK && disk_size > 0)
1031 zip->disk_number_with_cd += 1;
1032 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1033
1034 zip->cd_offset = mz_stream_tell(zip->stream);
1035 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_END);
1036 zip->cd_size = (uint32_t)mz_stream_tell(zip->cd_mem_stream);
1037 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_SET);
1038
1039 err = mz_stream_copy(zip->stream, zip->cd_mem_stream, (int32_t)zip->cd_size);
1040
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001041 mz_zip_print("Zip - Write cd (disk %"PRId32" entries %"PRId64" offset %"PRId64" size %"PRId64")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001042 zip->disk_number_with_cd, zip->number_entry, zip->cd_offset, zip->cd_size);
1043
1044 // Write the ZIP64 central directory header
1045 if (zip->cd_offset >= UINT32_MAX || zip->number_entry > UINT16_MAX)
1046 {
1047 zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
1048
1049 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
1050
1051 // Size of this 'zip64 end of central directory'
1052 if (err == MZ_OK)
1053 err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
1054 // Version made by
1055 if (err == MZ_OK)
1056 err = mz_stream_write_uint16(zip->stream, zip->version_madeby);
1057 // Version needed
1058 if (err == MZ_OK)
1059 err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
1060 // Number of this disk
1061 if (err == MZ_OK)
1062 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
1063 // Number of the disk with the start of the central directory
1064 if (err == MZ_OK)
1065 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
1066 // Total number of entries in the central dir on this disk
1067 if (err == MZ_OK)
1068 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
1069 // Total number of entries in the central dir
1070 if (err == MZ_OK)
1071 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
1072 // Size of the central directory
1073 if (err == MZ_OK)
1074 err = mz_stream_write_int64(zip->stream, zip->cd_size);
1075 // Offset of start of central directory with respect to the starting disk number
1076 if (err == MZ_OK)
1077 err = mz_stream_write_int64(zip->stream, zip->cd_offset);
1078 if (err == MZ_OK)
1079 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
1080
1081 // Number of the disk with the start of the central directory
1082 if (err == MZ_OK)
1083 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
1084 // Relative offset to the end of zip64 central directory
1085 if (err == MZ_OK)
1086 err = mz_stream_write_int64(zip->stream, zip64_eocd_pos_inzip);
1087 // Number of the disk with the start of the central directory
1088 if (err == MZ_OK)
1089 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd + 1);
1090 }
1091
1092 // Write the central directory header
1093
1094 // Signature
1095 if (err == MZ_OK)
1096 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
1097 // Number of this disk
1098 if (err == MZ_OK)
1099 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
1100 // Number of the disk with the start of the central directory
1101 if (err == MZ_OK)
1102 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
1103 // Total number of entries in the central dir on this disk
1104 if (err == MZ_OK)
1105 {
1106 if (zip->number_entry >= UINT16_MAX)
1107 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
1108 else
1109 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
1110 }
1111 // Total number of entries in the central dir
1112 if (err == MZ_OK)
1113 {
1114 if (zip->number_entry >= UINT16_MAX)
1115 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
1116 else
1117 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
1118 }
1119 // Size of the central directory
1120 if (err == MZ_OK)
1121 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_size);
1122 // Offset of start of central directory with respect to the starting disk number
1123 if (err == MZ_OK)
1124 {
1125 if (zip->cd_offset >= UINT32_MAX)
1126 err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
1127 else
1128 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_offset);
1129 }
1130
1131 // Write global comment
1132 if (zip->comment != NULL)
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001133 {
1134 comment_size = (int32_t)strlen(zip->comment);
1135 if (comment_size > UINT16_MAX)
1136 comment_size = UINT16_MAX;
1137 }
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001138 if (err == MZ_OK)
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001139 err = mz_stream_write_uint16(zip->stream, (uint16_t)comment_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001140 if (err == MZ_OK)
1141 {
1142 if (mz_stream_write(zip->stream, zip->comment, comment_size) != comment_size)
1143 err = MZ_READ_ERROR;
1144 }
1145 return err;
1146}
1147
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001148static int32_t mz_zip_recover_cd(void *handle)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001149{
1150 mz_zip *zip = (mz_zip *)handle;
1151 mz_zip_file local_file_info;
1152 void *local_file_info_stream = NULL;
1153 void *file_extra_stream = NULL;
1154 void *cd_mem_stream = NULL;
1155 uint64_t number_entry = 0;
1156 int64_t descriptor_pos = 0;
1157 int64_t disk_offset = 0;
1158 int64_t disk_number = 0;
1159 int64_t compressed_size = 0;
1160 int64_t uncompressed_size = 0;
1161 uint8_t descriptor_magic[4] = MZ_ZIP_MAGIC_DATADESCRIPTORU8;
1162 uint32_t crc32 = 0;
1163 int32_t disk_number_with_cd = 0;
1164 int32_t err = MZ_OK;
1165 uint8_t zip64 = 0;
1166
1167
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001168 mz_zip_print("Zip - Recover cd\n");
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001169
1170 mz_zip_get_cd_mem_stream(handle, &cd_mem_stream);
1171
1172 // Determine if we are on a split disk or not
1173 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, 0);
1174 if (mz_stream_tell(zip->stream) < 0)
1175 {
1176 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1177 mz_stream_seek(zip->stream, 0, MZ_SEEK_SET);
1178 }
1179 else
1180 disk_number_with_cd = 1;
1181
1182 if (mz_stream_is_open(cd_mem_stream) != MZ_OK)
1183 err = mz_stream_mem_open(cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
1184
1185 mz_stream_mem_create(&local_file_info_stream);
1186 mz_stream_mem_open(local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
1187
1188 while (err == MZ_OK)
1189 {
1190 memset(&local_file_info, 0, sizeof(local_file_info));
1191
1192 // Get current offset and disk number for central dir record
1193 disk_offset = mz_stream_tell(zip->stream);
1194 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1195
1196 // Read local headers
1197 err = mz_zip_entry_read_header(zip->stream, 1, &local_file_info, local_file_info_stream);
1198
1199 local_file_info.disk_offset = disk_offset;
1200 if (disk_number < 0)
1201 disk_number = 0;
1202 local_file_info.disk_number = (uint32_t)disk_number;
1203
1204 if (err == MZ_OK)
1205 {
1206 if (local_file_info.compressed_size > 0)
1207 {
1208 err = mz_stream_seek(zip->stream, local_file_info.compressed_size, MZ_SEEK_CUR);
1209 }
1210 else if (local_file_info.uncompressed_size > 0)
1211 {
1212 err = mz_stream_find(zip->stream, (const void *)descriptor_magic, sizeof(descriptor_magic),
1213 INT64_MAX, &descriptor_pos);
1214 }
1215 }
1216
1217 // Read descriptor if it exists so we can get to the next local header
1218 if ((err == MZ_OK) && (local_file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR))
1219 {
1220 mz_stream_mem_create(&file_extra_stream);
1221 mz_stream_mem_set_buffer(file_extra_stream, (void *)local_file_info.extrafield,
1222 local_file_info.extrafield_size);
1223
1224 zip64 = 0;
1225 if (mz_zip_extrafield_find(file_extra_stream, MZ_ZIP_EXTENSION_ZIP64, NULL) == MZ_OK)
1226 zip64 = 1;
1227
1228 mz_stream_mem_delete(&file_extra_stream);
1229
1230 err = mz_zip_entry_read_descriptor(zip->stream, zip64, &crc32,
1231 &compressed_size, &uncompressed_size);
1232
1233 if (local_file_info.crc == 0)
1234 local_file_info.crc = crc32;
1235 if (local_file_info.compressed_size == 0)
1236 local_file_info.compressed_size = compressed_size;
1237 if (local_file_info.uncompressed_size == 0)
1238 local_file_info.uncompressed_size = uncompressed_size;
1239 }
1240
1241 // Rewrite central dir with local headers and offsets
1242 if (err == MZ_OK)
1243 err = mz_zip_entry_write_header(cd_mem_stream, 0, &local_file_info);
1244
1245 if (err == MZ_OK)
1246 number_entry += 1;
1247 }
1248
1249 mz_stream_mem_delete(&local_file_info_stream);
1250
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001251 mz_zip_print("Zip - Recover cd complete (cddisk %"PRId32" entries %"PRId64")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001252 disk_number_with_cd, number_entry);
1253
1254 if (number_entry == 0)
1255 return err;
1256
1257 // Set new upper seek boundary for central dir mem stream
1258 disk_offset = mz_stream_tell(cd_mem_stream);
1259 mz_stream_mem_set_buffer_limit(cd_mem_stream, (int32_t)disk_offset);
1260
1261 // Set new central directory info
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001262 mz_zip_set_cd_start_pos(handle, 0);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001263 mz_zip_set_number_entry(handle, number_entry);
1264 mz_zip_set_disk_number_with_cd(handle, disk_number_with_cd);
1265
1266 return MZ_OK;
1267}
1268
1269void *mz_zip_create(void **handle)
1270{
1271 mz_zip *zip = NULL;
1272
1273 zip = (mz_zip *)MZ_ALLOC(sizeof(mz_zip));
1274 if (zip != NULL)
1275 memset(zip, 0, sizeof(mz_zip));
1276 if (handle != NULL)
1277 *handle = zip;
1278
1279 return zip;
1280}
1281
1282void mz_zip_delete(void **handle)
1283{
1284 mz_zip *zip = NULL;
1285 if (handle == NULL)
1286 return;
1287 zip = (mz_zip *)*handle;
1288 if (zip != NULL)
1289 {
1290 MZ_FREE(zip);
1291 }
1292 *handle = NULL;
1293}
1294
1295int32_t mz_zip_open(void *handle, void *stream, int32_t mode)
1296{
1297 mz_zip *zip = (mz_zip *)handle;
1298 int32_t err = MZ_OK;
1299
1300
1301 if (zip == NULL)
1302 return MZ_PARAM_ERROR;
1303
1304 mz_zip_print("Zip - Open\n");
1305
1306 zip->stream = stream;
1307
1308 mz_stream_mem_create(&zip->cd_mem_stream);
1309
1310 if (mode & MZ_OPEN_MODE_WRITE)
1311 {
1312 mz_stream_mem_open(zip->cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
1313 zip->cd_stream = zip->cd_mem_stream;
1314 }
1315 else
1316 {
1317 zip->cd_stream = stream;
1318 }
1319
1320 if ((mode & MZ_OPEN_MODE_READ) || (mode & MZ_OPEN_MODE_APPEND))
1321 {
1322 if ((mode & MZ_OPEN_MODE_CREATE) == 0)
1323 {
1324 err = mz_zip_read_cd(zip);
1325 if (err != MZ_OK)
1326 {
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001327 mz_zip_print("Zip - Error detected reading cd (%"PRId32")", err);
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001328 if (zip->recover && mz_zip_recover_cd(zip) == MZ_OK)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001329 err = MZ_OK;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001330 }
1331 }
1332
1333 if ((err == MZ_OK) && (mode & MZ_OPEN_MODE_APPEND))
1334 {
1335 if (zip->cd_size > 0)
1336 {
1337 // Store central directory in memory
1338 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
1339 if (err == MZ_OK)
1340 err = mz_stream_copy(zip->cd_mem_stream, zip->stream, (int32_t)zip->cd_size);
1341 if (err == MZ_OK)
1342 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
1343 }
1344 else
1345 {
1346 // If no central directory, append new zip to end of file
1347 err = mz_stream_seek(zip->stream, 0, MZ_SEEK_END);
1348 }
1349 }
1350 else
1351 {
1352 zip->cd_start_pos = zip->cd_offset;
1353 }
1354 }
1355
1356 if (err != MZ_OK)
1357 {
1358 mz_zip_close(zip);
1359 return err;
1360 }
1361
1362 // Memory streams used to store variable length file info data
1363 mz_stream_mem_create(&zip->file_info_stream);
1364 mz_stream_mem_open(zip->file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
1365
1366 mz_stream_mem_create(&zip->local_file_info_stream);
1367 mz_stream_mem_open(zip->local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
1368
1369 zip->open_mode = mode;
1370
1371 return err;
1372}
1373
1374int32_t mz_zip_close(void *handle)
1375{
1376 mz_zip *zip = (mz_zip *)handle;
1377 int32_t err = MZ_OK;
1378
1379 if (zip == NULL)
1380 return MZ_PARAM_ERROR;
1381
1382 mz_zip_print("Zip - Close\n");
1383
1384 if (mz_zip_entry_is_open(handle) == MZ_OK)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001385 err = mz_zip_entry_close(handle);
Nathan Moinvazirieffc1422018-11-08 15:24:17 -08001386
1387 if ((err == MZ_OK) && (zip->open_mode & MZ_OPEN_MODE_WRITE))
1388 err = mz_zip_write_cd(handle);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001389
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001390 if (zip->cd_mem_stream != NULL)
1391 {
1392 mz_stream_close(zip->cd_mem_stream);
1393 mz_stream_delete(&zip->cd_mem_stream);
1394 }
1395
1396 if (zip->file_info_stream != NULL)
1397 {
1398 mz_stream_mem_close(zip->file_info_stream);
1399 mz_stream_mem_delete(&zip->file_info_stream);
1400 }
1401 if (zip->local_file_info_stream != NULL)
1402 {
1403 mz_stream_mem_close(zip->local_file_info_stream);
1404 mz_stream_mem_delete(&zip->local_file_info_stream);
1405 }
1406
1407 if (zip->comment)
1408 {
1409 MZ_FREE(zip->comment);
1410 zip->comment = NULL;
1411 }
1412
1413 zip->stream = NULL;
1414 zip->cd_stream = NULL;
1415
1416 return err;
1417}
1418
1419int32_t mz_zip_get_comment(void *handle, const char **comment)
1420{
1421 mz_zip *zip = (mz_zip *)handle;
1422 if (zip == NULL || comment == NULL)
1423 return MZ_PARAM_ERROR;
1424 if (zip->comment == NULL)
1425 return MZ_EXIST_ERROR;
1426 *comment = zip->comment;
1427 return MZ_OK;
1428}
1429
1430int32_t mz_zip_set_comment(void *handle, const char *comment)
1431{
1432 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001433 int32_t comment_size = 0;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001434 if (zip == NULL || comment == NULL)
1435 return MZ_PARAM_ERROR;
1436 if (zip->comment != NULL)
1437 MZ_FREE(zip->comment);
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001438 comment_size = (int32_t)(strlen(comment) + 1);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001439 zip->comment = (char *)MZ_ALLOC(comment_size);
1440 if (zip->comment == NULL)
1441 return MZ_MEM_ERROR;
1442 strncpy(zip->comment, comment, comment_size - 1);
1443 zip->comment[comment_size - 1] = 0;
1444 return MZ_OK;
1445}
1446
1447int32_t mz_zip_get_version_madeby(void *handle, uint16_t *version_madeby)
1448{
1449 mz_zip *zip = (mz_zip *)handle;
1450 if (zip == NULL || version_madeby == NULL)
1451 return MZ_PARAM_ERROR;
1452 *version_madeby = zip->version_madeby;
1453 return MZ_OK;
1454}
1455
1456int32_t mz_zip_set_version_madeby(void *handle, uint16_t version_madeby)
1457{
1458 mz_zip *zip = (mz_zip *)handle;
1459 if (zip == NULL)
1460 return MZ_PARAM_ERROR;
1461 zip->version_madeby = version_madeby;
1462 return MZ_OK;
1463}
1464
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001465int32_t mz_zip_set_recover(void *handle, uint8_t recover)
1466{
1467 mz_zip *zip = (mz_zip *)handle;
1468 if (zip == NULL)
1469 return MZ_PARAM_ERROR;
1470 zip->recover = recover;
1471 return MZ_OK;
1472}
1473
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001474int32_t mz_zip_get_stream(void *handle, void **stream)
1475{
1476 mz_zip *zip = (mz_zip *)handle;
1477 if (zip == NULL || stream == NULL)
1478 return MZ_PARAM_ERROR;
1479 *stream = zip->stream;
1480 if (*stream == NULL)
1481 return MZ_EXIST_ERROR;
1482 return MZ_OK;
1483}
1484
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001485int32_t mz_zip_set_cd_start_pos(void *handle, int64_t cd_start_pos)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001486{
1487 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001488 if (zip == NULL)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001489 return MZ_PARAM_ERROR;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001490 zip->cd_start_pos = cd_start_pos;
1491 return MZ_OK;
1492}
1493
1494int32_t mz_zip_get_cd_mem_stream(void *handle, void **cd_mem_stream)
1495{
1496 mz_zip *zip = (mz_zip *)handle;
1497 if (zip == NULL || cd_mem_stream == NULL)
1498 return MZ_PARAM_ERROR;
1499 *cd_mem_stream = zip->cd_mem_stream;
1500 if (*cd_mem_stream == NULL)
1501 return MZ_EXIST_ERROR;
1502 return MZ_OK;
1503}
1504
1505static int32_t mz_zip_entry_close_int(void *handle)
1506{
1507 mz_zip *zip = (mz_zip *)handle;
1508
1509 if (zip->crypt_stream != NULL)
1510 mz_stream_delete(&zip->crypt_stream);
1511 zip->crypt_stream = NULL;
1512 if (zip->compress_stream != NULL)
1513 mz_stream_delete(&zip->compress_stream);
1514 zip->compress_stream = NULL;
1515
1516 zip->entry_opened = 0;
1517
1518 return MZ_OK;
1519}
1520
Nathan Moinvaziri26308b22018-08-08 09:40:15 -07001521static int32_t mz_zip_entry_open_int(void *handle, uint8_t raw, int16_t compress_level, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001522{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001523 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001524 int64_t max_total_in = 0;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001525 int64_t header_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001526 int64_t footer_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001527 int32_t err = MZ_OK;
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001528 uint8_t use_crypt = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001529
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001530 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001531 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001532
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001533 switch (zip->file_info.compression_method)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001534 {
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001535 case MZ_COMPRESS_METHOD_STORE:
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001536 case MZ_COMPRESS_METHOD_DEFLATE:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001537#ifdef HAVE_BZIP2
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001538 case MZ_COMPRESS_METHOD_BZIP2:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001539#endif
Nathan Moinvaziri566dfe02018-10-26 00:36:30 -07001540#ifdef HAVE_LZMA
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001541 case MZ_COMPRESS_METHOD_LZMA:
1542#endif
1543 err = MZ_OK;
1544 break;
1545 default:
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001546 return MZ_SUPPORT_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001547 }
1548
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001549 zip->entry_raw = raw;
1550
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001551 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password != NULL))
1552 {
1553 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
1554 {
1555 // Encrypt only when we are not trying to write raw and password is supplied.
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001556 if (!zip->entry_raw)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001557 use_crypt = 1;
1558 }
1559 else if (zip->open_mode & MZ_OPEN_MODE_READ)
1560 {
1561 // Decrypt only when password is supplied. Don't error when password
1562 // is not supplied as we may want to read the raw encrypted data.
1563 use_crypt = 1;
1564 }
1565 }
1566
1567 if ((err == MZ_OK) && (use_crypt))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001568 {
1569#ifdef HAVE_AES
1570 if (zip->file_info.aes_version)
1571 {
Nathan Moinvaziri21a31022018-10-24 09:50:16 -07001572 mz_stream_wzaes_create(&zip->crypt_stream);
1573 mz_stream_wzaes_set_password(zip->crypt_stream, password);
1574 mz_stream_wzaes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001575 }
1576 else
1577#endif
1578 {
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001579#ifdef HAVE_PKCRYPT
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001580 uint8_t verify1 = 0;
1581 uint8_t verify2 = 0;
1582
1583 // Info-ZIP modification to ZipCrypto format:
1584 // If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time.
1585
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001586 if (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR)
1587 {
1588 uint32_t dos_date = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001589
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001590 dos_date = mz_zip_time_t_to_dos_date(zip->file_info.modified_date);
1591
1592 verify1 = (uint8_t)((dos_date >> 16) & 0xff);
1593 verify2 = (uint8_t)((dos_date >> 8) & 0xff);
1594 }
1595 else
1596 {
1597 verify1 = (uint8_t)((zip->file_info.crc >> 16) & 0xff);
1598 verify2 = (uint8_t)((zip->file_info.crc >> 24) & 0xff);
1599 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001600
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001601 mz_stream_pkcrypt_create(&zip->crypt_stream);
1602 mz_stream_pkcrypt_set_password(zip->crypt_stream, password);
1603 mz_stream_pkcrypt_set_verify(zip->crypt_stream, verify1, verify2);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001604#endif
1605 }
1606 }
1607
1608 if (err == MZ_OK)
1609 {
1610 if (zip->crypt_stream == NULL)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001611 mz_stream_raw_create(&zip->crypt_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001612
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001613 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001614
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001615 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001616 }
1617
1618 if (err == MZ_OK)
1619 {
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001620 if (zip->entry_raw || zip->file_info.compression_method == MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001621 mz_stream_raw_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001622#ifdef HAVE_ZLIB
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001623 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001624 mz_stream_zlib_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001625#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001626#ifdef HAVE_BZIP2
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001627 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_BZIP2)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001628 mz_stream_bzip_create(&zip->compress_stream);
1629#endif
1630#ifdef HAVE_LZMA
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001631 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001632 mz_stream_lzma_create(&zip->compress_stream);
1633#endif
1634 else
1635 err = MZ_PARAM_ERROR;
1636 }
1637
1638 if (err == MZ_OK)
1639 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001640 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001641 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001642 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, compress_level);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001643 }
1644 else
1645 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001646 if (zip->entry_raw || zip->file_info.compression_method == MZ_COMPRESS_METHOD_STORE || zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001647 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001648 max_total_in = zip->file_info.compressed_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001649 mz_stream_set_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1650
1651 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_HEADER_SIZE, &header_size) == MZ_OK)
1652 max_total_in -= header_size;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001653 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
1654 max_total_in -= footer_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001655
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001656 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001657 }
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001658 if ((zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA) && (zip->file_info.flag & MZ_ZIP_FLAG_LZMA_EOS_MARKER) == 0)
juanii55bcdaf2018-02-11 20:55:57 -03001659 {
1660 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, zip->file_info.compressed_size);
1661 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT_MAX, zip->file_info.uncompressed_size);
1662 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001663 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001664
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001665 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1666
1667 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1668 }
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001669
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001670 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001671 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001672 zip->entry_opened = 1;
Nathan Moinvazirie63d2312018-11-03 19:45:41 -07001673 zip->entry_crc32 = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001674 }
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001675 else
1676 {
1677 mz_zip_entry_close_int(handle);
1678 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001679
1680 return err;
1681}
1682
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001683int32_t mz_zip_entry_is_open(void *handle)
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001684{
1685 mz_zip *zip = (mz_zip *)handle;
1686 if (zip == NULL)
1687 return MZ_PARAM_ERROR;
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001688 if (zip->entry_opened == 0)
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001689 return MZ_EXIST_ERROR;
1690 return MZ_OK;
1691}
1692
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001693int32_t mz_zip_entry_is_dir(void *handle)
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001694{
1695 mz_zip *zip = (mz_zip *)handle;
1696 int32_t filename_length = 0;
1697
1698 if (zip == NULL)
1699 return MZ_PARAM_ERROR;
1700 if (zip->entry_scanned == 0)
1701 return MZ_PARAM_ERROR;
1702 if (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK)
1703 return MZ_OK;
1704
1705 filename_length = (int32_t )strlen(zip->file_info.filename);
1706 if (filename_length > 0)
1707 {
1708 if ((zip->file_info.filename[filename_length - 1] == '/') ||
1709 (zip->file_info.filename[filename_length - 1] == '\\'))
1710 return MZ_OK;
1711 }
1712 return MZ_EXIST_ERROR;
1713}
1714
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001715int32_t mz_zip_entry_read_open(void *handle, uint8_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001716{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001717 mz_zip *zip = (mz_zip *)handle;
1718 int32_t err = MZ_OK;
Nathan Moinvaziriaacf8732018-11-06 09:38:46 -08001719 int32_t err_shift = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001720
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001721#if defined(MZ_ZIP_NO_ENCRYPTION)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001722 if (password != NULL)
1723 return MZ_PARAM_ERROR;
1724#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001725 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001726 return MZ_PARAM_ERROR;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001727 if ((zip->open_mode & MZ_OPEN_MODE_READ) == 0)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001728 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001729 if (zip->entry_scanned == 0)
1730 return MZ_PARAM_ERROR;
Nathan Moinvaziri0f09a002018-04-23 18:48:30 -07001731 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL) && (!raw))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001732 return MZ_PARAM_ERROR;
1733
Anand K Mistrycf622bb2018-11-09 11:32:42 +11001734 // Guard against seek overflows.
1735 if ((zip->disk_offset_shift > 0) &&
1736 (zip->file_info.disk_offset > (INT64_MAX - zip->disk_offset_shift)))
1737 return MZ_FORMAT_ERROR;
1738
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001739 if (zip->file_info.disk_number == zip->disk_number_with_cd)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001740 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1741 else
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001742 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001743
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001744 mz_zip_print("Zip - Entry - Read open (disk %"PRId32" "PRId16" offset %"PRId64")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001745 zip->file_info.disk_number, zip->file_info.disk_offset);
1746
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -07001747 err = mz_stream_seek(zip->stream, zip->file_info.disk_offset + zip->disk_offset_shift, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001748 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001749 err = mz_zip_entry_read_header(zip->stream, 1, &zip->local_file_info, zip->local_file_info_stream);
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001750
Nathan Moinvaziriaacf8732018-11-06 09:38:46 -08001751 if (err == MZ_FORMAT_ERROR && zip->disk_offset_shift > 0)
1752 {
1753 // Perhaps we didn't compensated correctly for incorrect cd offset
1754 err_shift = mz_stream_seek(zip->stream, zip->file_info.disk_offset, MZ_SEEK_SET);
1755 if (err_shift == MZ_OK)
1756 err_shift = mz_zip_entry_read_header(zip->stream, 1, &zip->local_file_info, zip->local_file_info_stream);
1757 if (err_shift == MZ_OK)
1758 {
1759 zip->disk_offset_shift = 0;
1760 err = err_shift;
1761 }
1762 }
1763
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001764#ifdef MZ_ZIP_NO_DECOMPRESSION
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001765 if (zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001766 err = MZ_SUPPORT_ERROR;
Viktor Szakatse7215072018-09-04 15:06:53 +00001767#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001768 if (err == MZ_OK)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001769 err = mz_zip_entry_open_int(handle, raw, 0, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001770
1771 return err;
1772}
1773
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001774int32_t mz_zip_entry_write_open(void *handle, const mz_zip_file *file_info, int16_t compress_level, uint8_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001775{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001776 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001777 int64_t disk_number = 0;
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001778 uint8_t is_dir = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001779 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001780
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001781#if defined(MZ_ZIP_NO_ENCRYPTION)
tz-lomb1b25802017-11-10 15:03:02 +03001782 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001783 return MZ_PARAM_ERROR;
1784#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001785 if (zip == NULL || file_info == NULL || file_info->filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001786 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001787
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001788 if (mz_zip_entry_is_open(handle) == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001789 {
1790 err = mz_zip_entry_close(handle);
1791 if (err != MZ_OK)
1792 return err;
1793 }
1794
1795 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001796
1797 mz_zip_print("Zip - Entry - Write open - %s (level %"PRId16" raw %"PRId8")\n",
1798 zip->file_info.filename, compress_level, raw);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001799
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001800 mz_stream_seek(zip->file_info_stream, 0, MZ_SEEK_SET);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001801 mz_stream_write(zip->file_info_stream, file_info, sizeof(mz_zip_file));
1802
1803 // Copy filename, extrafield, and comment internally
1804 if (file_info->filename != NULL)
1805 {
Nathan Moinvaziric714ef62018-10-25 22:01:02 -07001806 mz_stream_mem_get_buffer_at_current(zip->file_info_stream, (const void **)&zip->file_info.filename);
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001807 mz_stream_write_chars(zip->file_info_stream, file_info->filename, 1);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001808 }
1809 if (file_info->extrafield != NULL)
1810 {
Nathan Moinvaziri6bde0342018-10-25 22:05:13 -07001811 mz_stream_mem_get_buffer_at_current(zip->file_info_stream, (const void **)&zip->file_info.extrafield);
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001812 mz_stream_write(zip->file_info_stream, file_info->extrafield, file_info->extrafield_size);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001813 }
1814 if (file_info->comment != NULL)
1815 {
Nathan Moinvaziric714ef62018-10-25 22:01:02 -07001816 mz_stream_mem_get_buffer_at_current(zip->file_info_stream, (const void **)&zip->file_info.comment);
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001817 mz_stream_write_chars(zip->file_info_stream, file_info->comment, 1);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001818 }
1819
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001820 if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001821 {
1822 if ((compress_level == 8) || (compress_level == 9))
1823 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
1824 if (compress_level == 2)
1825 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
1826 if (compress_level == 1)
1827 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1828 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001829#ifdef HAVE_LZMA
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001830 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001831 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001832#endif
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001833
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001834 if (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK)
1835 is_dir = 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001836
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001837 if (!is_dir)
1838 {
1839 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
1840 if (password != NULL)
1841 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
1842 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001843
juaniib8887e92018-02-14 00:51:05 -03001844 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1845 zip->file_info.disk_number = (uint32_t)disk_number;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001846
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001847 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001848 zip->file_info.crc = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001849 zip->file_info.compressed_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001850
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001851#ifdef HAVE_AES
1852 if (zip->file_info.aes_version && zip->file_info.aes_encryption_mode == 0)
1853 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTION_MODE_256;
1854#endif
1855
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001856 if ((compress_level == 0) || (is_dir))
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001857 zip->file_info.compression_method = MZ_COMPRESS_METHOD_STORE;
Viktor Szakats2884e672018-04-30 08:12:13 +00001858
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001859#ifdef MZ_ZIP_NO_COMPRESSION
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001860 if (zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001861 err = MZ_SUPPORT_ERROR;
1862#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001863 if (err == MZ_OK)
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001864 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001865 if (err == MZ_OK)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001866 err = mz_zip_entry_open_int(handle, raw, compress_level, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001867
1868 return err;
1869}
1870
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001871int32_t mz_zip_entry_read(void *handle, void *buf, int32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001872{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001873 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001874 int32_t read = 0;
1875
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001876 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001877 return MZ_PARAM_ERROR;
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001878 if (UINT_MAX == UINT16_MAX && len > UINT16_MAX) // zlib limitation
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001879 return MZ_PARAM_ERROR;
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001880 if (len == 0)
1881 return MZ_PARAM_ERROR;
1882
Nathan Moinvazirieb80cd92018-07-11 16:45:09 -07001883 if (zip->file_info.compressed_size == 0)
1884 return 0;
1885
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001886 // Read entire entry even if uncompressed_size = 0, otherwise
1887 // aes encryption validation will fail if compressed_size > 0
Nathan Moinvazirie63d2312018-11-03 19:45:41 -07001888 read = mz_stream_read(zip->compress_stream, buf, len);
1889 if (read > 0)
1890 zip->entry_crc32 = mz_crypt_crc32_update(zip->entry_crc32, buf, read);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001891
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001892 mz_zip_print("Zip - Entry - Read - %"PRId32" (max %"PRId32")\n", read, len);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001893
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001894 return read;
1895}
1896
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001897int32_t mz_zip_entry_write(void *handle, const void *buf, int32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001898{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001899 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001900 int32_t written = 0;
1901
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001902 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001903 return MZ_PARAM_ERROR;
Nathan Moinvazirie63d2312018-11-03 19:45:41 -07001904 written = mz_stream_write(zip->compress_stream, buf, len);
1905 if (written > 0)
1906 zip->entry_crc32 = mz_crypt_crc32_update(zip->entry_crc32, buf, written);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001907
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001908 mz_zip_print("Zip - Entry - Write - %"PRId32" (max %"PRId32")\n", written, len);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001909
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001910 return written;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001911}
1912
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001913int32_t mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001914{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001915 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001916
1917 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001918 return MZ_PARAM_ERROR;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001919
1920 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
1921 {
1922 if (!zip->entry_scanned)
1923 return MZ_PARAM_ERROR;
1924 }
1925
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001926 *file_info = &zip->file_info;
1927 return MZ_OK;
1928}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001929
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001930int32_t mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001931{
1932 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001933 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001934 return MZ_PARAM_ERROR;
1935 *local_file_info = &zip->local_file_info;
1936 return MZ_OK;
1937}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001938
Nathan Moinvaziri915c5132018-10-26 20:00:52 -07001939int32_t mz_zip_entry_set_extrafield(void *handle, const uint8_t *extrafield, uint16_t extrafield_size)
1940{
1941 mz_zip *zip = (mz_zip *)handle;
1942
1943 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
1944 return MZ_PARAM_ERROR;
1945
1946 zip->file_info.extrafield = extrafield;
1947 zip->file_info.extrafield_size = extrafield_size;
1948 return MZ_OK;
1949}
1950
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001951int32_t mz_zip_entry_close(void *handle)
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001952{
1953 return mz_zip_entry_close_raw(handle, 0, 0);
1954}
1955
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001956int32_t mz_zip_entry_close_raw(void *handle, int64_t uncompressed_size, uint32_t crc32)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001957{
1958 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001959 int64_t compressed_size = 0;
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001960 int64_t total_in = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001961 int32_t err = MZ_OK;
1962
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001963 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001964 return MZ_PARAM_ERROR;
Nathan Moinvaziri915c5132018-10-26 20:00:52 -07001965
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001966 mz_stream_close(zip->compress_stream);
Nathan Moinvaziri6ac2ff42018-07-21 14:29:29 -07001967
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001968 if (!zip->entry_raw)
Nathan Moinvazirie63d2312018-11-03 19:45:41 -07001969 crc32 = zip->entry_crc32;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001970
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001971 mz_zip_print("Zip - Entry - Close (ucs %"PRId64" crc 0x%08"PRIx32")\n", compressed_size, crc32);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001972
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001973 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001974 {
Nathan Moinvazirie63d2312018-11-03 19:45:41 -07001975 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in);
Nathan Moinvaziri413822a2018-10-20 09:45:07 -07001976
1977 // If entire entry was not read verification will fail
1978 if ((total_in > 0) && (!zip->entry_raw))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001979 {
Nathan Moinvaziri413822a2018-10-20 09:45:07 -07001980#ifdef HAVE_AES
Nathan Moinvaziri413822a2018-10-20 09:45:07 -07001981 // AES zip version AE-1 will expect a valid crc as well
1982 if (zip->file_info.aes_version <= 0x0001)
1983#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001984 {
1985 if (crc32 != zip->file_info.crc)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001986 {
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001987 mz_zip_print("Zip - Entry - Crc failed (actual 0x%08"PRIx32" expected 0x%08"PRIx32")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001988 crc32, zip->file_info.crc);
1989
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001990 err = MZ_CRC_ERROR;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001991 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001992 }
1993 }
1994 }
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001995
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001996 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, &compressed_size);
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001997 if (!zip->entry_raw)
Nathan Moinvazirie63d2312018-11-03 19:45:41 -07001998 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN, &uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001999
2000 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
2001 {
2002 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002003 err = mz_stream_close(zip->crypt_stream);
2004
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07002005 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002006 }
2007
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07002008 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002009 {
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07002010 if ((err == MZ_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002011 {
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002012 if (zip->file_info.flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO)
2013 err = mz_zip_entry_write_descriptor(zip->stream, 0, compressed_size, 0);
2014 else
2015 err = mz_zip_entry_write_descriptor(zip->stream, crc32, compressed_size, uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002016 }
2017
2018 zip->file_info.crc = crc32;
2019 zip->file_info.compressed_size = compressed_size;
2020 zip->file_info.uncompressed_size = uncompressed_size;
2021
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08002022 mz_zip_print("Zip - Entry - Write cd (ucs %"PRId64" cs %"PRId64" crc 0x%08"PRIx32")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002023 uncompressed_size, compressed_size, crc32);
2024
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002025 if (err == MZ_OK)
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07002026 err = mz_zip_entry_write_header(zip->cd_mem_stream, 0, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002027
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07002028 zip->number_entry += 1;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002029 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002030
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002031 mz_zip_entry_close_int(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002032
2033 return err;
2034}
2035
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002036static int32_t mz_zip_goto_next_entry_int(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002037{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002038 mz_zip *zip = (mz_zip *)handle;
2039 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002040
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002041 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002042 return MZ_PARAM_ERROR;
2043
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002044 zip->entry_scanned = 0;
2045
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002046 mz_stream_set_prop_int64(zip->cd_stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Viktor Szakats915b82e2018-04-24 10:02:39 +00002047
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07002048 err = mz_stream_seek(zip->cd_stream, zip->cd_current_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002049 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07002050 err = mz_zip_entry_read_header(zip->cd_stream, 0, &zip->file_info, zip->file_info_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002051 if (err == MZ_OK)
2052 zip->entry_scanned = 1;
2053 return err;
2054}
2055
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07002056int32_t mz_zip_set_number_entry(void *handle, uint64_t number_entry)
2057{
2058 mz_zip *zip = (mz_zip *)handle;
2059 if (zip == NULL)
2060 return MZ_PARAM_ERROR;
2061 zip->number_entry = number_entry;
2062 return MZ_OK;
2063}
2064
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002065int32_t mz_zip_get_number_entry(void *handle, uint64_t *number_entry)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07002066{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002067 mz_zip *zip = (mz_zip *)handle;
2068 if (zip == NULL || number_entry == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07002069 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07002070 *number_entry = zip->number_entry;
2071 return MZ_OK;
2072}
2073
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002074int32_t mz_zip_set_disk_number_with_cd(void *handle, uint32_t disk_number_with_cd)
2075{
2076 mz_zip *zip = (mz_zip *)handle;
2077 if (zip == NULL)
2078 return MZ_PARAM_ERROR;
2079 zip->disk_number_with_cd = disk_number_with_cd;
2080 return MZ_OK;
2081}
2082
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002083int32_t mz_zip_get_disk_number_with_cd(void *handle, uint32_t *disk_number_with_cd)
juanii566b9782018-02-10 15:07:54 -03002084{
2085 mz_zip *zip = (mz_zip *)handle;
2086 if (zip == NULL || disk_number_with_cd == NULL)
2087 return MZ_PARAM_ERROR;
2088 *disk_number_with_cd = zip->disk_number_with_cd;
2089 return MZ_OK;
2090}
2091
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002092int64_t mz_zip_get_entry(void *handle)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002093{
2094 mz_zip *zip = (mz_zip *)handle;
2095
2096 if (zip == NULL)
2097 return MZ_PARAM_ERROR;
2098
2099 return zip->cd_current_pos;
2100}
2101
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002102int32_t mz_zip_goto_entry(void *handle, int64_t cd_pos)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002103{
2104 mz_zip *zip = (mz_zip *)handle;
2105
2106 if (zip == NULL)
2107 return MZ_PARAM_ERROR;
2108
2109 if (cd_pos < zip->cd_start_pos || cd_pos > zip->cd_start_pos + zip->cd_size)
2110 return MZ_PARAM_ERROR;
2111
2112 zip->cd_current_pos = cd_pos;
2113
2114 return mz_zip_goto_next_entry_int(handle);
2115}
2116
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002117int32_t mz_zip_goto_first_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002118{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002119 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002120
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002121 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002122 return MZ_PARAM_ERROR;
2123
Nathan Moinvaziricda36002017-10-21 09:37:18 -07002124 zip->cd_current_pos = zip->cd_start_pos;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002125
2126 return mz_zip_goto_next_entry_int(handle);
2127}
2128
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002129int32_t mz_zip_goto_next_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002130{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002131 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002132
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002133 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002134 return MZ_PARAM_ERROR;
2135
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002136 zip->cd_current_pos += (int64_t)MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002137 zip->file_info.extrafield_size + zip->file_info.comment_size;
2138
2139 return mz_zip_goto_next_entry_int(handle);
2140}
2141
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002142int32_t mz_zip_locate_entry(void *handle, const char *filename, uint8_t ignore_case)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002143{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002144 mz_zip *zip = (mz_zip *)handle;
2145 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002146 int32_t result = 0;
2147
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07002148 if (zip == NULL || filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002149 return MZ_PARAM_ERROR;
2150
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002151 // If we are already on the current entry, no need to search
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07002152 if ((zip->entry_scanned) && (zip->file_info.filename != NULL))
2153 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002154 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07002155 if (result == 0)
2156 return MZ_OK;
2157 }
2158
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002159 // Search all entries starting at the first
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002160 err = mz_zip_goto_first_entry(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002161 while (err == MZ_OK)
2162 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002163 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
2164 if (result == 0)
2165 return MZ_OK;
2166
2167 err = mz_zip_goto_next_entry(handle);
2168 }
2169
2170 return err;
2171}
2172
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002173int32_t mz_zip_locate_first_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002174{
2175 mz_zip *zip = (mz_zip *)handle;
2176 int32_t err = MZ_OK;
2177 int32_t result = 0;
2178
2179 // Search first entry looking for match
2180 err = mz_zip_goto_first_entry(handle);
2181 if (err != MZ_OK)
2182 return err;
2183
2184 result = cb(handle, userdata, &zip->file_info);
2185 if (result == 0)
2186 return MZ_OK;
2187
2188 return mz_zip_locate_next_entry(handle, userdata, cb);
2189}
2190
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002191int32_t mz_zip_locate_next_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002192{
2193 mz_zip *zip = (mz_zip *)handle;
2194 int32_t err = MZ_OK;
2195 int32_t result = 0;
2196
2197 // Search next entries looking for match
2198 err = mz_zip_goto_next_entry(handle);
2199 while (err == MZ_OK)
2200 {
2201 result = cb(handle, userdata, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002202 if (result == 0)
2203 return MZ_OK;
2204
2205 err = mz_zip_goto_next_entry(handle);
2206 }
2207
2208 return err;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002209}
2210
2211/***************************************************************************/
2212
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002213int32_t mz_zip_attrib_is_dir(uint32_t attrib, int32_t version_madeby)
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002214{
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002215 uint32_t posix_attrib = 0;
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002216 uint8_t system = MZ_HOST_SYSTEM(version_madeby);
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002217 int32_t err = MZ_OK;
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002218
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002219 err = mz_zip_attrib_convert(system, attrib, MZ_HOST_SYSTEM_UNIX, &posix_attrib);
2220 if (err == MZ_OK)
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002221 {
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002222 if ((posix_attrib & 0170000) == 0040000) // S_ISDIR
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002223 return MZ_OK;
2224 }
2225
2226 return MZ_EXIST_ERROR;
2227}
2228
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002229int32_t mz_zip_attrib_convert(uint8_t src_sys, uint32_t src_attrib, uint8_t target_sys, uint32_t *target_attrib)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002230{
2231 if (target_attrib == NULL)
2232 return MZ_PARAM_ERROR;
2233
2234 *target_attrib = 0;
2235
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002236 if ((src_sys == MZ_HOST_SYSTEM_MSDOS) || (src_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002237 {
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002238 if ((target_sys == MZ_HOST_SYSTEM_MSDOS) || (target_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002239 {
2240 *target_attrib = src_attrib;
2241 return MZ_OK;
2242 }
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002243 if ((target_sys == MZ_HOST_SYSTEM_UNIX) || (target_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002244 return mz_zip_attrib_win32_to_posix(src_attrib, target_attrib);
2245 }
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002246 else if ((src_sys == MZ_HOST_SYSTEM_UNIX) || (src_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002247 {
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002248 if ((target_sys == MZ_HOST_SYSTEM_UNIX) || (target_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002249 {
Nathan Moinvaziri5e6bac52018-10-08 23:47:02 -07002250 // If high bytes are set, it contains unix specific attributes
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002251 if ((src_attrib >> 16) != 0)
2252 src_attrib >>= 16;
2253
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002254 *target_attrib = src_attrib;
2255 return MZ_OK;
2256 }
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002257 if ((target_sys == MZ_HOST_SYSTEM_MSDOS) || (target_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002258 return mz_zip_attrib_posix_to_win32(src_attrib, target_attrib);
2259 }
2260
2261 return MZ_SUPPORT_ERROR;
2262}
2263
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002264int32_t mz_zip_attrib_posix_to_win32(uint32_t posix_attrib, uint32_t *win32_attrib)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002265{
2266 if (win32_attrib == NULL)
2267 return MZ_PARAM_ERROR;
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002268
2269 *win32_attrib = 0;
2270
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002271 // S_IWUSR | S_IWGRP | S_IWOTH | S_IXUSR | S_IXGRP | S_IXOTH
2272 if ((posix_attrib & 0000333) == 0 && (posix_attrib & 0000444) != 0)
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002273 *win32_attrib |= 0x01; // FILE_ATTRIBUTE_READONLY
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002274 // S_IFDIR
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002275 if ((posix_attrib & 0170000) == 0040000)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002276 *win32_attrib |= 0x10; // FILE_ATTRIBUTE_DIRECTORY
Nathan Moinvazirida4b58f2018-08-20 18:21:26 -07002277 // S_IFREG
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002278 else
2279 *win32_attrib |= 0x80; // FILE_ATTRIBUTE_NORMAL
2280
2281 return MZ_OK;
2282}
2283
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002284int32_t mz_zip_attrib_win32_to_posix(uint32_t win32_attrib, uint32_t *posix_attrib)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002285{
2286 if (posix_attrib == NULL)
2287 return MZ_PARAM_ERROR;
2288
2289 *posix_attrib = 0000444; // S_IRUSR | S_IRGRP | S_IROTH
2290 // FILE_ATTRIBUTE_READONLY
2291 if ((win32_attrib & 0x01) == 0)
2292 *posix_attrib |= 0000222; // S_IWUSR | S_IWGRP | S_IWOTH
2293 // FILE_ATTRIBUTE_DIRECTORY
2294 if ((win32_attrib & 0x10) == 0x10)
2295 *posix_attrib |= 0040111; // S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002296 else
2297 *posix_attrib |= 0100000; // S_IFREG
2298
2299 return MZ_OK;
2300}
2301
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002302/***************************************************************************/
2303
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002304int32_t mz_zip_extrafield_find(void *stream, uint16_t type, uint16_t *length)
2305{
2306 int32_t err = MZ_OK;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002307 uint16_t field_type = 0;
2308 uint16_t field_length = 0;
2309
2310 do
2311 {
2312 err = mz_stream_read_uint16(stream, &field_type);
2313 if (err == MZ_OK)
2314 err = mz_stream_read_uint16(stream, &field_length);
2315 if (err != MZ_OK)
2316 break;
2317
2318 if (type == field_type)
2319 {
2320 if (length != NULL)
2321 *length = field_length;
2322 return MZ_OK;
2323 }
2324
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002325 err = mz_stream_seek(stream, field_length, MZ_SEEK_CUR);
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002326 }
2327 while (err == MZ_OK);
2328
2329 return MZ_EXIST_ERROR;
2330}
2331
2332int32_t mz_zip_extrafield_read(void *stream, uint16_t *type, uint16_t *length)
2333{
2334 int32_t err = MZ_OK;
2335 if (type == NULL || length == NULL)
2336 return MZ_PARAM_ERROR;
2337 err = mz_stream_read_uint16(stream, type);
2338 if (err == MZ_OK)
2339 err = mz_stream_read_uint16(stream, length);
2340 return err;
2341}
2342
Nathan Moinvaziri1d6d1832018-11-10 09:03:55 -08002343int32_t mz_zip_extrafield_write(void *stream, uint16_t type, uint16_t length)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002344{
2345 int32_t err = MZ_OK;
2346 err = mz_stream_write_uint16(stream, type);
2347 if (err == MZ_OK)
2348 err = mz_stream_write_uint16(stream, length);
2349 return err;
2350}
2351
2352/***************************************************************************/
2353
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002354static int32_t mz_zip_invalid_date(const struct tm *ptm)
2355{
2356#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002357 return (!datevalue_in_range(0, 127 + 80, ptm->tm_year) || // 1980-based year, allow 80 extra
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002358 !datevalue_in_range(0, 11, ptm->tm_mon) ||
2359 !datevalue_in_range(1, 31, ptm->tm_mday) ||
2360 !datevalue_in_range(0, 23, ptm->tm_hour) ||
2361 !datevalue_in_range(0, 59, ptm->tm_min) ||
2362 !datevalue_in_range(0, 59, ptm->tm_sec));
2363#undef datevalue_in_range
2364}
2365
2366static void mz_zip_dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
2367{
2368 uint64_t date = (uint64_t)(dos_date >> 16);
2369
Nathan Moinvaziri1d6d1832018-11-10 09:03:55 -08002370 ptm->tm_mday = (uint16_t)(date & 0x1f);
2371 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
2372 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
2373 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
2374 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
2375 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002376 ptm->tm_isdst = -1;
2377}
2378
2379int32_t mz_zip_dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
2380{
2381 if (ptm == NULL)
2382 return MZ_PARAM_ERROR;
2383
2384 mz_zip_dosdate_to_raw_tm(dos_date, ptm);
2385
2386 if (mz_zip_invalid_date(ptm))
2387 {
2388 // Invalid date stored, so don't return it
2389 memset(ptm, 0, sizeof(struct tm));
2390 return MZ_FORMAT_ERROR;
2391 }
2392 return MZ_OK;
2393}
2394
2395time_t mz_zip_dosdate_to_time_t(uint64_t dos_date)
2396{
2397 struct tm ptm;
2398 mz_zip_dosdate_to_raw_tm(dos_date, &ptm);
2399 return mktime(&ptm);
2400}
2401
2402int32_t mz_zip_time_t_to_tm(time_t unix_time, struct tm *ptm)
2403{
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07002404 struct tm *ltm = NULL;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002405 if (ptm == NULL)
2406 return MZ_PARAM_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002407 ltm = localtime(&unix_time); // Returns a 1900-based year
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07002408 if (ltm == NULL)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002409 {
2410 // Invalid date stored, so don't return it
2411 memset(ptm, 0, sizeof(struct tm));
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07002412 return MZ_INTERNAL_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002413 }
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07002414 memcpy(ptm, ltm, sizeof(struct tm));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002415 return MZ_OK;
2416}
2417
2418uint32_t mz_zip_time_t_to_dos_date(time_t unix_time)
2419{
2420 struct tm ptm;
2421 mz_zip_time_t_to_tm(unix_time, &ptm);
2422 return mz_zip_tm_to_dosdate((const struct tm *)&ptm);
2423}
2424
2425uint32_t mz_zip_tm_to_dosdate(const struct tm *ptm)
2426{
Nathan Moinvazirie33916b2018-05-01 13:45:08 -07002427 struct tm fixed_tm;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002428
2429 // Years supported:
2430
2431 // [00, 79] (assumed to be between 2000 and 2079)
2432 // [80, 207] (assumed to be between 1980 and 2107, typical output of old
2433 // software that does 'year-1900' to get a double digit year)
2434 // [1980, 2107] (due to format limitations, only years 1980-2107 can be stored.)
2435
2436 memcpy(&fixed_tm, ptm, sizeof(struct tm));
2437 if (fixed_tm.tm_year >= 1980) // range [1980, 2107]
2438 fixed_tm.tm_year -= 1980;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002439 else if (fixed_tm.tm_year >= 80) // range [80, 207]
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002440 fixed_tm.tm_year -= 80;
2441 else // range [00, 79]
2442 fixed_tm.tm_year += 20;
2443
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002444 if (mz_zip_invalid_date(&fixed_tm))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002445 return 0;
2446
Anand K Mistry57b65f82018-11-09 10:52:19 +11002447 return (((uint32_t)fixed_tm.tm_mday + (32 * ((uint32_t)fixed_tm.tm_mon + 1)) + (512 * (uint32_t)fixed_tm.tm_year)) << 16) |
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002448 (((uint32_t)fixed_tm.tm_sec / 2) + (32 * (uint32_t)fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002449}
2450
2451int32_t mz_zip_ntfs_to_unix_time(uint64_t ntfs_time, time_t *unix_time)
2452{
2453 *unix_time = (time_t)((ntfs_time - 116444736000000000LL) / 10000000);
2454 return MZ_OK;
2455}
2456
2457int32_t mz_zip_unix_to_ntfs_time(time_t unix_time, uint64_t *ntfs_time)
2458{
2459 *ntfs_time = ((uint64_t)unix_time * 10000000) + 116444736000000000LL;
2460 return MZ_OK;
2461}
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002462
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002463/***************************************************************************/
2464
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002465int32_t mz_zip_path_compare(const char *path1, const char *path2, uint8_t ignore_case)
2466{
2467 do
2468 {
2469 if ((*path1 == '\\' && *path2 == '/') ||
2470 (*path2 == '\\' && *path1 == '/'))
2471 {
2472 // Ignore comparison of path slashes
2473 }
2474 else if (ignore_case)
2475 {
2476 if (tolower(*path1) != tolower(*path2))
2477 break;
2478 }
2479 else if (*path1 != *path2)
2480 {
2481 break;
2482 }
2483
2484 path1 += 1;
2485 path2 += 1;
2486 }
2487 while (*path1 != 0 && *path2 != 0);
2488
2489 if (ignore_case)
2490 return (int32_t)(tolower(*path1) - tolower(*path2));
2491
2492 return (int32_t)(*path1 - *path2);
2493}
2494
2495/***************************************************************************/