blob: 8b0c9ad6cbed05be1425a24460f71a599d670ff6 [file] [log] [blame]
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08001/* zip.c -- Zip manipulation
Nathan Moinvaziri11679d62017-10-16 20:15:35 -07002 Version 2.0.1, October 16th, 2017
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08003 part of the MiniZip project
4
5 Copyright (C) 2010-2017 Nathan Moinvaziri
6 Modifications for AES, PKWARE disk spanning
7 https://github.com/nmoinvaz/minizip
8 Copyright (C) 2009-2010 Mathias Svensson
9 Modifications for Zip64 support
10 http://result42.com
11 Copyright (C) 1998-2010 Gilles Vollant
12 http://www.winimage.com/zLibDll/minizip.html
13
14 This program is distributed under the terms of the same license as zlib.
15 See the accompanying LICENSE file for the full text of the license.
16*/
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <stdint.h>
21#include <string.h>
22#include <errno.h>
23
24#include "zlib.h"
25
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070026#include "mz.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080027#include "mz_strm.h"
28#ifdef HAVE_AES
29# include "mz_strm_aes.h"
30#endif
31#ifdef HAVE_BZIP2
32# include "mz_strm_bzip.h"
33#endif
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070034#ifdef HAVE_CRYPT
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080035# include "mz_strm_crypt.h"
36#endif
37#ifdef HAVE_LZMA
38# include "mz_strm_lzma.h"
39#endif
40#include "mz_strm_mem.h"
41#include "mz_strm_zlib.h"
42
43#include "mz_zip.h"
44
45/***************************************************************************/
46
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070047#define MZ_ZIP_MAGIC_LOCALHEADER (0x04034b50)
48#define MZ_ZIP_MAGIC_CENTRALHEADER (0x02014b50)
49#define MZ_ZIP_MAGIC_ENDHEADER (0x06054b50)
50#define MZ_ZIP_MAGIC_ENDHEADER64 (0x06064b50)
51#define MZ_ZIP_MAGIC_ENDLOCHEADER64 (0x07064b50)
52#define MZ_ZIP_MAGIC_DATADESCRIPTOR (0x08074b50)
53
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070054#define MZ_ZIP_SIZE_CD_ITEM (0x2e)
55#define MZ_ZIP_SIZE_CD_LOCATOR64 (0x14)
56#define MZ_ZIP_SIZE_LOCALHEADER (0x1e)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080057
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080058/***************************************************************************/
59
60typedef struct mz_zip_s
61{
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070062 mz_zip_global global_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080063 mz_zip_file file_info;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070064 mz_zip_file local_file_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080065 mz_zip_compress compress_info;
66
67 void *stream; // main stream
68 void *cd_stream; // memory stream for central directory
69 void *compress_stream; // compression stream
70 void *crc32_stream; // crc32 stream
71 void *crypt_stream; // encryption stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070072 void *file_info_stream; // memory stream for storing file info
73 void *local_file_info_stream; // memory stream for storing file info
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080074
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070075 int32_t open_mode;
76
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080077 uint32_t number_disk; // number of the current disk, used for spanning ZIP
78 uint32_t number_disk_with_CD; // number the the disk with central dir, used for spanning ZIP
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070079
80 uint64_t disk_offset; // byte before the zip file, (>0 for sfx)
81
82 uint64_t cd_current_pos; // pos of the current file in the central dir
83 uint64_t cd_pos; // position of the beginning of the central dir
84 uint64_t cd_offset; // offset of start of central directory
85 uint64_t cd_size; // size of the central directory
86
87 uint16_t entry_scanned;
88 uint16_t entry_opened; // 1 if a file in the zip is currently writ.
89 uint64_t entry_read;
90
91#ifdef HAVE_AES
92 uint16_t aes_version;
93 uint8_t aes_encryption_mode;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080094#endif
95} mz_zip;
96
97/***************************************************************************/
98
99// Locate the central directory of a zip file (at the end, just before the global comment)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700100static int32_t mz_zip_search_cd(void *stream, uint64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800101{
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700102 uint8_t buf[1024 + 4];
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800103 uint64_t file_size = 0;
Nathan Moinvaziri77008c62017-10-16 14:50:31 -0700104 uint64_t back_read = 0;
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700105 uint64_t max_back = UINT16_MAX; // maximum size of global comment
Nathan Moinvaziri77008c62017-10-16 14:50:31 -0700106 uint32_t read_size = sizeof(buf);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800107 uint64_t read_pos = 0;
108 uint32_t i = 0;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700109
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700110 *central_pos = 0;
111
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800112 if (mz_stream_seek(stream, 0, MZ_STREAM_SEEK_END) != MZ_OK)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700113 return MZ_STREAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800114
115 file_size = mz_stream_tell(stream);
116
117 if (max_back > file_size)
118 max_back = file_size;
119
120 while (back_read < max_back)
121 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700122 back_read += (sizeof(buf) - 4);
123 if (back_read > max_back)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800124 back_read = max_back;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800125
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700126 read_pos = file_size - back_read;
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700127 if (read_size > (file_size - read_pos))
128 read_size = (uint32_t)(file_size - read_pos);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800129
130 if (mz_stream_seek(stream, read_pos, MZ_STREAM_SEEK_SET) != MZ_OK)
131 break;
132 if (mz_stream_read(stream, buf, read_size) != read_size)
133 break;
134
135 for (i = read_size - 3; (i--) > 0;)
136 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700137 if (((*(buf + i)) == (MZ_ZIP_MAGIC_ENDHEADER & 0xff)) &&
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700138 ((*(buf + i + 1)) == (MZ_ZIP_MAGIC_ENDHEADER >> 8 & 0xff)) &&
139 ((*(buf + i + 2)) == (MZ_ZIP_MAGIC_ENDHEADER >> 16 & 0xff)) &&
140 ((*(buf + i + 3)) == (MZ_ZIP_MAGIC_ENDHEADER >> 24 & 0xff)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800141 {
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700142 *central_pos = read_pos + i;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700143 return MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800144 }
145 }
146
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700147 if (*central_pos != 0)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800148 break;
149 }
150
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700151 return MZ_EXIST_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800152}
153
154// Locate the central directory 64 of a zip file (at the end, just before the global comment)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700155static int32_t mz_zip_search_zip64_cd(void *stream, const uint64_t end_central_offset, uint64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800156{
157 uint64_t offset = 0;
158 uint32_t value32 = 0;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700159 int16_t err = MZ_OK;
160
161
162 *central_pos = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800163
164 // Zip64 end of central directory locator
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700165 err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_STREAM_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800166 // Read locator signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700167 if (err == MZ_OK)
168 {
169 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700170 if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700171 err = MZ_FORMAT_ERROR;
172 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800173 // Number of the disk with the start of the zip64 end of central directory
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700174 if (err == MZ_OK)
175 err = mz_stream_read_uint32(stream, &value32);
176 // Relative offset of the zip64 end of central directory record8
177 if (err == MZ_OK)
178 err = mz_stream_read_uint64(stream, &offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800179 // Total number of disks
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700180 if (err == MZ_OK)
181 err = mz_stream_read_uint32(stream, &value32);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800182 // Goto end of central directory record
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700183 if (err == MZ_OK)
184 err = mz_stream_seek(stream, offset, MZ_STREAM_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800185 // The signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700186 if (err == MZ_OK)
187 {
188 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700189 if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700190 err = MZ_FORMAT_ERROR;
191 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800192
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700193 if (err == MZ_OK)
194 *central_pos = offset;
195
196 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800197}
198
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700199int mz_zip_read_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800200{
201 mz_zip *zip = NULL;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700202 uint64_t number_entry_cd = 0;
203 uint64_t number_entry_cd64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800204 uint64_t number_entry = 0;
205 uint64_t central_pos = 0;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700206 uint64_t central_pos64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800207 uint16_t value16 = 0;
208 uint32_t value32 = 0;
209 uint64_t value64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800210 int16_t err = MZ_OK;
211
212
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700213 zip = (mz_zip *)handle;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800214 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700215 return MZ_PARAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800216
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700217 // Read and cache central directory records
218 if (mz_zip_search_cd(zip->stream, &central_pos) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800219 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700220 // Read end of central directory info
221 err = mz_stream_seek(zip->stream, central_pos, MZ_STREAM_SEEK_SET);
222 // The signature, already checked
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800223 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700224 err = mz_stream_read_uint32(zip->stream, &value32);
225 // Number of this disk
226 if (err == MZ_OK)
227 err = mz_stream_read_uint16(zip->stream, &value16);
228 zip->number_disk = value16;
229 // Number of the disk with the start of the central directory
230 if (err == MZ_OK)
231 err = mz_stream_read_uint16(zip->stream, &value16);
232 zip->global_info.number_disk_with_cd = value16;
233 // Total number of entries in the central dir on this disk
234 if (err == MZ_OK)
235 err = mz_stream_read_uint16(zip->stream, &value16);
236 zip->global_info.number_entry = value16;
237 // Total number of entries in the central dir
238 if (err == MZ_OK)
239 err = mz_stream_read_uint16(zip->stream, &value16);
240 number_entry_cd = value16;
241 if (number_entry_cd != zip->global_info.number_entry)
242 err = MZ_FORMAT_ERROR;
243 // Size of the central directory
244 if (err == MZ_OK)
245 err = mz_stream_read_uint32(zip->stream, &value32);
246 if (err == MZ_OK)
247 zip->cd_size = value32;
248 // Offset of start of central directory with respect to the starting disk number
249 if (err == MZ_OK)
250 err = mz_stream_read_uint32(zip->stream, &value32);
251 zip->cd_offset = value32;
252 // Zip file global comment length
253 if (err == MZ_OK)
254 err = mz_stream_read_uint16(zip->stream, &zip->global_info.comment_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800255
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700256 if ((err == MZ_OK) && ((number_entry_cd == UINT16_MAX) || (zip->cd_offset == UINT32_MAX)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800257 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700258 // Format should be Zip64, as the central directory or file size is too large
259 if (mz_zip_search_zip64_cd(zip->stream, central_pos, &central_pos64) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800260 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700261 central_pos = central_pos64;
262
263 err = mz_stream_seek(zip->stream, central_pos, MZ_STREAM_SEEK_SET);
264 // The signature, already checked
265 if (err == MZ_OK)
266 err = mz_stream_read_uint32(zip->stream, &value32);
267 // Size of zip64 end of central directory record
268 if (err == MZ_OK)
269 err = mz_stream_read_uint64(zip->stream, &value64);
270 // Version made by
271 if (err == MZ_OK)
272 err = mz_stream_read_uint16(zip->stream, &value16);
273 // Version needed to extract
274 if (err == MZ_OK)
275 err = mz_stream_read_uint16(zip->stream, &value16);
276 // Number of this disk
277 if (err == MZ_OK)
278 err = mz_stream_read_uint32(zip->stream, &zip->number_disk);
279 // Number of the disk with the start of the central directory
280 if (err == MZ_OK)
281 err = mz_stream_read_uint32(zip->stream, &zip->global_info.number_disk_with_cd);
282 // Total number of entries in the central directory on this disk
283 if (err == MZ_OK)
284 err = mz_stream_read_uint64(zip->stream, &number_entry);
285 // Total number of entries in the central directory
286 if (err == MZ_OK)
287 err = mz_stream_read_uint64(zip->stream, &number_entry_cd64);
288 if (number_entry == UINT32_MAX)
289 zip->global_info.number_entry = number_entry_cd64;
290 // Size of the central directory
291 if (err == MZ_OK)
292 err = mz_stream_read_uint64(zip->stream, &zip->cd_size);
293 // Offset of start of central directory with respect to the starting disk number
294 if (err == MZ_OK)
295 err = mz_stream_read_uint64(zip->stream, &zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800296 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700297 else if ((zip->global_info.number_entry == UINT16_MAX) || (number_entry_cd != zip->global_info.number_entry) ||
298 (zip->cd_size == UINT16_MAX) || (zip->cd_offset == UINT32_MAX))
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700299 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700300 err = MZ_FORMAT_ERROR;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700301 }
302 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800303 }
304
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700305 if (err == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800306 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700307 if (central_pos < zip->cd_offset + zip->cd_size)
308 err = MZ_FORMAT_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800309 }
310
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700311 if ((err == MZ_OK) && (zip->global_info.comment_size > 0))
312 {
313 zip->global_info.comment = (char *)malloc(zip->global_info.comment_size + 1);
314 if (zip->global_info.comment)
315 {
316 if (mz_stream_read(zip->stream, zip->global_info.comment,
317 zip->global_info.comment_size) != zip->global_info.comment_size)
318 err = MZ_STREAM_ERROR;
319
320 zip->global_info.comment[zip->global_info.comment_size] = 0;
321 }
322 }
323
324 if (err == MZ_OK)
325 {
326 zip->disk_offset = central_pos - (zip->cd_offset + zip->cd_size);
327 zip->cd_pos = zip->disk_offset;
328 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800329
330 return err;
331}
332
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700333int mz_zip_write_cd(void *handle, const char *global_comment, uint16_t version_madeby)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800334{
335 mz_zip *zip = NULL;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800336 uint16_t comment_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700337 uint64_t zip64_eocd_pos_inzip = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800338 uint64_t pos = 0;
339 uint64_t cd_pos = 0;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700340 int64_t disk_number = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800341 int16_t err = MZ_OK;
342
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700343
344 zip = (mz_zip *)handle;
345 if (zip == NULL)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800346 return MZ_PARAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800347
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800348 if (global_comment == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700349 global_comment = zip->global_info.comment;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700350
351 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
352 zip->number_disk_with_CD = (uint32_t)disk_number + 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700353 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800354
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700355 zip->cd_pos = mz_stream_tell(zip->stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800356 mz_stream_seek(zip->cd_stream, 0, MZ_STREAM_SEEK_END);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700357 zip->cd_size = (uint32_t)mz_stream_tell(zip->cd_stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800358 mz_stream_seek(zip->cd_stream, 0, MZ_STREAM_SEEK_SET);
359
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700360 err = mz_stream_copy(zip->stream, zip->cd_stream, (int32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800361
362 mz_stream_close(zip->cd_stream);
363 mz_stream_delete(&zip->cd_stream);
364
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800365 // Write the ZIP64 central directory header
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700366 if (pos >= UINT32_MAX || zip->global_info.number_entry > UINT32_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800367 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700368 zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800369
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700370 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800371
372 // Size of this 'zip64 end of central directory'
373 if (err == MZ_OK)
374 err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
375 // Version made by
376 if (err == MZ_OK)
377 err = mz_stream_write_uint16(zip->stream, version_madeby);
378 // Version needed
379 if (err == MZ_OK)
380 err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
381 // Number of this disk
382 if (err == MZ_OK)
383 err = mz_stream_write_uint32(zip->stream, zip->number_disk_with_CD);
384 // Number of the disk with the start of the central directory
385 if (err == MZ_OK)
386 err = mz_stream_write_uint32(zip->stream, zip->number_disk_with_CD);
387 // Total number of entries in the central dir on this disk
388 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700389 err = mz_stream_write_uint64(zip->stream, zip->global_info.number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800390 // Total number of entries in the central dir
391 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700392 err = mz_stream_write_uint64(zip->stream, zip->global_info.number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800393 // Size of the central directory
394 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700395 err = mz_stream_write_uint64(zip->stream, (uint64_t)zip->cd_size);
396 // Offset of start of central directory with respect to the starting disk number
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800397 if (err == MZ_OK)
398 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700399 cd_pos = zip->cd_pos - zip->disk_offset;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800400 err = mz_stream_write_uint64(zip->stream, cd_pos);
401 }
402 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700403 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800404 // Number of the disk with the start of the central directory
405 if (err == MZ_OK)
406 err = mz_stream_write_uint32(zip->stream, zip->number_disk_with_CD);
407 // Relative offset to the end of zip64 central directory
408 if (err == MZ_OK)
409 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700410 cd_pos = zip64_eocd_pos_inzip - zip->cd_pos;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800411 err = mz_stream_write_uint64(zip->stream, cd_pos);
412 }
413 // Number of the disk with the start of the central directory
414 if (err == MZ_OK)
415 err = mz_stream_write_uint32(zip->stream, zip->number_disk_with_CD + 1);
416 }
417
418 // Write the central directory header
419
420 // Signature
421 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700422 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800423 // Number of this disk
424 if (err == MZ_OK)
425 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_disk_with_CD);
426 // Number of the disk with the start of the central directory
427 if (err == MZ_OK)
428 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_disk_with_CD);
429 // Total number of entries in the central dir on this disk
430 if (err == MZ_OK)
431 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700432 if (zip->global_info.number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800433 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
434 else
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700435 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->global_info.number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800436 }
437 // Total number of entries in the central dir
438 if (err == MZ_OK)
439 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700440 if (zip->global_info.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 Moinvaziri8f67ff92017-10-17 23:22:29 -0700443 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->global_info.number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800444 }
445 // Size of the central directory
446 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700447 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800448 // Offset of start of central directory with respect to the starting disk number
449 if (err == MZ_OK)
450 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700451 cd_pos = zip->cd_pos - zip->disk_offset;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800452 if (pos >= UINT32_MAX)
453 err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
454 else
455 err = mz_stream_write_uint32(zip->stream, (uint32_t)cd_pos);
456 }
457
458 // Write global comment
459 if (global_comment != NULL)
460 comment_size = (uint16_t)strlen(global_comment);
461 if (err == MZ_OK)
462 err = mz_stream_write_uint16(zip->stream, comment_size);
463 if (err == MZ_OK)
464 {
465 if (mz_stream_write(zip->stream, global_comment, comment_size) != comment_size)
466 err = MZ_STREAM_ERROR;
467 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700468 return err;
469}
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800470
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700471extern void* ZEXPORT mz_zip_open(void *stream, int32_t mode)
472{
473 mz_zip *zip = NULL;
474 int16_t err = MZ_OK;
475
476
477 zip = (mz_zip *)malloc(sizeof(mz_zip));
478 if (zip == NULL)
479 return NULL;
480
481 memset(zip, 0, sizeof(mz_zip));
482
483 zip->stream = stream;
484 zip->open_mode = mode;
485
486 if (zip->open_mode & MZ_STREAM_MODE_WRITE)
487 {
488 mz_stream_mem_create(&zip->cd_stream);
489 mz_stream_mem_set_grow(zip->cd_stream, 1);
490 mz_stream_mem_open(zip->cd_stream, NULL, MZ_STREAM_MODE_CREATE);
491 }
492
493 if ((zip->open_mode & MZ_STREAM_MODE_READ) || (mode & MZ_STREAM_MODE_APPEND))
494 {
495 err = mz_zip_read_cd(zip);
496
497 if ((err == MZ_OK) && (mode & MZ_STREAM_MODE_APPEND))
498 {
499 // Store central directory in memory
500 if (mz_stream_seek(zip->stream, zip->cd_offset + zip->disk_offset, MZ_STREAM_SEEK_SET) != MZ_OK)
501 err = MZ_STREAM_ERROR;
502 if (mz_stream_copy(zip->cd_stream, zip->stream, (uint32_t)zip->cd_size) != MZ_OK)
503 err = MZ_STREAM_ERROR;
504 if (mz_stream_seek(zip->stream, zip->cd_offset + zip->disk_offset, MZ_STREAM_SEEK_SET) != MZ_OK)
505 err = MZ_STREAM_ERROR;
506 }
507 }
508
509 if (err == MZ_OK)
510 {
511 mz_stream_mem_create(&zip->file_info_stream);
512 mz_stream_mem_set_grow(zip->file_info_stream, 1);
513 mz_stream_mem_set_grow_size(zip->file_info_stream, 4096);
514
515 err = mz_stream_mem_open(zip->file_info_stream, NULL, MZ_STREAM_MODE_CREATE);
516 }
517
518 if (err == MZ_OK)
519 {
520 mz_stream_mem_create(&zip->local_file_info_stream);
521 mz_stream_mem_set_grow(zip->local_file_info_stream, 1);
522 mz_stream_mem_set_grow_size(zip->local_file_info_stream, 4096);
523
524 err = mz_stream_mem_open(zip->local_file_info_stream, NULL, MZ_STREAM_MODE_CREATE);
525 }
526
527 if (err != MZ_OK)
528 {
529 if (zip->file_info_stream != NULL)
530 mz_stream_mem_delete(&zip->file_info_stream);
531 if (zip->local_file_info_stream != NULL)
532 mz_stream_mem_delete(&zip->local_file_info_stream);
533
534 mz_stream_close(zip->cd_stream);
535 mz_stream_delete(&zip->cd_stream);
536
537 if (zip->global_info.comment)
538 free(zip->global_info.comment);
539
540 free(zip);
541 return NULL;
542 }
543
544 return zip;
545}
546
547extern int ZEXPORT mz_zip_close(void *handle, const char *global_comment, uint16_t version_madeby)
548{
549 mz_zip *zip = NULL;
550
551 int16_t err = MZ_OK;
552
553 if (handle == NULL)
554 return MZ_PARAM_ERROR;
555 zip = (mz_zip *)handle;
556
557 if (zip->entry_opened == 1)
558 {
559 err = mz_zip_entry_close(handle);
560 if (err != MZ_OK)
561 return err;
562 }
563
564 if (zip->open_mode & MZ_STREAM_MODE_WRITE)
565 err = mz_zip_write_cd(handle, global_comment, version_madeby);
566
567 mz_stream_mem_close(zip->file_info_stream);
568 mz_stream_mem_delete(&zip->file_info_stream);
569
570 mz_stream_mem_close(zip->local_file_info_stream);
571 mz_stream_mem_delete(&zip->local_file_info_stream);
572
573 if (zip->global_info.comment)
574 free(zip->global_info.comment);
575
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800576 free(zip);
577
578 return err;
579}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700580
581extern int ZEXPORT mz_zip_get_global_info(void *handle, mz_zip_global **global_info)
582{
583 mz_zip *zip = NULL;
584
585 if (handle == NULL || global_info == NULL)
586 return MZ_PARAM_ERROR;
587 zip = (mz_zip *)handle;
588 *global_info = &zip->global_info;
589 return MZ_OK;
590}
591
592static int mz_zip_entry_get_version_needed(mz_zip_file *file_info)
593{
594 mz_zip *zip = NULL;
595 int16_t version_needed = 20;
596
597 if (file_info == NULL)
598 return MZ_PARAM_ERROR;
599
600 if (file_info->version_needed > 0)
601 version_needed = file_info->version_needed;
602
603 // Calculate extra field size
604 if (file_info->zip_64)
605 version_needed = 45;
606#ifdef HAVE_AES
607 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
608 version_needed = 51;
609#endif
610#ifdef HAVE_LZMA
611 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
612 version_needed = 63;
613#endif
614 return version_needed;
615}
616
617// Get info about the current file in the zip file
618static int mz_zip_entry_read_header(void *stream, uint8_t local, mz_zip_file *file_info, void *file_info_stream)
619{
620 uint32_t magic = 0;
621 uint32_t extra_pos = 0;
622 uint16_t extra_header_id = 0;
623 uint16_t extra_data_size = 0;
624 uint16_t value16 = 0;
625 uint32_t value32 = 0;
626 uint64_t value64 = 0;
627 int64_t seek = 0;
628 int16_t err = MZ_OK;
629
630
631 memset(file_info, 0, sizeof(mz_zip_file));
632
633 // Check the magic
634 err = mz_stream_read_uint32(stream, &magic);
635 if (magic == MZ_ZIP_MAGIC_ENDHEADER || magic == MZ_ZIP_MAGIC_ENDHEADER64)
636 err = MZ_END_OF_LIST;
637 else if ((local) && (magic != MZ_ZIP_MAGIC_LOCALHEADER))
638 err = MZ_FORMAT_ERROR;
639 else if ((!local) && (magic != MZ_ZIP_MAGIC_CENTRALHEADER))
640 err = MZ_FORMAT_ERROR;
641
642 // Read header fields
643 if (err == MZ_OK)
644 {
645 if (!local)
646 err = mz_stream_read_uint16(stream, &file_info->version_madeby);
647 if (err == MZ_OK)
648 err = mz_stream_read_uint16(stream, &file_info->version_needed);
649 if (err == MZ_OK)
650 err = mz_stream_read_uint16(stream, &file_info->flag);
651 if (err == MZ_OK)
652 err = mz_stream_read_uint16(stream, &file_info->compression_method);
653 if (err == MZ_OK)
654 err = mz_stream_read_uint32(stream, &file_info->dos_date);
655 if (err == MZ_OK)
656 err = mz_stream_read_uint32(stream, &file_info->crc);
657 if (err == MZ_OK)
658 err = mz_stream_read_uint32(stream, &value32);
659 file_info->compressed_size = value32;
660 if (err == MZ_OK)
661 err = mz_stream_read_uint32(stream, &value32);
662 file_info->uncompressed_size = value32;
663 if (err == MZ_OK)
664 err = mz_stream_read_uint16(stream, &file_info->filename_size);
665 if (err == MZ_OK)
666 err = mz_stream_read_uint16(stream, &file_info->extrafield_size);
667 if (!local)
668 {
669 if (err == MZ_OK)
670 err = mz_stream_read_uint16(stream, &file_info->comment_size);
671 if (err == MZ_OK)
672 err = mz_stream_read_uint16(stream, &value16);
673 file_info->disk_num_start = value16;
674 if (err == MZ_OK)
675 err = mz_stream_read_uint16(stream, &file_info->internal_fa);
676 if (err == MZ_OK)
677 err = mz_stream_read_uint32(stream, &file_info->external_fa);
678 if (err == MZ_OK)
679 err = mz_stream_read_uint32(stream, &value32);
680 file_info->disk_offset = value32;
681 }
682 }
683
684 if (err == MZ_OK)
685 err = mz_stream_seek(file_info_stream, 0, MZ_STREAM_SEEK_SET);
686
687 if ((err == MZ_OK) && (file_info->filename_size > 0))
688 {
689 // Read filename in our memory stream buffer
690 mz_stream_mem_get_buffer(file_info_stream, (void **)&file_info->filename);
691
692 if (err == MZ_OK)
693 err = mz_stream_copy(file_info_stream, stream, file_info->filename_size);
694 if (err == MZ_OK)
695 err = mz_stream_write_uint8(file_info_stream, 0);
696
697 seek += file_info->filename_size + 1;
698 }
699
700 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
701 {
702 mz_stream_mem_get_buffer_at(file_info_stream, seek, (void **)&file_info->extrafield);
703
704 err = mz_stream_copy(file_info_stream, stream, file_info->extrafield_size);
705 if (err == MZ_OK)
706 err = mz_stream_write_uint8(file_info_stream, 0);
707
708 // Seek back and parse the extra field
709 if (err == MZ_OK)
710 err = mz_stream_seek(file_info_stream, seek, MZ_STREAM_SEEK_SET);
711
712 seek += file_info->extrafield_size + 1;
713
714 while ((err == MZ_OK) && (extra_pos < file_info->extrafield_size))
715 {
716 err = mz_stream_read_uint16(file_info_stream, &extra_header_id);
717 if (err == MZ_OK)
718 err = mz_stream_read_uint16(file_info_stream, &extra_data_size);
719
720 // ZIP64 extra field
721 if (extra_header_id == 0x0001)
722 {
723 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
724 err = mz_stream_read_uint64(file_info_stream, &file_info->uncompressed_size);
725 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
726 err = mz_stream_read_uint64(file_info_stream, &file_info->compressed_size);
727 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
728 err = mz_stream_read_uint64(file_info_stream, &value64);
729 file_info->disk_offset = value64;
730 if ((err == MZ_OK) && (file_info->disk_num_start == UINT32_MAX))
731 err = mz_stream_read_uint32(file_info_stream, &file_info->disk_num_start);
732 }
733#ifdef HAVE_AES
734 // AES extra field
735 else if (extra_header_id == 0x9901)
736 {
737 uint8_t value8 = 0;
738 // Verify version info
739 err = mz_stream_read_uint16(file_info_stream, &value16);
740 // Support AE-1 and AE-2
741 if (value16 != 1 && value16 != 2)
742 err = MZ_FORMAT_ERROR;
743 file_info->aes_version = value16;
744 if (err == MZ_OK)
745 err = mz_stream_read_uint8(file_info_stream, &value8);
746 if ((char)value8 != 'A')
747 err = MZ_FORMAT_ERROR;
748 if (err == MZ_OK)
749 err = mz_stream_read_uint8(file_info_stream, &value8);
750 if ((char)value8 != 'E')
751 err = MZ_FORMAT_ERROR;
752 // Get AES encryption strength and actual compression method
753 if (err == MZ_OK)
754 err = mz_stream_read_uint8(file_info_stream, &value8);
755 file_info->aes_encryption_mode = value8;
756 if (err == MZ_OK)
757 err = mz_stream_read_uint16(file_info_stream, &value16);
758 file_info->compression_method = value16;
759 }
760#endif
761 else
762 {
763 err = mz_stream_seek(file_info_stream, extra_data_size, MZ_STREAM_SEEK_CUR);
764 }
765
766 extra_pos += 4 + extra_data_size;
767 }
768 }
769
770 if ((err == MZ_OK) && (file_info->comment_size > 0))
771 {
772 mz_stream_mem_get_buffer_at(file_info_stream, seek, (void **)&file_info->comment);
773
774 err = mz_stream_copy(file_info_stream, stream, file_info->comment_size);
775 if (err == MZ_OK)
776 err = mz_stream_write_uint8(file_info_stream, 0);
777 }
778
779 return err;
780}
781
782static int mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
783{
784 uint16_t extrafield_size = 0;
785 uint16_t extrafield_zip64_size = 0;
786 uint16_t filename_size = 0;
787 uint16_t comment_size = 0;
788 int16_t err = MZ_OK;
789
790 if (file_info == NULL)
791 return MZ_PARAM_ERROR;
792
793 if (file_info->disk_offset >= UINT32_MAX)
794 file_info->zip_64 = 1;
795
796 extrafield_size = file_info->extrafield_size;
797 if (file_info->zip_64)
798 {
799 extrafield_size += 4;
800 if (file_info->uncompressed_size >= UINT32_MAX)
801 extrafield_zip64_size += 8;
802 if (file_info->compressed_size >= UINT32_MAX)
803 extrafield_zip64_size += 8;
804 if (file_info->disk_offset >= UINT32_MAX)
805 extrafield_zip64_size += 8;
806 extrafield_size += extrafield_zip64_size;
807 }
808#ifdef HAVE_AES
809 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
810 extrafield_size += 4 + 7;
811#endif
812
813 file_info->version_needed = mz_zip_entry_get_version_needed(file_info);
814
815 if (local)
816 {
817 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
818 }
819 else
820 {
821 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
822 if (err == MZ_OK)
823 err = mz_stream_write_uint16(stream, file_info->version_madeby);
824 }
825
826 if (err == MZ_OK)
827 err = mz_stream_write_uint16(stream, file_info->version_needed);
828 if (err == MZ_OK)
829 err = mz_stream_write_uint16(stream, file_info->flag);
830 if (err == MZ_OK)
831 {
832#ifdef HAVE_AES
833 if (file_info->aes_version > 0)
834 err = mz_stream_write_uint16(stream, MZ_AES_METHOD);
835 else
836#endif
837 err = mz_stream_write_uint16(stream, file_info->compression_method);
838 }
839 if (err == MZ_OK)
840 err = mz_stream_write_uint32(stream, file_info->dos_date);
841
842 if (err == MZ_OK)
843 err = mz_stream_write_uint32(stream, file_info->crc); // crc
844 if (err == MZ_OK)
845 {
846 if (file_info->compressed_size >= UINT32_MAX) // compr size
847 err = mz_stream_write_uint32(stream, UINT32_MAX);
848 else
849 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
850 }
851 if (err == MZ_OK)
852 {
853 if (file_info->uncompressed_size >= UINT32_MAX) // uncompr size
854 err = mz_stream_write_uint32(stream, UINT32_MAX);
855 else
856 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
857 }
858
859 filename_size = (uint16_t)strlen(file_info->filename);
860 if (err == MZ_OK)
861 err = mz_stream_write_uint16(stream, filename_size);
862 if (err == MZ_OK)
863 err = mz_stream_write_uint16(stream, extrafield_size);
864
865 if (!local)
866 {
867 if (file_info->comment != NULL)
868 comment_size = (uint16_t)strlen(file_info->comment);
869 if (err == MZ_OK)
870 err = mz_stream_write_uint16(stream, comment_size);
871 if (err == MZ_OK)
872 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_num_start);
873 if (err == MZ_OK)
874 err = mz_stream_write_uint16(stream, file_info->internal_fa);
875 if (err == MZ_OK)
876 err = mz_stream_write_uint32(stream, file_info->external_fa);
877 if (err == MZ_OK)
878 {
879 if (file_info->disk_offset >= UINT32_MAX)
880 err = mz_stream_write_uint32(stream, UINT32_MAX);
881 else
882 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
883 }
884 }
885
886 if (err == MZ_OK)
887 {
888 if (mz_stream_write(stream, file_info->filename, filename_size) != filename_size)
889 err = MZ_STREAM_ERROR;
890 }
891 if (err == MZ_OK)
892 {
893 if (mz_stream_write(stream, file_info->extrafield, file_info->extrafield_size) != file_info->extrafield_size)
894 err = MZ_STREAM_ERROR;
895 }
896 // Add ZIP64 extra info header to central directory
897 if ((err == MZ_OK) && (file_info->zip_64))
898 {
899 err = mz_stream_write_uint16(stream, 0x0001);
900 if (err == MZ_OK)
901 err = mz_stream_write_uint16(stream, extrafield_zip64_size);
902 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
903 err = mz_stream_write_uint64(stream, file_info->uncompressed_size);
904 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
905 err = mz_stream_write_uint64(stream, file_info->compressed_size);
906 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
907 err = mz_stream_write_uint64(stream, file_info->disk_offset);
908 }
909
910#ifdef HAVE_AES
911 // Write AES extra info header to central directory
912 if ((err == MZ_OK) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
913 {
914 err = mz_stream_write_uint16(stream, 0x9901);
915 if (err == MZ_OK)
916 err = mz_stream_write_uint16(stream, 7);
917 if (err == MZ_OK)
918 err = mz_stream_write_uint16(stream, file_info->aes_version);
919 if (err == MZ_OK)
920 err = mz_stream_write_uint8(stream, 'A');
921 if (err == MZ_OK)
922 err = mz_stream_write_uint8(stream, 'E');
923 if (err == MZ_OK)
924 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
925 if (err == MZ_OK)
926 err = mz_stream_write_uint16(stream, file_info->compression_method);
927 }
928#endif
929 if ((err == MZ_OK) && (file_info->comment != NULL))
930 {
931 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != MZ_OK)
932 err = MZ_STREAM_ERROR;
933 free(file_info->comment);
934 }
935
936 return err;
937}
938
939static int mz_zip_entry_open_int(void *handle, const char *password)
940{
941 mz_zip *zip = NULL;
942 uint64_t extrafield_local_offset = 0;
943 uint16_t extrafield_local_size = 0;
944 uint32_t size_variable = 0;
945 int64_t max_total_in = 0;
946 int64_t total_in = 0;
947 int64_t footer_size = 0;
948 int16_t err = MZ_OK;
949
950 if (handle == NULL)
951 return MZ_PARAM_ERROR;
952 zip = (mz_zip *)handle;
953
954 if ((zip->file_info.compression_method != MZ_COMPRESS_METHOD_RAW) &&
955 (zip->file_info.compression_method != MZ_COMPRESS_METHOD_DEFLATE))
956 {
957#ifdef HAVE_BZIP2
958 if (zip->file_info.compression_method != MZ_COMPRESS_METHOD_BZIP2)
959 return MZ_INTERNAL_ERROR;
960#elif HAVE_LZMA
961 if (unzip->file_info.compression_method != MZ_METHOD_LZMA)
962 return MZ_INTERNAL_ERROR;
963#else
964 return MZ_INTERNAL_ERROR;
965#endif
966 }
967
968 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL))
969 return MZ_PARAM_ERROR;
970
971 max_total_in = zip->file_info.compressed_size;
972
973 if ((err == Z_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED))
974 {
975#ifdef HAVE_AES
976 if (zip->file_info.aes_version)
977 {
978 mz_stream_aes_create(&zip->crypt_stream);
979 mz_stream_aes_set_password(zip->crypt_stream, password);
980 mz_stream_aes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
981
982 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
983 max_total_in -= footer_size;
984 }
985 else
986#endif
987 {
988#ifdef HAVE_CRYPT
989 uint8_t verify1 = 0;
990 uint8_t verify2 = 0;
991
992 // Info-ZIP modification to ZipCrypto format:
993 // If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time.
994
995 verify1 = (uint8_t)((zip->file_info.dos_date >> 16) & 0xff);
996 verify2 = (uint8_t)((zip->file_info.dos_date >> 8) & 0xff);
997
998 mz_stream_crypt_create(&zip->crypt_stream);
999 mz_stream_crypt_set_password(zip->crypt_stream, password);
1000 mz_stream_crypt_set_verify(zip->crypt_stream, verify1, verify2);
1001#endif
1002 }
1003 }
1004
1005 if (err == MZ_OK)
1006 {
1007 if (zip->crypt_stream == NULL)
1008 {
1009 mz_stream_passthru_create(&zip->crypt_stream);
1010 mz_stream_set_base(zip->crypt_stream, zip->stream);
1011 }
1012 else
1013 {
1014 mz_stream_set_base(zip->crypt_stream, zip->stream);
1015
1016 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
1017 }
1018
1019 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in) == MZ_OK)
1020 max_total_in -= total_in;
1021 }
1022
1023 if (err == MZ_OK)
1024 {
1025 if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_RAW)
1026 {
1027 mz_stream_passthru_create(&zip->compress_stream);
1028 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1029 }
1030 else
1031 {
1032 if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
1033 mz_stream_zlib_create(&zip->compress_stream);
1034#ifdef HAVE_BZIP2
1035 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_BZIP2)
1036 mz_stream_bzip_create(&zip->compress_stream);
1037#endif
1038#ifdef HAVE_LZMA
1039 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
1040 mz_stream_lzma_create(&zip->compress_stream);
1041#endif
1042 else
1043 err = MZ_PARAM_ERROR;
1044
1045 if (err == MZ_OK)
1046 {
1047 if (zip->open_mode & MZ_STREAM_MODE_WRITE)
1048 {
1049 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, zip->compress_info.level);
1050 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_WINDOW_BITS, zip->compress_info.window_bits);
1051 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_MEM_LEVEL, zip->compress_info.mem_level);
1052 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_STRATEGY, zip->compress_info.strategy);
1053 }
1054 else
1055 {
1056 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
1057 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1058 }
1059
1060 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1061
1062 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1063 }
1064 }
1065 }
1066
1067 if (err == Z_OK)
1068 {
1069 mz_stream_crc32_create(&zip->crc32_stream);
1070 mz_stream_set_base(zip->crc32_stream, zip->compress_stream);
1071
1072 err = mz_stream_open(zip->crc32_stream, NULL, zip->open_mode);
1073 }
1074
1075 if (err == MZ_OK)
1076 {
1077 zip->entry_opened = 1;
1078 }
1079
1080 return err;
1081}
1082
1083extern int ZEXPORT mz_zip_entry_read_open(void *handle, int raw, const char *password)
1084{
1085 mz_zip *zip = NULL;
1086 uint64_t extrafield_local_offset = 0;
1087 uint16_t extrafield_local_size = 0;
1088 uint32_t size_variable = 0;
1089 int64_t max_total_in = 0;
1090 int64_t total_in = 0;
1091 int64_t footer_size = 0;
1092 int16_t err = MZ_OK;
1093
1094#if !defined(HAVE_CRYPT) && !defined(HAVE_AES)
1095 if (password != NULL)
1096 return MZ_PARAM_ERROR;
1097#endif
1098 if (handle == NULL)
1099 return MZ_PARAM_ERROR;
1100
1101 zip = (mz_zip *)handle;
1102
1103 if (zip->entry_scanned == 0)
1104 return MZ_PARAM_ERROR;
1105 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL))
1106 return MZ_PARAM_ERROR;
1107
1108 if (zip->file_info.disk_num_start == zip->global_info.number_disk_with_cd)
1109 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1110 else
1111 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_num_start);
1112
1113 err = mz_stream_seek(zip->stream, zip->disk_offset + zip->file_info.disk_offset, MZ_STREAM_SEEK_SET);
1114 if (err == MZ_OK)
1115 err = mz_zip_entry_read_header(zip->stream, 1, &zip->local_file_info, zip->local_file_info_stream);
1116 if (err == MZ_OK)
1117 err = mz_zip_entry_open_int(handle, password);
1118
1119 return err;
1120}
1121
1122extern int ZEXPORT mz_zip_entry_write_open(void *handle, const mz_zip_file *file_info,
1123 const mz_zip_compress *compress_info, const mz_zip_crypt *crypt_info)
1124{
1125 mz_zip *zip = NULL;
1126 int64_t disk_number = 0;
1127 int16_t err = MZ_OK;
1128
1129#if !defined(HAVE_CRYPT) && !defined(HAVE_AES)
1130 if (crypt_info != NULL)
1131 return MZ_PARAM_ERROR;
1132#endif
1133 if (handle == NULL)
1134 return MZ_PARAM_ERROR;
1135 if (file_info == NULL || file_info->filename == NULL)
1136 return MZ_PARAM_ERROR;
1137 if (compress_info == NULL)
1138 return MZ_PARAM_ERROR;
1139 if (crypt_info == NULL)
1140 return MZ_PARAM_ERROR;
1141
1142 zip = (mz_zip *)handle;
1143
1144 if (zip->entry_opened == 1)
1145 {
1146 err = mz_zip_entry_close(handle);
1147 if (err != MZ_OK)
1148 return err;
1149 }
1150
1151 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
1152 memcpy(&zip->compress_info, compress_info, sizeof(mz_zip_compress));
1153
1154 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
1155#ifdef HAVE_LZMA
1156 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
1157#endif
1158 if ((zip->compress_info.level == 8) || (zip->compress_info.level == 9))
1159 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
1160 if (zip->compress_info.level == 2)
1161 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
1162 if (zip->compress_info.level == 1)
1163 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1164
1165 if (crypt_info->password != NULL)
1166 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
1167 else
1168 zip->file_info.flag &= ~MZ_ZIP_FLAG_ENCRYPTED;
1169
1170 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
1171 zip->file_info.disk_num_start = (uint32_t)disk_number;
1172
1173 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
1174 zip->file_info.compressed_size = 0;
1175 zip->file_info.uncompressed_size = 0;
1176 zip->file_info.compression_method = compress_info->method;
1177#ifdef HAVE_AES
1178 if (crypt_info->aes)
1179 {
1180 zip->file_info.aes_version = MZ_AES_VERSION;
1181 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTIONMODE;
1182 }
1183#endif
1184
1185 if (err == MZ_OK)
1186 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
1187 if (err == MZ_OK)
1188 err = mz_zip_entry_open_int(handle, crypt_info->password);
1189
1190 return err;
1191}
1192
1193extern int ZEXPORT mz_zip_entry_read(void *handle, void *buf, uint32_t len)
1194{
1195 mz_zip *zip = NULL;
1196 int32_t read = 0;
1197
1198 if (handle == NULL)
1199 return MZ_PARAM_ERROR;
1200
1201 zip = (mz_zip *)handle;
1202
1203 if (zip->entry_opened == 0)
1204 return MZ_PARAM_ERROR;
1205
1206 if (len == 0)
1207 return 0;
1208 if (len > UINT16_MAX) // Zlib limitation
1209 return MZ_PARAM_ERROR;
1210
1211 read = mz_stream_read(zip->crc32_stream, buf, len);
1212
1213 if (read > 0)
1214 zip->entry_read += read;
1215
1216 return read;
1217}
1218
1219extern int ZEXPORT mz_zip_entry_write(void *handle, const void *buf, uint32_t len)
1220{
1221 mz_zip *zip = NULL;
1222
1223 if (handle == NULL)
1224 return MZ_PARAM_ERROR;
1225
1226 zip = (mz_zip *)handle;
1227
1228 if (zip->entry_opened == 0)
1229 return MZ_PARAM_ERROR;
1230
1231 return mz_stream_write(zip->crc32_stream, buf, len);
1232}
1233
1234extern int ZEXPORT mz_zip_entry_close_raw(void *handle, uint64_t uncompressed_size, uint32_t crc32)
1235{
1236 mz_zip *zip = NULL;
1237 uint64_t compressed_size = 0;
1238 uint16_t extrafield_size = 0;
1239 uint16_t extrafield_zip64_size = 0;
1240 uint16_t filename_size = 0;
1241 uint16_t comment_size = 0;
1242 uint16_t version_needed = 0;
1243 int16_t err = MZ_OK;
1244
1245 if (handle == NULL)
1246 return MZ_PARAM_ERROR;
1247
1248 zip = (mz_zip *)handle;
1249
1250 if (zip->entry_opened == 0)
1251 return MZ_PARAM_ERROR;
1252
1253 mz_stream_close(zip->compress_stream);
1254 crc32 = mz_stream_crc32_get_value(zip->crc32_stream);
1255
1256 if ((zip->open_mode & MZ_STREAM_MODE_WRITE) == 0)
1257 {
1258#ifdef HAVE_AES
1259 // AES zip version AE-1 will expect a valid crc as well
1260 if (zip->aes_version <= 0x0001)
1261#endif
1262 {
1263 if ((zip->entry_read > 0) && (zip->file_info.compression_method != MZ_COMPRESS_METHOD_RAW))
1264 {
1265 if (crc32 != zip->file_info.crc)
1266 err = MZ_CRC_ERROR;
1267 }
1268 }
1269 }
1270 else
1271 {
1272 if ((zip->compress_info.method != MZ_COMPRESS_METHOD_RAW) || (uncompressed_size == 0))
1273 {
1274 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&uncompressed_size);
1275 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
1276 }
1277 }
1278
1279 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
1280 {
1281 mz_stream_set_base(zip->crypt_stream, zip->stream);
1282
1283 err = mz_stream_close(zip->crypt_stream);
1284
1285 if ((zip->compress_info.method != MZ_COMPRESS_METHOD_RAW) || (uncompressed_size == 0))
1286 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
1287 }
1288
1289 mz_stream_delete(&zip->crypt_stream);
1290
1291 mz_stream_delete(&zip->compress_stream);
1292 mz_stream_crc32_delete(&zip->crc32_stream);
1293
1294 if (zip->open_mode & MZ_STREAM_MODE_WRITE)
1295 {
1296 if (err == MZ_OK)
1297 {
1298 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
1299 if (err == MZ_OK)
1300 err = mz_stream_write_uint32(zip->stream, crc32);
1301 if (err == MZ_OK)
1302 {
1303 if (zip->file_info.zip_64)
1304 err = mz_stream_write_uint64(zip->stream, compressed_size);
1305 else
1306 err = mz_stream_write_uint32(zip->stream, (uint32_t)compressed_size);
1307 }
1308 if (err == MZ_OK)
1309 {
1310 if (zip->file_info.zip_64)
1311 err = mz_stream_write_uint64(zip->stream, uncompressed_size);
1312 else
1313 err = mz_stream_write_uint32(zip->stream, (uint32_t)uncompressed_size);
1314 }
1315 }
1316
1317 zip->file_info.crc = crc32;
1318 zip->file_info.compressed_size = compressed_size;
1319 zip->file_info.uncompressed_size = uncompressed_size;
1320
1321 if (err == MZ_OK)
1322 err = mz_zip_entry_write_header(zip->cd_stream, 0, &zip->file_info);
1323
1324 zip->global_info.number_entry += 1;
1325 }
1326
1327 zip->entry_opened = 0;
1328
1329 return err;
1330}
1331
1332extern int ZEXPORT mz_zip_entry_close(void *handle)
1333{
1334 return mz_zip_entry_close_raw(handle, 0, 0);
1335}
1336
1337extern int ZEXPORT mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
1338{
1339 mz_zip *zip = NULL;
1340
1341 if (handle == NULL)
1342 return MZ_PARAM_ERROR;
1343
1344 zip = (mz_zip *)handle;
1345
1346 if (zip->entry_scanned == 0)
1347 return MZ_PARAM_ERROR;
1348
1349 *file_info = &zip->file_info;
1350 return MZ_OK;
1351}
1352
1353extern int ZEXPORT mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
1354{
1355 mz_zip *zip = NULL;
1356
1357 if (handle == NULL)
1358 return MZ_PARAM_ERROR;
1359
1360 zip = (mz_zip *)handle;
1361
1362 if (zip->entry_scanned == 0 || zip->entry_opened == 0)
1363 return MZ_PARAM_ERROR;
1364
1365 *local_file_info = &zip->local_file_info;
1366 return MZ_OK;
1367}
1368
1369static int mz_zip_goto_next_entry_int(void *handle)
1370{
1371 mz_zip *zip = NULL;
1372 int16_t err = MZ_OK;
1373
1374 if (handle == NULL)
1375 return MZ_PARAM_ERROR;
1376
1377 zip = (mz_zip *)handle;
1378 zip->entry_scanned = 0;
1379
1380 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1381
1382 err = mz_stream_seek(zip->stream, zip->disk_offset + zip->cd_current_pos, MZ_STREAM_SEEK_SET);
1383 if (err == MZ_OK)
1384 err = mz_zip_entry_read_header(zip->stream, 0, &zip->file_info, zip->file_info_stream);
1385 if (err == MZ_OK)
1386 zip->entry_scanned = 1;
1387 return err;
1388}
1389
1390extern int ZEXPORT mz_zip_goto_first_entry(void *handle)
1391{
1392 mz_zip *zip = NULL;
1393
1394 if (handle == NULL)
1395 return MZ_PARAM_ERROR;
1396
1397 zip = (mz_zip *)handle;
1398 zip->cd_current_pos = zip->cd_offset;
1399
1400 return mz_zip_goto_next_entry_int(handle);
1401}
1402
1403extern int ZEXPORT mz_zip_goto_next_entry(void *handle)
1404{
1405 mz_zip *zip = NULL;
1406
1407 if (handle == NULL)
1408 return MZ_PARAM_ERROR;
1409
1410 zip = (mz_zip *)handle;
1411 zip->cd_current_pos += MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
1412 zip->file_info.extrafield_size + zip->file_info.comment_size;
1413
1414 return mz_zip_goto_next_entry_int(handle);
1415}
1416
1417extern int ZEXPORT mz_zip_locate_entry(void *handle, const char *filename, mz_filename_compare_cb filename_compare_cb)
1418{
1419 mz_zip *zip = NULL;
1420 int16_t err = MZ_OK;
1421 int32_t result = 0;
1422
1423 if (handle == NULL)
1424 return MZ_PARAM_ERROR;
1425
1426 zip = (mz_zip *)handle;
1427
1428 err = mz_zip_goto_first_entry(handle);
1429
1430 while (err == MZ_OK)
1431 {
1432 if (filename_compare_cb != NULL)
1433 result = filename_compare_cb(handle, zip->file_info.filename, filename);
1434 else
1435 result = strcmp(zip->file_info.filename, filename);
1436
1437 if (result == 0)
1438 return MZ_OK;
1439
1440 err = mz_zip_goto_next_entry(handle);
1441 }
1442
1443 return err;
1444}