blob: 1d7404244e9de3775fd55c58a4c526e4184efdba [file] [log] [blame]
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08001/* zip.c -- Zip manipulation
Nathan Moinvaziric98dec42018-08-15 07:37:30 -07002 Version 2.5.0, August 15, 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
13 http://www.winimage.com/zLibDll/minizip.html
14
15 This program is distributed under the terms of the same license as zlib.
16 See the accompanying LICENSE file for the full text of the license.
17*/
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <stdint.h>
22#include <string.h>
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -070023#include <ctype.h>
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -070024#include <time.h>
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080025#include <errno.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"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080029#include "mz_strm.h"
30#ifdef HAVE_AES
31# include "mz_strm_aes.h"
32#endif
33#ifdef HAVE_BZIP2
34# include "mz_strm_bzip.h"
35#endif
Nathan Moinvazirib47b41b2018-07-11 09:44:29 -070036#include "mz_strm_crc32.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080037#ifdef HAVE_LZMA
38# include "mz_strm_lzma.h"
39#endif
Nathan Moinvazirib47b41b2018-07-11 09:44:29 -070040#include "mz_strm_mem.h"
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -070041#ifdef HAVE_PKCRYPT
42# include "mz_strm_pkcrypt.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
50/***************************************************************************/
51
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070052#define MZ_ZIP_MAGIC_LOCALHEADER (0x04034b50)
53#define MZ_ZIP_MAGIC_CENTRALHEADER (0x02014b50)
54#define MZ_ZIP_MAGIC_ENDHEADER (0x06054b50)
55#define MZ_ZIP_MAGIC_ENDHEADER64 (0x06064b50)
56#define MZ_ZIP_MAGIC_ENDLOCHEADER64 (0x07064b50)
57#define MZ_ZIP_MAGIC_DATADESCRIPTOR (0x08074b50)
58
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070059#define MZ_ZIP_SIZE_CD_ITEM (0x2e)
60#define MZ_ZIP_SIZE_CD_LOCATOR64 (0x14)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080061
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070062#define MZ_ZIP_EXTENSION_ZIP64 (0x0001)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -070063#define MZ_ZIP_EXTENSION_NTFS (0x000a)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070064#define MZ_ZIP_EXTENSION_AES (0x9901)
65
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080066/***************************************************************************/
67
68typedef struct mz_zip_s
69{
70 mz_zip_file file_info;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070071 mz_zip_file local_file_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080072
73 void *stream; // main stream
Nathan Moinvaziricda36002017-10-21 09:37:18 -070074 void *cd_stream; // pointer to the stream with the cd
75 void *cd_mem_stream; // memory stream for central directory
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080076 void *compress_stream; // compression stream
77 void *crc32_stream; // crc32 stream
78 void *crypt_stream; // encryption stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070079 void *file_info_stream; // memory stream for storing file info
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -070080 void *local_file_info_stream; // memory stream for storing local file info
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080081
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070082 int32_t open_mode;
83
Nathan Moinvazirifd039e32017-10-22 14:40:39 -070084 uint32_t disk_number_with_cd; // number of the disk with the central dir
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -070085 uint64_t disk_offset_shift; // correction for zips that have wrong offset start of cd
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070086
Nathan Moinvaziricda36002017-10-21 09:37:18 -070087 uint64_t cd_start_pos; // pos of the first file in the central dir stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070088 uint64_t cd_current_pos; // pos of the current file in the central dir
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070089 uint64_t cd_offset; // offset of start of central directory
90 uint64_t cd_size; // size of the central directory
91
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -070092 uint8_t entry_scanned; // entry header information read ok
93 uint8_t entry_opened; // entry is open for read/write
94 uint8_t entry_raw; // entry opened with raw mode
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070095
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070096 int64_t number_entry;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070097
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -070098 uint16_t version_madeby;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070099 char *comment;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800100} mz_zip;
101
102/***************************************************************************/
103
Nathan Moinvazirie824da82018-07-26 17:53:15 -0700104// Locate the end of central directory of a zip file (at the end, just before the global comment)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700105static int32_t mz_zip_search_eocd(void *stream, uint64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800106{
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700107 uint8_t buf[1024 + 4];
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700108 int64_t file_size = 0;
109 int64_t back_read = 0;
110 int64_t max_back = UINT16_MAX; // maximum size of global comment
111 int32_t read_size = sizeof(buf);
112 int64_t read_pos = 0;
113 int32_t i = 0;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700114
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700115 *central_pos = 0;
116
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700117 if (mz_stream_seek(stream, 0, MZ_SEEK_END) != MZ_OK)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700118 return MZ_STREAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800119
120 file_size = mz_stream_tell(stream);
121
122 if (max_back > file_size)
123 max_back = file_size;
124
125 while (back_read < max_back)
126 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700127 back_read += (sizeof(buf) - 4);
128 if (back_read > max_back)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800129 back_read = max_back;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800130
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700131 read_pos = file_size - back_read;
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700132 if (read_size > (file_size - read_pos))
133 read_size = (uint32_t)(file_size - read_pos);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800134
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700135 if (mz_stream_seek(stream, read_pos, MZ_SEEK_SET) != MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800136 break;
137 if (mz_stream_read(stream, buf, read_size) != read_size)
138 break;
139
140 for (i = read_size - 3; (i--) > 0;)
141 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700142 if (((*(buf + i)) == (MZ_ZIP_MAGIC_ENDHEADER & 0xff)) &&
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700143 ((*(buf + i + 1)) == (MZ_ZIP_MAGIC_ENDHEADER >> 8 & 0xff)) &&
144 ((*(buf + i + 2)) == (MZ_ZIP_MAGIC_ENDHEADER >> 16 & 0xff)) &&
145 ((*(buf + i + 3)) == (MZ_ZIP_MAGIC_ENDHEADER >> 24 & 0xff)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800146 {
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700147 *central_pos = read_pos + i;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700148 return MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800149 }
150 }
151
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700152 if (*central_pos != 0)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800153 break;
154 }
155
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700156 return MZ_EXIST_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800157}
158
Nathan Moinvazirie824da82018-07-26 17:53:15 -0700159// Locate the end of central directory 64 of a zip file
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700160static int32_t mz_zip_search_zip64_eocd(void *stream, const uint64_t end_central_offset, uint64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800161{
162 uint64_t offset = 0;
163 uint32_t value32 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700164 int32_t err = MZ_OK;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700165
166
167 *central_pos = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800168
169 // Zip64 end of central directory locator
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700170 err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800171 // Read locator signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700172 if (err == MZ_OK)
173 {
174 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700175 if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700176 err = MZ_FORMAT_ERROR;
177 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800178 // Number of the disk with the start of the zip64 end of central directory
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700179 if (err == MZ_OK)
180 err = mz_stream_read_uint32(stream, &value32);
181 // Relative offset of the zip64 end of central directory record8
182 if (err == MZ_OK)
183 err = mz_stream_read_uint64(stream, &offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800184 // Total number of disks
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700185 if (err == MZ_OK)
186 err = mz_stream_read_uint32(stream, &value32);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800187 // Goto end of central directory record
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700188 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700189 err = mz_stream_seek(stream, offset, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800190 // The signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700191 if (err == MZ_OK)
192 {
193 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700194 if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700195 err = MZ_FORMAT_ERROR;
196 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800197
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700198 if (err == MZ_OK)
199 *central_pos = offset;
200
201 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800202}
203
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700204static int32_t mz_zip_read_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800205{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700206 mz_zip *zip = (mz_zip *)handle;
207 int64_t number_entry_cd = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700208 uint64_t number_entry_cd64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800209 uint64_t number_entry = 0;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700210 uint64_t eocd_pos = 0;
211 uint64_t eocd_pos64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800212 uint16_t value16 = 0;
213 uint32_t value32 = 0;
214 uint64_t value64 = 0;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700215 uint16_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700216 int32_t err = MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800217
218
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800219 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700220 return MZ_PARAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800221
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700222 // Read and cache central directory records
Nathan Moinvaziri77687f32018-08-09 16:29:26 -0700223 err = mz_zip_search_eocd(zip->stream, &eocd_pos);
224 if (err == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800225 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700226 // Read end of central directory info
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700227 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700228 // The signature, already checked
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800229 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700230 err = mz_stream_read_uint32(zip->stream, &value32);
231 // Number of this disk
232 if (err == MZ_OK)
233 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700234 // Number of the disk with the start of the central directory
235 if (err == MZ_OK)
236 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700237 zip->disk_number_with_cd = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700238 // Total number of entries in the central dir on this disk
239 if (err == MZ_OK)
240 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700241 zip->number_entry = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700242 // Total number of entries in the central dir
243 if (err == MZ_OK)
244 err = mz_stream_read_uint16(zip->stream, &value16);
245 number_entry_cd = value16;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700246 if (number_entry_cd != zip->number_entry)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700247 err = MZ_FORMAT_ERROR;
248 // Size of the central directory
249 if (err == MZ_OK)
250 err = mz_stream_read_uint32(zip->stream, &value32);
251 if (err == MZ_OK)
252 zip->cd_size = value32;
253 // Offset of start of central directory with respect to the starting disk number
254 if (err == MZ_OK)
255 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -0700256 if (err == MZ_OK)
257 zip->cd_offset = value32;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700258 // Zip file global comment length
259 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700260 err = mz_stream_read_uint16(zip->stream, &comment_size);
Nathan Moinvaziri51bf61d2018-07-26 16:41:54 -0700261 if ((err == MZ_OK) && (comment_size > 0))
262 {
263 zip->comment = (char *)MZ_ALLOC(comment_size + 1);
264 if (zip->comment)
265 {
266 if (mz_stream_read(zip->stream, zip->comment, comment_size) != comment_size)
267 err = MZ_STREAM_ERROR;
268 zip->comment[comment_size] = 0;
269 }
270 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800271
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700272 if ((err == MZ_OK) && ((number_entry_cd == UINT16_MAX) || (zip->cd_offset == UINT32_MAX)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800273 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700274 // Format should be Zip64, as the central directory or file size is too large
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700275 if (mz_zip_search_zip64_eocd(zip->stream, eocd_pos, &eocd_pos64) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800276 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700277 eocd_pos = eocd_pos64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700278
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700279 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700280 // The signature, already checked
281 if (err == MZ_OK)
282 err = mz_stream_read_uint32(zip->stream, &value32);
283 // Size of zip64 end of central directory record
284 if (err == MZ_OK)
285 err = mz_stream_read_uint64(zip->stream, &value64);
286 // Version made by
287 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700288 err = mz_stream_read_uint16(zip->stream, &zip->version_madeby);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700289 // Version needed to extract
290 if (err == MZ_OK)
291 err = mz_stream_read_uint16(zip->stream, &value16);
292 // Number of this disk
293 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700294 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700295 // Number of the disk with the start of the central directory
296 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700297 err = mz_stream_read_uint32(zip->stream, &zip->disk_number_with_cd);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700298 // Total number of entries in the central directory on this disk
299 if (err == MZ_OK)
300 err = mz_stream_read_uint64(zip->stream, &number_entry);
301 // Total number of entries in the central directory
302 if (err == MZ_OK)
303 err = mz_stream_read_uint64(zip->stream, &number_entry_cd64);
304 if (number_entry == UINT32_MAX)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700305 zip->number_entry = number_entry_cd64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700306 // Size of the central directory
307 if (err == MZ_OK)
308 err = mz_stream_read_uint64(zip->stream, &zip->cd_size);
309 // Offset of start of central directory with respect to the starting disk number
310 if (err == MZ_OK)
311 err = mz_stream_read_uint64(zip->stream, &zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800312 }
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700313 else if ((zip->number_entry == UINT16_MAX) || (number_entry_cd != zip->number_entry) ||
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700314 (zip->cd_size == UINT16_MAX) || (zip->cd_offset == UINT32_MAX))
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700315 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700316 err = MZ_FORMAT_ERROR;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700317 }
318 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800319 }
320
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700321 if (err == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800322 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700323 if (eocd_pos < zip->cd_offset + zip->cd_size)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700324 err = MZ_FORMAT_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800325 }
326
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -0700327 if (err == MZ_OK)
328 {
329 // Verify central directory signature exists at offset
330 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
331 if (err == MZ_OK)
332 err = mz_stream_read_uint32(zip->stream, &value32);
333 if (value32 != MZ_ZIP_MAGIC_CENTRALHEADER)
334 {
335 // If not found attempt to seek backward to find it
336 err = mz_stream_seek(zip->stream, eocd_pos - zip->cd_size, MZ_SEEK_SET);
337 if (err == MZ_OK)
338 err = mz_stream_read_uint32(zip->stream, &value32);
339 if (value32 == MZ_ZIP_MAGIC_CENTRALHEADER)
340 {
341 // If found compensate for incorrect locations
342 value64 = zip->cd_offset;
343 zip->cd_offset = eocd_pos - zip->cd_size;
344 zip->disk_offset_shift = zip->cd_offset - value64;
345 }
346 }
347 }
348
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800349 return err;
350}
351
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700352static int32_t mz_zip_write_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800353{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700354 mz_zip *zip = (mz_zip *)handle;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800355 uint16_t comment_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700356 uint64_t zip64_eocd_pos_inzip = 0;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700357 int64_t disk_number = 0;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700358 int64_t disk_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700359 int32_t err = MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800360
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700361
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700362 if (zip == NULL)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800363 return MZ_PARAM_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000364
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700365 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700366 zip->disk_number_with_cd = (uint32_t)disk_number;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700367 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_SIZE, &disk_size) == MZ_OK && disk_size > 0)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700368 zip->disk_number_with_cd += 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700369 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800370
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700371 zip->cd_offset = mz_stream_tell(zip->stream);
372 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_END);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700373 zip->cd_size = (uint32_t)mz_stream_tell(zip->cd_mem_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700374 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_SET);
Viktor Szakats915b82e2018-04-24 10:02:39 +0000375
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700376 err = mz_stream_copy(zip->stream, zip->cd_mem_stream, (int32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800377
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800378 // Write the ZIP64 central directory header
juanii3f59ffc2018-02-08 12:44:17 -0300379 if (zip->cd_offset >= UINT32_MAX || zip->number_entry > UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800380 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700381 zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800382
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700383 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800384
385 // Size of this 'zip64 end of central directory'
386 if (err == MZ_OK)
387 err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
388 // Version made by
389 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700390 err = mz_stream_write_uint16(zip->stream, zip->version_madeby);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800391 // Version needed
392 if (err == MZ_OK)
393 err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
394 // Number of this disk
395 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700396 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800397 // Number of the disk with the start of the central directory
398 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700399 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800400 // Total number of entries in the central dir on this disk
401 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700402 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800403 // Total number of entries in the central dir
404 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700405 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800406 // Size of the central directory
407 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700408 err = mz_stream_write_uint64(zip->stream, (uint64_t)zip->cd_size);
409 // Offset of start of central directory with respect to the starting disk number
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800410 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700411 err = mz_stream_write_uint64(zip->stream, zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800412 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700413 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700414
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800415 // Number of the disk with the start of the central directory
416 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700417 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800418 // Relative offset to the end of zip64 central directory
419 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700420 err = mz_stream_write_uint64(zip->stream, zip64_eocd_pos_inzip);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800421 // Number of the disk with the start of the central directory
422 if (err == MZ_OK)
juaniic65486d2018-02-08 17:07:07 -0300423 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd + 1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800424 }
425
426 // Write the central directory header
427
Viktor Szakats915b82e2018-04-24 10:02:39 +0000428 // Signature
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800429 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700430 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800431 // Number of this disk
432 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700433 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800434 // Number of the disk with the start of the central directory
435 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700436 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800437 // Total number of entries in the central dir on this disk
438 if (err == MZ_OK)
439 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700440 if (zip->number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800441 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
442 else
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700443 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800444 }
445 // Total number of entries in the central dir
446 if (err == MZ_OK)
447 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700448 if (zip->number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800449 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
450 else
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700451 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800452 }
453 // Size of the central directory
454 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700455 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800456 // Offset of start of central directory with respect to the starting disk number
457 if (err == MZ_OK)
458 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700459 if (zip->cd_offset >= UINT32_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800460 err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
461 else
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700462 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800463 }
464
465 // Write global comment
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700466 if (zip->comment != NULL)
467 comment_size = (uint16_t)strlen(zip->comment);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800468 if (err == MZ_OK)
469 err = mz_stream_write_uint16(zip->stream, comment_size);
470 if (err == MZ_OK)
471 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700472 if (mz_stream_write(zip->stream, zip->comment, comment_size) != comment_size)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800473 err = MZ_STREAM_ERROR;
474 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700475 return err;
476}
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800477
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700478void *mz_zip_create(void **handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700479{
480 mz_zip *zip = NULL;
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700481
482 zip = (mz_zip *)MZ_ALLOC(sizeof(mz_zip));
483 if (zip != NULL)
484 {
485 memset(zip, 0, sizeof(mz_zip));
486 }
487 if (handle != NULL)
488 *handle = zip;
489
490 return zip;
491}
492
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700493void mz_zip_delete(void **handle)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700494{
495 mz_zip *zip = NULL;
496 if (handle == NULL)
497 return;
498 zip = (mz_zip *)*handle;
499 if (zip != NULL)
500 {
501 MZ_FREE(zip);
502 }
503 *handle = NULL;
504}
505
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700506int32_t mz_zip_open(void *handle, void *stream, int32_t mode)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700507{
508 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700509 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700510
511
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700512 if (zip == NULL)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700513 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700514
515 zip->stream = stream;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700516
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800517 if (mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700518 {
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700519 mz_stream_mem_create(&zip->cd_mem_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700520 mz_stream_mem_open(zip->cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700521
522 zip->cd_stream = zip->cd_mem_stream;
523 }
524 else
525 {
526 zip->cd_stream = stream;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700527 }
528
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800529 if ((mode & MZ_OPEN_MODE_READ) || (mode & MZ_OPEN_MODE_APPEND))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700530 {
Nathan Moinvaziri446d0622018-07-16 19:17:46 -0700531 if ((mode & MZ_OPEN_MODE_CREATE) == 0)
532 err = mz_zip_read_cd(zip);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700533
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700534 if ((err == MZ_OK) && (mode & MZ_OPEN_MODE_APPEND))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700535 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700536 if (zip->cd_size > 0)
537 {
538 // Store central directory in memory
539 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
540 if (err == MZ_OK)
541 err = mz_stream_copy(zip->cd_mem_stream, zip->stream, (uint32_t)zip->cd_size);
542 if (err == MZ_OK)
543 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
544 }
545 else
546 {
547 // If no central directory, append new zip to end of file
548 err = mz_stream_seek(zip->stream, 0, MZ_SEEK_END);
549 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700550 }
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700551 else
552 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700553 zip->cd_start_pos = zip->cd_offset;
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700554 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700555 }
556
557 if (err == MZ_OK)
558 {
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700559 // Memory streams used to store variable length file info data
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700560 mz_stream_mem_create(&zip->file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700561 mz_stream_mem_open(zip->file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700562
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700563 mz_stream_mem_create(&zip->local_file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700564 mz_stream_mem_open(zip->local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700565 }
566
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700567 if (err != MZ_OK)
568 {
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800569 mz_zip_close(zip);
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700570 return err;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700571 }
572
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800573 zip->open_mode = mode;
574
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700575 return err;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700576}
577
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700578int32_t mz_zip_close(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700579{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700580 mz_zip *zip = (mz_zip *)handle;
581 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700582
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700583 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700584 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700585
Nathan Moinvaziri77687f32018-08-09 16:29:26 -0700586 if (mz_zip_entry_is_open(handle) == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700587 {
588 err = mz_zip_entry_close(handle);
589 if (err != MZ_OK)
590 return err;
591 }
592
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700593 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700594 err = mz_zip_write_cd(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700595
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700596 if (zip->cd_mem_stream != NULL)
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700597 {
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700598 mz_stream_close(zip->cd_mem_stream);
599 mz_stream_delete(&zip->cd_mem_stream);
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700600 }
601
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800602 if (zip->file_info_stream != NULL)
603 {
604 mz_stream_mem_close(zip->file_info_stream);
605 mz_stream_mem_delete(&zip->file_info_stream);
606 }
607 if (zip->local_file_info_stream != NULL)
608 {
609 mz_stream_mem_close(zip->local_file_info_stream);
610 mz_stream_mem_delete(&zip->local_file_info_stream);
611 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700612
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700613 if (zip->comment)
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700614 MZ_FREE(zip->comment);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700615
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800616 return err;
617}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700618
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700619int32_t mz_zip_get_comment(void *handle, const char **comment)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700620{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700621 mz_zip *zip = (mz_zip *)handle;
622 if (zip == NULL || comment == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700623 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700624 if (zip->comment == NULL)
625 return MZ_EXIST_ERROR;
626 *comment = zip->comment;
627 return MZ_OK;
628}
629
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700630int32_t mz_zip_set_comment(void *handle, const char *comment)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700631{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700632 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700633 uint16_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700634 if (zip == NULL || comment == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700635 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700636 if (zip->comment != NULL)
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700637 MZ_FREE(zip->comment);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700638 comment_size = (uint16_t)(strlen(comment) + 1);
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700639 zip->comment = (char *)MZ_ALLOC(comment_size);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700640 strncpy(zip->comment, comment, comment_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700641 return MZ_OK;
642}
643
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700644int32_t mz_zip_get_version_madeby(void *handle, uint16_t *version_madeby)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700645{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700646 mz_zip *zip = (mz_zip *)handle;
647 if (zip == NULL || version_madeby == NULL)
648 return MZ_PARAM_ERROR;
649 *version_madeby = zip->version_madeby;
650 return MZ_OK;
651}
652
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700653int32_t mz_zip_set_version_madeby(void *handle, uint16_t version_madeby)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700654{
655 mz_zip *zip = (mz_zip *)handle;
656 if (zip == NULL)
657 return MZ_PARAM_ERROR;
658 zip->version_madeby = version_madeby;
659 return MZ_OK;
660}
661
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700662// Get info about the current file in the zip file
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700663static int32_t mz_zip_entry_read_header(void *stream, uint8_t local, mz_zip_file *file_info, void *file_info_stream)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700664{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700665 uint64_t ntfs_time = 0;
666 uint32_t reserved = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700667 uint32_t magic = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700668 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700669 uint32_t extra_pos = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700670 uint32_t extra_data_size_read = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700671 uint16_t extra_header_id = 0;
672 uint16_t extra_data_size = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700673 uint16_t ntfs_attrib_id = 0;
674 uint16_t ntfs_attrib_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700675 uint16_t value16 = 0;
676 uint32_t value32 = 0;
677 uint64_t value64 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700678 int64_t max_seek = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700679 int64_t seek = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700680 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700681
682
683 memset(file_info, 0, sizeof(mz_zip_file));
684
685 // Check the magic
686 err = mz_stream_read_uint32(stream, &magic);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700687 if (err == MZ_END_OF_STREAM)
688 err = MZ_END_OF_LIST;
689 else if (magic == MZ_ZIP_MAGIC_ENDHEADER || magic == MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700690 err = MZ_END_OF_LIST;
691 else if ((local) && (magic != MZ_ZIP_MAGIC_LOCALHEADER))
692 err = MZ_FORMAT_ERROR;
693 else if ((!local) && (magic != MZ_ZIP_MAGIC_CENTRALHEADER))
694 err = MZ_FORMAT_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000695
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700696 // Read header fields
697 if (err == MZ_OK)
698 {
699 if (!local)
700 err = mz_stream_read_uint16(stream, &file_info->version_madeby);
701 if (err == MZ_OK)
702 err = mz_stream_read_uint16(stream, &file_info->version_needed);
703 if (err == MZ_OK)
704 err = mz_stream_read_uint16(stream, &file_info->flag);
705 if (err == MZ_OK)
706 err = mz_stream_read_uint16(stream, &file_info->compression_method);
707 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700708 {
709 err = mz_stream_read_uint32(stream, &dos_date);
710 file_info->modified_date = mz_zip_dosdate_to_time_t(dos_date);
711 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700712 if (err == MZ_OK)
713 err = mz_stream_read_uint32(stream, &file_info->crc);
714 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700715 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700716 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700717 file_info->compressed_size = value32;
718 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700719 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700720 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700721 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700722 file_info->uncompressed_size = value32;
723 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700724 if (err == MZ_OK)
725 err = mz_stream_read_uint16(stream, &file_info->filename_size);
726 if (err == MZ_OK)
727 err = mz_stream_read_uint16(stream, &file_info->extrafield_size);
728 if (!local)
729 {
730 if (err == MZ_OK)
731 err = mz_stream_read_uint16(stream, &file_info->comment_size);
732 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700733 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700734 err = mz_stream_read_uint16(stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700735 file_info->disk_number = value16;
736 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700737 if (err == MZ_OK)
738 err = mz_stream_read_uint16(stream, &file_info->internal_fa);
739 if (err == MZ_OK)
740 err = mz_stream_read_uint32(stream, &file_info->external_fa);
741 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700742 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700743 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700744 file_info->disk_offset = value32;
745 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700746 }
747 }
748
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700749 max_seek = file_info->filename_size + file_info->extrafield_size + file_info->comment_size + 3;
750 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700751 err = mz_stream_seek(file_info_stream, max_seek, MZ_SEEK_SET);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700752 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700753 err = mz_stream_seek(file_info_stream, 0, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700754
755 if ((err == MZ_OK) && (file_info->filename_size > 0))
756 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700757 mz_stream_mem_get_buffer(file_info_stream, (const void **)&file_info->filename);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700758
759 err = mz_stream_copy(file_info_stream, stream, file_info->filename_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700760 if (err == MZ_OK)
761 err = mz_stream_write_uint8(file_info_stream, 0);
762
763 seek += file_info->filename_size + 1;
764 }
765
766 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
767 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700768 mz_stream_mem_get_buffer_at(file_info_stream, seek, (const void **)&file_info->extrafield);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700769
770 err = mz_stream_copy(file_info_stream, stream, file_info->extrafield_size);
771 if (err == MZ_OK)
772 err = mz_stream_write_uint8(file_info_stream, 0);
773
774 // Seek back and parse the extra field
775 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700776 err = mz_stream_seek(file_info_stream, seek, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700777
778 seek += file_info->extrafield_size + 1;
779
780 while ((err == MZ_OK) && (extra_pos < file_info->extrafield_size))
781 {
782 err = mz_stream_read_uint16(file_info_stream, &extra_header_id);
783 if (err == MZ_OK)
784 err = mz_stream_read_uint16(file_info_stream, &extra_data_size);
785
786 // ZIP64 extra field
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700787 if (extra_header_id == MZ_ZIP_EXTENSION_ZIP64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700788 {
789 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
790 err = mz_stream_read_uint64(file_info_stream, &file_info->uncompressed_size);
791 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
792 err = mz_stream_read_uint64(file_info_stream, &file_info->compressed_size);
793 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700794 err = mz_stream_read_uint64(file_info_stream, &file_info->disk_offset);
juanii4ae79922018-02-11 14:29:36 -0300795 if ((err == MZ_OK) && (file_info->disk_number == UINT16_MAX))
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700796 err = mz_stream_read_uint32(file_info_stream, &file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700797 }
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700798 // NTFS extra field
799 else if (extra_header_id == MZ_ZIP_EXTENSION_NTFS)
800 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700801 if (err == MZ_OK)
802 err = mz_stream_read_uint32(file_info_stream, &reserved);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700803 extra_data_size_read = 4;
804
805 while ((err == MZ_OK) && (extra_data_size_read < extra_data_size))
806 {
807 err = mz_stream_read_uint16(file_info_stream, &ntfs_attrib_id);
808 if (err == MZ_OK)
809 err = mz_stream_read_uint16(file_info_stream, &ntfs_attrib_size);
810
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700811 if ((err == MZ_OK) && (ntfs_attrib_id == 0x01) && (ntfs_attrib_size == 24))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700812 {
813 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
814 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->modified_date);
815
juanii7063b0e2018-02-11 13:56:21 -0300816 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700817 {
818 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
819 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->accessed_date);
820 }
juanii7063b0e2018-02-11 13:56:21 -0300821 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700822 {
823 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
824 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->creation_date);
825 }
826 }
827 else
828 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700829 if (err == MZ_OK)
830 err = mz_stream_seek(file_info_stream, ntfs_attrib_size, MZ_SEEK_CUR);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700831 }
832
833 extra_data_size_read += ntfs_attrib_size + 4;
834 }
835 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700836#ifdef HAVE_AES
837 // AES extra field
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700838 else if (extra_header_id == MZ_ZIP_EXTENSION_AES)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700839 {
840 uint8_t value8 = 0;
841 // Verify version info
842 err = mz_stream_read_uint16(file_info_stream, &value16);
843 // Support AE-1 and AE-2
844 if (value16 != 1 && value16 != 2)
845 err = MZ_FORMAT_ERROR;
846 file_info->aes_version = value16;
847 if (err == MZ_OK)
848 err = mz_stream_read_uint8(file_info_stream, &value8);
849 if ((char)value8 != 'A')
850 err = MZ_FORMAT_ERROR;
851 if (err == MZ_OK)
852 err = mz_stream_read_uint8(file_info_stream, &value8);
853 if ((char)value8 != 'E')
854 err = MZ_FORMAT_ERROR;
855 // Get AES encryption strength and actual compression method
856 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700857 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700858 err = mz_stream_read_uint8(file_info_stream, &value8);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700859 file_info->aes_encryption_mode = value8;
860 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700861 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700862 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700863 err = mz_stream_read_uint16(file_info_stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700864 file_info->compression_method = value16;
865 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700866 }
867#endif
868 else
869 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700870 err = mz_stream_seek(file_info_stream, extra_data_size, MZ_SEEK_CUR);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700871 }
872
873 extra_pos += 4 + extra_data_size;
874 }
875 }
876
877 if ((err == MZ_OK) && (file_info->comment_size > 0))
878 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700879 mz_stream_mem_get_buffer_at(file_info_stream, seek, (const void **)&file_info->comment);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700880
881 err = mz_stream_copy(file_info_stream, stream, file_info->comment_size);
882 if (err == MZ_OK)
883 err = mz_stream_write_uint8(file_info_stream, 0);
884 }
885
886 return err;
887}
888
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700889static int32_t mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700890{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700891 uint64_t ntfs_time = 0;
892 uint32_t reserved = 0;
893 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700894 uint16_t extrafield_size = 0;
895 uint16_t extrafield_zip64_size = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700896 uint16_t extrafield_ntfs_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700897 uint16_t filename_size = 0;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700898 uint16_t filename_length = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700899 uint16_t comment_size = 0;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700900 uint16_t version_needed = 0;
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700901 uint16_t field_type = 0;
902 uint16_t field_length = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700903 int32_t err = MZ_OK;
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700904 int32_t err_mem = MZ_OK;
905 uint8_t zip64 = 0;
906 uint8_t skip_aes = 0;
907 void *extrafield_ms = NULL;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700908
909 if (file_info == NULL)
910 return MZ_PARAM_ERROR;
911
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700912 // Calculate extra field sizes
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700913 if (file_info->uncompressed_size >= UINT32_MAX)
914 extrafield_zip64_size += 8;
915 if (file_info->compressed_size >= UINT32_MAX)
916 extrafield_zip64_size += 8;
917 if (file_info->disk_offset >= UINT32_MAX)
918 extrafield_zip64_size += 8;
919
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700920 if (file_info->zip64 == MZ_ZIP64_AUTO)
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700921 {
922 // If uncompressed size is unknown, assume zip64 for 64-bit data descriptors
923 zip64 = (local && file_info->uncompressed_size == 0) || (extrafield_zip64_size > 0);
924 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700925 else if (file_info->zip64 == MZ_ZIP64_FORCE)
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700926 {
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700927 zip64 = 1;
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700928 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700929 else if (file_info->zip64 == MZ_ZIP64_DISABLE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700930 {
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700931 // Zip64 extension is required to zip file
932 if (extrafield_zip64_size > 0)
933 return MZ_PARAM_ERROR;
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700934 }
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700935
936 if (zip64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700937 {
938 extrafield_size += 4;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700939 extrafield_size += extrafield_zip64_size;
940 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700941
942 // Calculate extra field size and check for duplicates
943 if (file_info->extrafield_size > 0)
944 {
945 mz_stream_mem_create(&extrafield_ms);
946 mz_stream_mem_set_buffer(extrafield_ms, (void *)file_info->extrafield, file_info->extrafield_size);
947
948 do
949 {
950 err_mem = mz_stream_read_uint16(extrafield_ms, &field_type);
951 if (err_mem == MZ_OK)
952 err_mem = mz_stream_read_uint16(extrafield_ms, &field_length);
953 if (err_mem != MZ_OK)
954 break;
955
956 // Prefer incoming ntfs, aes extensions over ours
957 if (field_type == MZ_ZIP_EXTENSION_AES)
958 skip_aes = 1;
959
960 // Prefer our zip64, ntfs extension over incoming
961 if (field_type != MZ_ZIP_EXTENSION_ZIP64 && field_type != MZ_ZIP_EXTENSION_NTFS)
962 extrafield_size += 4 + field_length;
963
964 if (err_mem == MZ_OK)
965 err_mem = mz_stream_seek(extrafield_ms, field_length, SEEK_CUR);
966 }
967 while (err_mem == MZ_OK);
968 }
969
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700970#ifdef HAVE_AES
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700971 if (!skip_aes)
972 {
973 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
974 extrafield_size += 4 + 7;
975 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700976#endif
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700977 // NTFS timestamps
Nathan Moinvazirid9e159a2018-02-13 15:30:30 -0800978 if ((file_info->modified_date != 0) &&
979 (file_info->accessed_date != 0) &&
980 (file_info->creation_date != 0))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700981 {
juanii3679a3d2018-02-11 13:55:38 -0300982 extrafield_ntfs_size += 8 + 8 + 8 + 4 + 2 + 2;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700983 extrafield_size += 4;
984 extrafield_size += extrafield_ntfs_size;
985 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700986
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700987 if (local)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700988 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700989 else
990 {
991 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
992 if (err == MZ_OK)
993 err = mz_stream_write_uint16(stream, file_info->version_madeby);
994 }
995
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700996 // Calculate version needed to extract
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700997 if (err == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700998 {
999 version_needed = file_info->version_needed;
1000 if (version_needed == 0)
1001 {
1002 version_needed = 20;
1003 if (zip64)
1004 version_needed = 45;
1005#ifdef HAVE_AES
1006 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
1007 version_needed = 51;
1008#endif
1009#ifdef HAVE_LZMA
1010 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
1011 version_needed = 63;
1012#endif
1013 }
1014 err = mz_stream_write_uint16(stream, version_needed);
1015 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001016 if (err == MZ_OK)
1017 err = mz_stream_write_uint16(stream, file_info->flag);
1018 if (err == MZ_OK)
1019 {
1020#ifdef HAVE_AES
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001021 if (file_info->aes_version)
1022 err = mz_stream_write_uint16(stream, MZ_COMPRESS_METHOD_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001023 else
1024#endif
1025 err = mz_stream_write_uint16(stream, file_info->compression_method);
1026 }
1027 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001028 {
Nathan Moinvaziri17cdaec2017-10-26 10:18:41 -07001029 if (file_info->modified_date != 0)
1030 dos_date = mz_zip_time_t_to_dos_date(file_info->modified_date);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001031 err = mz_stream_write_uint32(stream, dos_date);
1032 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001033
1034 if (err == MZ_OK)
1035 err = mz_stream_write_uint32(stream, file_info->crc); // crc
1036 if (err == MZ_OK)
1037 {
1038 if (file_info->compressed_size >= UINT32_MAX) // compr size
1039 err = mz_stream_write_uint32(stream, UINT32_MAX);
1040 else
1041 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
1042 }
1043 if (err == MZ_OK)
1044 {
1045 if (file_info->uncompressed_size >= UINT32_MAX) // uncompr size
1046 err = mz_stream_write_uint32(stream, UINT32_MAX);
1047 else
1048 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
1049 }
1050
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001051 filename_length = (uint16_t)strlen(file_info->filename);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001052 if (err == MZ_OK)
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001053 {
1054 filename_size = filename_length;
1055 if (mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -07001056 {
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001057 if ((file_info->filename[filename_length - 1] == '/') ||
Nathan Moinvaziri46f71432018-05-03 17:24:32 -07001058 (file_info->filename[filename_length - 1] == '\\'))
1059 filename_length -= 1;
1060 else
1061 filename_size += 1;
1062 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001063 err = mz_stream_write_uint16(stream, filename_size);
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001064 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001065 if (err == MZ_OK)
1066 err = mz_stream_write_uint16(stream, extrafield_size);
1067
1068 if (!local)
1069 {
1070 if (file_info->comment != NULL)
1071 comment_size = (uint16_t)strlen(file_info->comment);
1072 if (err == MZ_OK)
1073 err = mz_stream_write_uint16(stream, comment_size);
1074 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001075 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001076 if (err == MZ_OK)
1077 err = mz_stream_write_uint16(stream, file_info->internal_fa);
1078 if (err == MZ_OK)
1079 err = mz_stream_write_uint32(stream, file_info->external_fa);
1080 if (err == MZ_OK)
1081 {
1082 if (file_info->disk_offset >= UINT32_MAX)
1083 err = mz_stream_write_uint32(stream, UINT32_MAX);
1084 else
1085 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
1086 }
1087 }
1088
1089 if (err == MZ_OK)
1090 {
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001091 if (mz_stream_write(stream, file_info->filename, filename_length) != filename_length)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001092 err = MZ_STREAM_ERROR;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001093 if (err == MZ_OK)
1094 {
Nathan Moinvaziri240b3b62018-05-02 13:38:14 -07001095 // Ensure that directories have a slash appended to them for compatibility
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001096 if (mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK)
1097 err = mz_stream_write_uint8(stream, '/');
1098 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001099 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001100
1101 if (file_info->extrafield_size > 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001102 {
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001103 err_mem = mz_stream_mem_seek(extrafield_ms, 0, SEEK_SET);
1104 while (err == MZ_OK && err_mem == MZ_OK)
1105 {
1106 err_mem = mz_stream_read_uint16(extrafield_ms, &field_type);
1107 if (err_mem == MZ_OK)
1108 err_mem = mz_stream_read_uint16(extrafield_ms, &field_length);
1109 if (err_mem != MZ_OK)
1110 break;
1111
1112 // Prefer our zip 64, ntfs extensions over incoming
1113 if (field_type == MZ_ZIP_EXTENSION_ZIP64 || field_type == MZ_ZIP_EXTENSION_NTFS)
1114 {
1115 err_mem = mz_stream_seek(extrafield_ms, field_length, SEEK_CUR);
1116 continue;
1117 }
1118
1119 err = mz_stream_write_uint16(stream, field_type);
1120 if (err == MZ_OK)
1121 err = mz_stream_write_uint16(stream, field_length);
1122 if (err == MZ_OK)
1123 err = mz_stream_copy(stream, extrafield_ms, field_length);
1124 }
1125
1126 mz_stream_mem_delete(&extrafield_ms);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001127 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001128
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001129 // Add ZIP64 extra info header to central directory
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001130 if ((err == MZ_OK) && (zip64))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001131 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001132 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_ZIP64);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001133 if (err == MZ_OK)
1134 err = mz_stream_write_uint16(stream, extrafield_zip64_size);
1135 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
1136 err = mz_stream_write_uint64(stream, file_info->uncompressed_size);
1137 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
1138 err = mz_stream_write_uint64(stream, file_info->compressed_size);
1139 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
1140 err = mz_stream_write_uint64(stream, file_info->disk_offset);
1141 }
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001142 // Write NTFS timestamps
1143 if ((err == MZ_OK) && (extrafield_ntfs_size > 0))
1144 {
1145 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_NTFS);
1146 if (err == MZ_OK)
1147 err = mz_stream_write_uint16(stream, extrafield_ntfs_size);
1148 if (err == MZ_OK)
1149 err = mz_stream_write_uint32(stream, reserved);
1150 if (err == MZ_OK)
1151 err = mz_stream_write_uint16(stream, 0x01);
1152 if (err == MZ_OK)
1153 err = mz_stream_write_uint16(stream, extrafield_ntfs_size - 8);
juanii3679a3d2018-02-11 13:55:38 -03001154 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001155 {
1156 mz_zip_unix_to_ntfs_time(file_info->modified_date, &ntfs_time);
1157 err = mz_stream_write_uint64(stream, ntfs_time);
1158 }
juanii3679a3d2018-02-11 13:55:38 -03001159 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001160 {
1161 mz_zip_unix_to_ntfs_time(file_info->accessed_date, &ntfs_time);
1162 err = mz_stream_write_uint64(stream, ntfs_time);
1163 }
juanii3679a3d2018-02-11 13:55:38 -03001164 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001165 {
1166 mz_zip_unix_to_ntfs_time(file_info->creation_date, &ntfs_time);
1167 err = mz_stream_write_uint64(stream, ntfs_time);
1168 }
1169 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001170#ifdef HAVE_AES
1171 // Write AES extra info header to central directory
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001172 if ((err == MZ_OK) && (!skip_aes) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001173 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001174 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001175 if (err == MZ_OK)
1176 err = mz_stream_write_uint16(stream, 7);
1177 if (err == MZ_OK)
1178 err = mz_stream_write_uint16(stream, file_info->aes_version);
1179 if (err == MZ_OK)
1180 err = mz_stream_write_uint8(stream, 'A');
1181 if (err == MZ_OK)
1182 err = mz_stream_write_uint8(stream, 'E');
1183 if (err == MZ_OK)
1184 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
1185 if (err == MZ_OK)
1186 err = mz_stream_write_uint16(stream, file_info->compression_method);
1187 }
1188#endif
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001189 if ((err == MZ_OK) && (!local) && (file_info->comment != NULL))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001190 {
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001191 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != file_info->comment_size)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001192 err = MZ_STREAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001193 }
1194
1195 return err;
1196}
1197
Nathan Moinvaziri26308b22018-08-08 09:40:15 -07001198static 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 -07001199{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001200 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001201 int64_t max_total_in = 0;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001202 int64_t header_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001203 int64_t footer_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001204 int32_t err = MZ_OK;
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001205 uint8_t use_crypt = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001206
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001207 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001208 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001209
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001210 switch (zip->file_info.compression_method)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001211 {
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001212 case MZ_COMPRESS_METHOD_STORE:
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001213 case MZ_COMPRESS_METHOD_DEFLATE:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001214#ifdef HAVE_BZIP2
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001215 case MZ_COMPRESS_METHOD_BZIP2:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001216#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001217#if HAVE_LZMA
1218 case MZ_COMPRESS_METHOD_LZMA:
1219#endif
1220 err = MZ_OK;
1221 break;
1222 default:
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001223 return MZ_SUPPORT_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001224 }
1225
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001226 zip->entry_raw = raw;
1227
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001228 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password != NULL))
1229 {
1230 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
1231 {
1232 // Encrypt only when we are not trying to write raw and password is supplied.
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001233 if (!zip->entry_raw)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001234 use_crypt = 1;
1235 }
1236 else if (zip->open_mode & MZ_OPEN_MODE_READ)
1237 {
1238 // Decrypt only when password is supplied. Don't error when password
1239 // is not supplied as we may want to read the raw encrypted data.
1240 use_crypt = 1;
1241 }
1242 }
1243
1244 if ((err == MZ_OK) && (use_crypt))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001245 {
1246#ifdef HAVE_AES
1247 if (zip->file_info.aes_version)
1248 {
1249 mz_stream_aes_create(&zip->crypt_stream);
1250 mz_stream_aes_set_password(zip->crypt_stream, password);
1251 mz_stream_aes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001252 }
1253 else
1254#endif
1255 {
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001256#ifdef HAVE_PKCRYPT
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001257 uint8_t verify1 = 0;
1258 uint8_t verify2 = 0;
1259
1260 // Info-ZIP modification to ZipCrypto format:
1261 // If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time.
1262
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001263 if (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR)
1264 {
1265 uint32_t dos_date = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001266
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001267 dos_date = mz_zip_time_t_to_dos_date(zip->file_info.modified_date);
1268
1269 verify1 = (uint8_t)((dos_date >> 16) & 0xff);
1270 verify2 = (uint8_t)((dos_date >> 8) & 0xff);
1271 }
1272 else
1273 {
1274 verify1 = (uint8_t)((zip->file_info.crc >> 16) & 0xff);
1275 verify2 = (uint8_t)((zip->file_info.crc >> 24) & 0xff);
1276 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001277
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001278 mz_stream_pkcrypt_create(&zip->crypt_stream);
1279 mz_stream_pkcrypt_set_password(zip->crypt_stream, password);
1280 mz_stream_pkcrypt_set_verify(zip->crypt_stream, verify1, verify2);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001281#endif
1282 }
1283 }
1284
1285 if (err == MZ_OK)
1286 {
1287 if (zip->crypt_stream == NULL)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001288 mz_stream_raw_create(&zip->crypt_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001289
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001290 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001291
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001292 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001293 }
1294
1295 if (err == MZ_OK)
1296 {
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001297 if (zip->entry_raw || zip->file_info.compression_method == MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001298 mz_stream_raw_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001299#ifdef HAVE_ZLIB
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001300 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001301 mz_stream_zlib_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001302#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001303#ifdef HAVE_BZIP2
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001304 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_BZIP2)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001305 mz_stream_bzip_create(&zip->compress_stream);
1306#endif
1307#ifdef HAVE_LZMA
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001308 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001309 mz_stream_lzma_create(&zip->compress_stream);
1310#endif
1311 else
1312 err = MZ_PARAM_ERROR;
1313 }
1314
1315 if (err == MZ_OK)
1316 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001317 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001318 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001319 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, compress_level);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001320 }
1321 else
1322 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001323 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 -07001324 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001325 max_total_in = zip->file_info.compressed_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001326 mz_stream_set_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1327
1328 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_HEADER_SIZE, &header_size) == MZ_OK)
1329 max_total_in -= header_size;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001330 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
1331 max_total_in -= footer_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001332
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001333 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001334 }
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001335 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 -03001336 {
1337 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, zip->file_info.compressed_size);
1338 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT_MAX, zip->file_info.uncompressed_size);
1339 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001340 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001341
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001342 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1343
1344 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1345 }
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001346
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001347 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001348 {
1349 mz_stream_crc32_create(&zip->crc32_stream);
1350 mz_stream_set_base(zip->crc32_stream, zip->compress_stream);
1351
1352 err = mz_stream_open(zip->crc32_stream, NULL, zip->open_mode);
1353 }
1354
1355 if (err == MZ_OK)
1356 {
1357 zip->entry_opened = 1;
1358 }
1359
1360 return err;
1361}
1362
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001363int32_t mz_zip_entry_is_open(void *handle)
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001364{
1365 mz_zip *zip = (mz_zip *)handle;
1366 if (zip == NULL)
1367 return MZ_PARAM_ERROR;
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001368 if (zip->entry_opened == 0)
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001369 return MZ_EXIST_ERROR;
1370 return MZ_OK;
1371}
1372
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001373int32_t mz_zip_entry_is_dir(void *handle)
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001374{
1375 mz_zip *zip = (mz_zip *)handle;
1376 int32_t filename_length = 0;
1377
1378 if (zip == NULL)
1379 return MZ_PARAM_ERROR;
1380 if (zip->entry_scanned == 0)
1381 return MZ_PARAM_ERROR;
1382 if (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK)
1383 return MZ_OK;
1384
1385 filename_length = (int32_t )strlen(zip->file_info.filename);
1386 if (filename_length > 0)
1387 {
1388 if ((zip->file_info.filename[filename_length - 1] == '/') ||
1389 (zip->file_info.filename[filename_length - 1] == '\\'))
1390 return MZ_OK;
1391 }
1392 return MZ_EXIST_ERROR;
1393}
1394
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001395int32_t mz_zip_entry_read_open(void *handle, uint8_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001396{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001397 mz_zip *zip = (mz_zip *)handle;
1398 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001399
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001400#if defined(MZ_ZIP_NO_ENCRYPTION)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001401 if (password != NULL)
1402 return MZ_PARAM_ERROR;
1403#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001404 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001405 return MZ_PARAM_ERROR;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001406 if ((zip->open_mode & MZ_OPEN_MODE_READ) == 0)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001407 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001408 if (zip->entry_scanned == 0)
1409 return MZ_PARAM_ERROR;
Nathan Moinvaziri0f09a002018-04-23 18:48:30 -07001410 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL) && (!raw))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001411 return MZ_PARAM_ERROR;
1412
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001413 if (zip->file_info.disk_number == zip->disk_number_with_cd)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001414 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1415 else
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001416 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001417
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -07001418 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 -07001419 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001420 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 -07001421
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001422#ifdef MZ_ZIP_NO_DECOMPRESSION
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001423 if (zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001424 err = MZ_SUPPORT_ERROR;
1425#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001426 if (err == MZ_OK)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001427 err = mz_zip_entry_open_int(handle, raw, 0, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001428
1429 return err;
1430}
1431
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001432int32_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 -07001433{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001434 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001435 int64_t disk_number = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001436 int32_t err = MZ_OK;
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001437 int32_t len = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001438
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001439#if defined(MZ_ZIP_NO_ENCRYPTION)
tz-lomb1b25802017-11-10 15:03:02 +03001440 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001441 return MZ_PARAM_ERROR;
1442#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001443 if (zip == NULL || file_info == NULL || file_info->filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001444 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001445
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001446 if (mz_zip_entry_is_open(handle) == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001447 {
1448 err = mz_zip_entry_close(handle);
1449 if (err != MZ_OK)
1450 return err;
1451 }
1452
1453 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001454
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001455 mz_stream_seek(zip->file_info_stream, 0, SEEK_SET);
1456 mz_stream_write(zip->file_info_stream, file_info, sizeof(mz_zip_file));
1457
1458 // Copy filename, extrafield, and comment internally
1459 if (file_info->filename != NULL)
1460 {
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001461 mz_stream_mem_get_buffer_at_current(zip->file_info_stream, &zip->file_info.filename);
1462 mz_stream_write_chars(zip->file_info_stream, file_info->filename, 1);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001463 }
1464 if (file_info->extrafield != NULL)
1465 {
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001466 mz_stream_mem_get_buffer_at_current(zip->file_info_stream, &zip->file_info.extrafield);
1467 mz_stream_write(zip->file_info_stream, file_info->extrafield, file_info->extrafield_size);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001468 }
1469 if (file_info->comment != NULL)
1470 {
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001471 mz_stream_mem_get_buffer_at_current(zip->file_info_stream, &zip->file_info.comment);
1472 mz_stream_write_chars(zip->file_info_stream, file_info->comment, 1);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001473 }
1474
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001475 if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001476 {
1477 if ((compress_level == 8) || (compress_level == 9))
1478 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
1479 if (compress_level == 2)
1480 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
1481 if (compress_level == 1)
1482 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1483 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001484#ifdef HAVE_LZMA
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001485 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001486 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001487#endif
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001488
1489 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001490
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001491 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001492 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001493
juaniib8887e92018-02-14 00:51:05 -03001494 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1495 zip->file_info.disk_number = (uint32_t)disk_number;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001496
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001497 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001498 zip->file_info.crc = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001499 zip->file_info.compressed_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001500
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001501#ifdef HAVE_AES
1502 if (zip->file_info.aes_version && zip->file_info.aes_encryption_mode == 0)
1503 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTION_MODE_256;
1504#endif
1505
Nathan Moinvaziri4827f712018-05-02 10:50:47 -07001506 if ((compress_level == 0) || (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK))
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001507 zip->file_info.compression_method = MZ_COMPRESS_METHOD_STORE;
Viktor Szakats2884e672018-04-30 08:12:13 +00001508
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001509#ifdef MZ_ZIP_NO_COMPRESSION
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001510 if (zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001511 err = MZ_SUPPORT_ERROR;
1512#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001513 if (err == MZ_OK)
1514 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
1515 if (err == MZ_OK)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001516 err = mz_zip_entry_open_int(handle, raw, compress_level, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001517
1518 return err;
1519}
1520
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001521int32_t mz_zip_entry_read(void *handle, void *buf, int32_t len)
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 int32_t read = 0;
1525
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001526 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001527 return MZ_PARAM_ERROR;
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001528 if (UINT_MAX == UINT16_MAX && len > UINT16_MAX) // zlib limitation
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001529 return MZ_PARAM_ERROR;
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001530 if (len == 0)
1531 return MZ_PARAM_ERROR;
1532
Nathan Moinvazirieb80cd92018-07-11 16:45:09 -07001533 if (zip->file_info.compressed_size == 0)
1534 return 0;
1535
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001536 // Read entire entry even if uncompressed_size = 0, otherwise
1537 // aes encryption validation will fail if compressed_size > 0
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001538 read = mz_stream_read(zip->crc32_stream, buf, len);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001539 return read;
1540}
1541
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001542int32_t mz_zip_entry_write(void *handle, const void *buf, int32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001543{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001544 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001545 int32_t written = 0;
1546
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001547 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001548 return MZ_PARAM_ERROR;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001549 written = mz_stream_write(zip->crc32_stream, buf, len);
1550 return written;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001551}
1552
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001553int32_t mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001554{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001555 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001556 if (zip == NULL || !zip->entry_scanned)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001557 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001558 *file_info = &zip->file_info;
1559 return MZ_OK;
1560}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001561
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001562int32_t mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001563{
1564 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001565 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001566 return MZ_PARAM_ERROR;
1567 *local_file_info = &zip->local_file_info;
1568 return MZ_OK;
1569}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001570
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001571int32_t mz_zip_entry_close(void *handle)
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001572{
1573 return mz_zip_entry_close_raw(handle, 0, 0);
1574}
1575
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001576int32_t mz_zip_entry_close_raw(void *handle, uint64_t uncompressed_size, uint32_t crc32)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001577{
1578 mz_zip *zip = (mz_zip *)handle;
1579 uint64_t compressed_size = 0;
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001580 int64_t total_in = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001581 int32_t err = MZ_OK;
1582
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001583 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001584 return MZ_PARAM_ERROR;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001585
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001586 mz_stream_close(zip->compress_stream);
Nathan Moinvaziri6ac2ff42018-07-21 14:29:29 -07001587
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001588 if (!zip->entry_raw)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001589 crc32 = mz_stream_crc32_get_value(zip->crc32_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001590
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001591 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001592 {
1593#ifdef HAVE_AES
1594 // AES zip version AE-1 will expect a valid crc as well
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001595 if (zip->file_info.aes_version <= 0x0001)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001596#endif
1597 {
Nathan Moinvaziri6ac2ff42018-07-21 14:29:29 -07001598 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in);
Nathan Moinvaziri184f4cb2018-07-09 07:53:17 -07001599 // If entire entry was not read this will fail
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001600 if ((total_in > 0) && (!zip->entry_raw))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001601 {
1602 if (crc32 != zip->file_info.crc)
1603 err = MZ_CRC_ERROR;
1604 }
1605 }
1606 }
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001607
1608 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001609 if (!zip->entry_raw)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001610 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001611
1612 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
1613 {
1614 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001615 err = mz_stream_close(zip->crypt_stream);
1616
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001617 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001618 }
1619
1620 mz_stream_delete(&zip->crypt_stream);
1621
1622 mz_stream_delete(&zip->compress_stream);
1623 mz_stream_crc32_delete(&zip->crc32_stream);
1624
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001625 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001626 {
1627 if (err == MZ_OK)
1628 {
1629 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
1630 if (err == MZ_OK)
1631 err = mz_stream_write_uint32(zip->stream, crc32);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001632 // Store data descriptor as 8 bytes if zip 64 extension enabled
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001633 if (err == MZ_OK)
1634 {
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001635 // Zip 64 extension is enabled when uncompressed size is > UINT32_mAX
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001636 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001637 err = mz_stream_write_uint32(zip->stream, (uint32_t)compressed_size);
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001638 else
1639 err = mz_stream_write_uint64(zip->stream, compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001640 }
1641 if (err == MZ_OK)
1642 {
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001643 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001644 err = mz_stream_write_uint32(zip->stream, (uint32_t)uncompressed_size);
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001645 else
1646 err = mz_stream_write_uint64(zip->stream, uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001647 }
1648 }
1649
1650 zip->file_info.crc = crc32;
1651 zip->file_info.compressed_size = compressed_size;
1652 zip->file_info.uncompressed_size = uncompressed_size;
1653
1654 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001655 err = mz_zip_entry_write_header(zip->cd_mem_stream, 0, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001656
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001657 zip->number_entry += 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001658 }
1659
1660 zip->entry_opened = 0;
1661
1662 return err;
1663}
1664
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001665static int32_t mz_zip_goto_next_entry_int(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001666{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001667 mz_zip *zip = (mz_zip *)handle;
1668 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001669
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001670 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001671 return MZ_PARAM_ERROR;
1672
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001673 zip->entry_scanned = 0;
1674
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001675 mz_stream_set_prop_int64(zip->cd_stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Viktor Szakats915b82e2018-04-24 10:02:39 +00001676
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001677 err = mz_stream_seek(zip->cd_stream, zip->cd_current_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001678 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001679 err = mz_zip_entry_read_header(zip->cd_stream, 0, &zip->file_info, zip->file_info_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001680 if (err == MZ_OK)
1681 zip->entry_scanned = 1;
1682 return err;
1683}
1684
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001685int32_t mz_zip_get_number_entry(void *handle, int64_t *number_entry)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001686{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001687 mz_zip *zip = (mz_zip *)handle;
1688 if (zip == NULL || number_entry == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001689 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001690 *number_entry = zip->number_entry;
1691 return MZ_OK;
1692}
1693
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001694int32_t mz_zip_get_disk_number_with_cd(void *handle, uint32_t *disk_number_with_cd)
juanii566b9782018-02-10 15:07:54 -03001695{
1696 mz_zip *zip = (mz_zip *)handle;
1697 if (zip == NULL || disk_number_with_cd == NULL)
1698 return MZ_PARAM_ERROR;
1699 *disk_number_with_cd = zip->disk_number_with_cd;
1700 return MZ_OK;
1701}
1702
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001703int64_t mz_zip_get_entry(void *handle)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001704{
1705 mz_zip *zip = (mz_zip *)handle;
1706
1707 if (zip == NULL)
1708 return MZ_PARAM_ERROR;
1709
1710 return zip->cd_current_pos;
1711}
1712
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001713int32_t mz_zip_goto_entry(void *handle, uint64_t cd_pos)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001714{
1715 mz_zip *zip = (mz_zip *)handle;
1716
1717 if (zip == NULL)
1718 return MZ_PARAM_ERROR;
1719
1720 if (cd_pos < zip->cd_start_pos || cd_pos > zip->cd_start_pos + zip->cd_size)
1721 return MZ_PARAM_ERROR;
1722
1723 zip->cd_current_pos = cd_pos;
1724
1725 return mz_zip_goto_next_entry_int(handle);
1726}
1727
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001728int32_t mz_zip_goto_first_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001729{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001730 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001731
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001732 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001733 return MZ_PARAM_ERROR;
1734
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001735 zip->cd_current_pos = zip->cd_start_pos;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001736
1737 return mz_zip_goto_next_entry_int(handle);
1738}
1739
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001740int32_t mz_zip_goto_next_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001741{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001742 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001743
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001744 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001745 return MZ_PARAM_ERROR;
1746
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001747 zip->cd_current_pos += MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
1748 zip->file_info.extrafield_size + zip->file_info.comment_size;
1749
1750 return mz_zip_goto_next_entry_int(handle);
1751}
1752
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001753int32_t mz_zip_locate_entry(void *handle, const char *filename, uint8_t ignore_case)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001754{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001755 mz_zip *zip = (mz_zip *)handle;
1756 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001757 int32_t result = 0;
1758
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001759 if (zip == NULL || filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001760 return MZ_PARAM_ERROR;
1761
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001762 // If we are already on the current entry, no need to search
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001763 if ((zip->entry_scanned) && (zip->file_info.filename != NULL))
1764 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001765 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001766 if (result == 0)
1767 return MZ_OK;
1768 }
1769
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001770 // Search all entries starting at the first
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001771 err = mz_zip_goto_first_entry(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001772 while (err == MZ_OK)
1773 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001774 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
1775 if (result == 0)
1776 return MZ_OK;
1777
1778 err = mz_zip_goto_next_entry(handle);
1779 }
1780
1781 return err;
1782}
1783
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001784int32_t mz_zip_locate_first_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001785{
1786 mz_zip *zip = (mz_zip *)handle;
1787 int32_t err = MZ_OK;
1788 int32_t result = 0;
1789
1790 // Search first entry looking for match
1791 err = mz_zip_goto_first_entry(handle);
1792 if (err != MZ_OK)
1793 return err;
1794
1795 result = cb(handle, userdata, &zip->file_info);
1796 if (result == 0)
1797 return MZ_OK;
1798
1799 return mz_zip_locate_next_entry(handle, userdata, cb);
1800}
1801
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001802int32_t mz_zip_locate_next_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001803{
1804 mz_zip *zip = (mz_zip *)handle;
1805 int32_t err = MZ_OK;
1806 int32_t result = 0;
1807
1808 // Search next entries looking for match
1809 err = mz_zip_goto_next_entry(handle);
1810 while (err == MZ_OK)
1811 {
1812 result = cb(handle, userdata, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001813 if (result == 0)
1814 return MZ_OK;
1815
1816 err = mz_zip_goto_next_entry(handle);
1817 }
1818
1819 return err;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001820}
1821
1822/***************************************************************************/
1823
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07001824int32_t mz_zip_attrib_is_dir(int32_t attributes, int32_t version_madeby)
1825{
1826 int32_t host_system = (uint8_t)(version_madeby >> 8);
1827
1828 if (host_system == MZ_HOST_SYSTEM_MSDOS || host_system == MZ_HOST_SYSTEM_WINDOWS_NTFS)
1829 {
1830 if ((attributes & 0x10) == 0x10) // FILE_ATTRIBUTE_DIRECTORY
1831 return MZ_OK;
1832 }
1833 else if (host_system == MZ_HOST_SYSTEM_UNIX || host_system == MZ_HOST_SYSTEM_OSX_DARWIN)
1834 {
1835 if ((attributes & 00170000) == 0040000) // S_ISDIR
1836 return MZ_OK;
1837 }
1838
1839 return MZ_EXIST_ERROR;
1840}
1841
1842/***************************************************************************/
1843
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001844static int32_t mz_zip_invalid_date(const struct tm *ptm)
1845{
1846#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001847 return (!datevalue_in_range(0, 127 + 80, ptm->tm_year) || // 1980-based year, allow 80 extra
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001848 !datevalue_in_range(0, 11, ptm->tm_mon) ||
1849 !datevalue_in_range(1, 31, ptm->tm_mday) ||
1850 !datevalue_in_range(0, 23, ptm->tm_hour) ||
1851 !datevalue_in_range(0, 59, ptm->tm_min) ||
1852 !datevalue_in_range(0, 59, ptm->tm_sec));
1853#undef datevalue_in_range
1854}
1855
1856static void mz_zip_dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
1857{
1858 uint64_t date = (uint64_t)(dos_date >> 16);
1859
1860 ptm->tm_mday = (uint16_t)(date & 0x1f);
1861 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
1862 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
1863 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
1864 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
1865 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
1866 ptm->tm_isdst = -1;
1867}
1868
1869int32_t mz_zip_dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
1870{
1871 if (ptm == NULL)
1872 return MZ_PARAM_ERROR;
1873
1874 mz_zip_dosdate_to_raw_tm(dos_date, ptm);
1875
1876 if (mz_zip_invalid_date(ptm))
1877 {
1878 // Invalid date stored, so don't return it
1879 memset(ptm, 0, sizeof(struct tm));
1880 return MZ_FORMAT_ERROR;
1881 }
1882 return MZ_OK;
1883}
1884
1885time_t mz_zip_dosdate_to_time_t(uint64_t dos_date)
1886{
1887 struct tm ptm;
1888 mz_zip_dosdate_to_raw_tm(dos_date, &ptm);
1889 return mktime(&ptm);
1890}
1891
1892int32_t mz_zip_time_t_to_tm(time_t unix_time, struct tm *ptm)
1893{
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001894 struct tm *ltm = NULL;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001895 if (ptm == NULL)
1896 return MZ_PARAM_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001897 ltm = localtime(&unix_time); // Returns a 1900-based year
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001898 if (ltm == NULL)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001899 {
1900 // Invalid date stored, so don't return it
1901 memset(ptm, 0, sizeof(struct tm));
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001902 return MZ_INTERNAL_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001903 }
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001904 memcpy(ptm, ltm, sizeof(struct tm));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001905 return MZ_OK;
1906}
1907
1908uint32_t mz_zip_time_t_to_dos_date(time_t unix_time)
1909{
1910 struct tm ptm;
1911 mz_zip_time_t_to_tm(unix_time, &ptm);
1912 return mz_zip_tm_to_dosdate((const struct tm *)&ptm);
1913}
1914
1915uint32_t mz_zip_tm_to_dosdate(const struct tm *ptm)
1916{
Nathan Moinvazirie33916b2018-05-01 13:45:08 -07001917 struct tm fixed_tm;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001918
1919 // Years supported:
1920
1921 // [00, 79] (assumed to be between 2000 and 2079)
1922 // [80, 207] (assumed to be between 1980 and 2107, typical output of old
1923 // software that does 'year-1900' to get a double digit year)
1924 // [1980, 2107] (due to format limitations, only years 1980-2107 can be stored.)
1925
1926 memcpy(&fixed_tm, ptm, sizeof(struct tm));
1927 if (fixed_tm.tm_year >= 1980) // range [1980, 2107]
1928 fixed_tm.tm_year -= 1980;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001929 else if (fixed_tm.tm_year >= 80) // range [80, 207]
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001930 fixed_tm.tm_year -= 80;
1931 else // range [00, 79]
1932 fixed_tm.tm_year += 20;
1933
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001934 if (mz_zip_invalid_date(&fixed_tm))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001935 return 0;
1936
1937 return (uint32_t)(((fixed_tm.tm_mday) + (32 * (fixed_tm.tm_mon + 1)) + (512 * fixed_tm.tm_year)) << 16) |
1938 ((fixed_tm.tm_sec / 2) + (32 * fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
1939}
1940
1941int32_t mz_zip_ntfs_to_unix_time(uint64_t ntfs_time, time_t *unix_time)
1942{
1943 *unix_time = (time_t)((ntfs_time - 116444736000000000LL) / 10000000);
1944 return MZ_OK;
1945}
1946
1947int32_t mz_zip_unix_to_ntfs_time(time_t unix_time, uint64_t *ntfs_time)
1948{
1949 *ntfs_time = ((uint64_t)unix_time * 10000000) + 116444736000000000LL;
1950 return MZ_OK;
1951}
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001952
1953int32_t mz_zip_path_compare(const char *path1, const char *path2, uint8_t ignore_case)
1954{
1955 do
1956 {
1957 if ((*path1 == '\\' && *path2 == '/') ||
1958 (*path2 == '\\' && *path1 == '/'))
1959 {
1960 // Ignore comparison of path slashes
1961 }
1962 else if (ignore_case)
1963 {
1964 if (tolower(*path1) != tolower(*path2))
1965 break;
1966 }
1967 else if (*path1 != *path2)
1968 {
1969 break;
1970 }
1971
1972 path1 += 1;
1973 path2 += 1;
1974 }
1975 while (*path1 != 0 && *path2 != 0);
1976
1977 if (ignore_case)
1978 return (int32_t)(tolower(*path1) - tolower(*path2));
1979
1980 return (int32_t)(*path1 - *path2);
1981}
1982
1983/***************************************************************************/