@appzung/react-native-code-push 8.3.2 → 9.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CodePush.podspec +3 -3
- package/README.md +3 -1
- package/android/app/build.gradle +2 -0
- package/android/app/src/main/AndroidManifest.xml +1 -2
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java +15 -8
- package/android/build.gradle +3 -0
- package/ios/CodePush/CodePush.m +2 -1
- package/ios/CodePush/SSZipArchive/Info.plist +26 -0
- package/ios/CodePush/SSZipArchive/README.md +1 -1
- package/ios/CodePush/SSZipArchive/SSZipArchive.h +129 -27
- package/ios/CodePush/SSZipArchive/SSZipArchive.m +1119 -314
- package/ios/CodePush/SSZipArchive/SSZipCommon.h +71 -0
- package/ios/CodePush/SSZipArchive/Supporting Files/PrivacyInfo.xcprivacy +23 -0
- package/ios/CodePush/SSZipArchive/include/ZipArchive.h +25 -0
- package/ios/CodePush/SSZipArchive/minizip/LICENSE +17 -0
- package/ios/CodePush/SSZipArchive/minizip/mz.h +273 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_compat.c +1306 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_compat.h +346 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_crypt.c +187 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_crypt.h +65 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_crypt_apple.c +526 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_os.c +348 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_os.h +176 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_os_posix.c +350 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm.c +556 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm.h +132 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_buf.c +383 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_buf.h +42 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_mem.c +269 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_mem.h +48 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_os.h +40 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_os_posix.c +203 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_pkcrypt.c +334 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_pkcrypt.h +46 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_split.c +429 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_split.h +43 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_wzaes.c +360 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_wzaes.h +46 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_zlib.c +389 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_zlib.h +43 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_zip.c +2782 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_zip.h +262 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_zip_rw.c +1942 -0
- package/ios/CodePush/SSZipArchive/minizip/mz_zip_rw.h +285 -0
- package/ios/CodePush.xcodeproj/project.pbxproj +245 -130
- package/ios/PrivacyInfo.xcprivacy +31 -0
- package/package.json +2 -2
- package/ios/CodePush/SSZipArchive/Common.h +0 -81
- package/ios/CodePush/SSZipArchive/aes/aes.h +0 -198
- package/ios/CodePush/SSZipArchive/aes/aes_via_ace.h +0 -541
- package/ios/CodePush/SSZipArchive/aes/aescrypt.c +0 -294
- package/ios/CodePush/SSZipArchive/aes/aeskey.c +0 -548
- package/ios/CodePush/SSZipArchive/aes/aesopt.h +0 -739
- package/ios/CodePush/SSZipArchive/aes/aestab.c +0 -391
- package/ios/CodePush/SSZipArchive/aes/aestab.h +0 -173
- package/ios/CodePush/SSZipArchive/aes/brg_endian.h +0 -126
- package/ios/CodePush/SSZipArchive/aes/brg_types.h +0 -219
- package/ios/CodePush/SSZipArchive/aes/entropy.c +0 -54
- package/ios/CodePush/SSZipArchive/aes/entropy.h +0 -16
- package/ios/CodePush/SSZipArchive/aes/fileenc.c +0 -144
- package/ios/CodePush/SSZipArchive/aes/fileenc.h +0 -121
- package/ios/CodePush/SSZipArchive/aes/hmac.c +0 -145
- package/ios/CodePush/SSZipArchive/aes/hmac.h +0 -103
- package/ios/CodePush/SSZipArchive/aes/prng.c +0 -155
- package/ios/CodePush/SSZipArchive/aes/prng.h +0 -82
- package/ios/CodePush/SSZipArchive/aes/pwd2key.c +0 -103
- package/ios/CodePush/SSZipArchive/aes/pwd2key.h +0 -57
- package/ios/CodePush/SSZipArchive/aes/sha1.c +0 -258
- package/ios/CodePush/SSZipArchive/aes/sha1.h +0 -73
- package/ios/CodePush/SSZipArchive/minizip/crypt.h +0 -130
- package/ios/CodePush/SSZipArchive/minizip/ioapi.c +0 -369
- package/ios/CodePush/SSZipArchive/minizip/ioapi.h +0 -175
- package/ios/CodePush/SSZipArchive/minizip/mztools.c +0 -284
- package/ios/CodePush/SSZipArchive/minizip/mztools.h +0 -31
- package/ios/CodePush/SSZipArchive/minizip/unzip.c +0 -1839
- package/ios/CodePush/SSZipArchive/minizip/unzip.h +0 -248
- package/ios/CodePush/SSZipArchive/minizip/zip.c +0 -1910
- package/ios/CodePush/SSZipArchive/minizip/zip.h +0 -202
|
@@ -3,29 +3,215 @@
|
|
|
3
3
|
// SSZipArchive
|
|
4
4
|
//
|
|
5
5
|
// Created by Sam Soffes on 7/21/10.
|
|
6
|
-
// Copyright (c) Sam Soffes 2010-2015. All rights reserved.
|
|
7
6
|
//
|
|
8
|
-
#import "SSZipArchive.h"
|
|
9
|
-
#include "unzip.h"
|
|
10
|
-
#include "zip.h"
|
|
11
|
-
#import "zlib.h"
|
|
12
|
-
#import "zconf.h"
|
|
13
7
|
|
|
8
|
+
#import "SSZipArchive.h"
|
|
9
|
+
#include "minizip/mz_compat.h"
|
|
10
|
+
#include "minizip/mz_zip.h"
|
|
11
|
+
#include "minizip/mz_os.h"
|
|
12
|
+
#include <zlib.h>
|
|
14
13
|
#include <sys/stat.h>
|
|
15
14
|
|
|
15
|
+
NSString *const SSZipArchiveErrorDomain = @"SSZipArchiveErrorDomain";
|
|
16
|
+
|
|
16
17
|
#define CHUNK 16384
|
|
17
18
|
|
|
19
|
+
int _zipOpenEntry(zipFile entry, NSString *name, const zip_fileinfo *zipfi, int level, NSString *password, BOOL aes);
|
|
20
|
+
BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo);
|
|
21
|
+
|
|
22
|
+
#ifndef API_AVAILABLE
|
|
23
|
+
// Xcode 7- compatibility
|
|
24
|
+
#define API_AVAILABLE(...)
|
|
25
|
+
#endif
|
|
26
|
+
|
|
27
|
+
@interface NSData(SSZipArchive)
|
|
28
|
+
- (NSString *)_base64RFC4648 API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0));
|
|
29
|
+
- (NSString *)_hexString;
|
|
30
|
+
@end
|
|
31
|
+
|
|
32
|
+
@interface NSString (SSZipArchive)
|
|
33
|
+
- (NSString *)_sanitizedPath;
|
|
34
|
+
- (BOOL)_escapesTargetDirectory:(NSString *)targetDirectory;
|
|
35
|
+
@end
|
|
36
|
+
|
|
18
37
|
@interface SSZipArchive ()
|
|
19
|
-
|
|
38
|
+
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
|
20
39
|
@end
|
|
21
40
|
|
|
22
41
|
@implementation SSZipArchive
|
|
23
42
|
{
|
|
43
|
+
/// path for zip file
|
|
24
44
|
NSString *_path;
|
|
25
|
-
NSString *_filename;
|
|
26
45
|
zipFile _zip;
|
|
27
46
|
}
|
|
28
47
|
|
|
48
|
+
#pragma mark - Password check
|
|
49
|
+
|
|
50
|
+
+ (BOOL)isFilePasswordProtectedAtPath:(NSString *)path {
|
|
51
|
+
// Begin opening
|
|
52
|
+
zipFile zip = unzOpen(path.fileSystemRepresentation);
|
|
53
|
+
if (zip == NULL) {
|
|
54
|
+
return NO;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
BOOL passwordProtected = NO;
|
|
58
|
+
int ret = unzGoToFirstFile(zip);
|
|
59
|
+
if (ret == UNZ_OK) {
|
|
60
|
+
do {
|
|
61
|
+
ret = unzOpenCurrentFile(zip);
|
|
62
|
+
if (ret != UNZ_OK) {
|
|
63
|
+
// attempting with an arbitrary password to workaround `unzOpenCurrentFile` limitation on AES encrypted files
|
|
64
|
+
ret = unzOpenCurrentFilePassword(zip, "");
|
|
65
|
+
unzCloseCurrentFile(zip);
|
|
66
|
+
if (ret == UNZ_OK || ret == MZ_PASSWORD_ERROR) {
|
|
67
|
+
passwordProtected = YES;
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
unz_file_info fileInfo = {};
|
|
72
|
+
ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
|
73
|
+
unzCloseCurrentFile(zip);
|
|
74
|
+
if (ret != UNZ_OK) {
|
|
75
|
+
break;
|
|
76
|
+
} else if ((fileInfo.flag & MZ_ZIP_FLAG_ENCRYPTED) == 1) {
|
|
77
|
+
passwordProtected = YES;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ret = unzGoToNextFile(zip);
|
|
82
|
+
} while (ret == UNZ_OK);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
unzClose(zip);
|
|
86
|
+
return passwordProtected;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
+ (BOOL)isPasswordValidForArchiveAtPath:(NSString *)path password:(NSString *)pw error:(NSError **)error {
|
|
90
|
+
if (error) {
|
|
91
|
+
*error = nil;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
zipFile zip = unzOpen(path.fileSystemRepresentation);
|
|
95
|
+
if (zip == NULL) {
|
|
96
|
+
if (error) {
|
|
97
|
+
*error = [NSError errorWithDomain:SSZipArchiveErrorDomain
|
|
98
|
+
code:SSZipArchiveErrorCodeFailedOpenZipFile
|
|
99
|
+
userInfo:@{NSLocalizedDescriptionKey: @"failed to open zip file"}];
|
|
100
|
+
}
|
|
101
|
+
return NO;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Initialize passwordValid to YES (No password required)
|
|
105
|
+
BOOL passwordValid = YES;
|
|
106
|
+
int ret = unzGoToFirstFile(zip);
|
|
107
|
+
if (ret == UNZ_OK) {
|
|
108
|
+
do {
|
|
109
|
+
if (pw.length == 0) {
|
|
110
|
+
ret = unzOpenCurrentFile(zip);
|
|
111
|
+
} else {
|
|
112
|
+
ret = unzOpenCurrentFilePassword(zip, [pw cStringUsingEncoding:NSUTF8StringEncoding]);
|
|
113
|
+
}
|
|
114
|
+
if (ret != UNZ_OK) {
|
|
115
|
+
if (ret != MZ_PASSWORD_ERROR) {
|
|
116
|
+
if (error) {
|
|
117
|
+
*error = [NSError errorWithDomain:SSZipArchiveErrorDomain
|
|
118
|
+
code:SSZipArchiveErrorCodeFailedOpenFileInZip
|
|
119
|
+
userInfo:@{NSLocalizedDescriptionKey: @"failed to open file in zip archive"}];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
passwordValid = NO;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
unz_file_info fileInfo = {};
|
|
126
|
+
ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
|
127
|
+
if (ret != UNZ_OK) {
|
|
128
|
+
if (error) {
|
|
129
|
+
*error = [NSError errorWithDomain:SSZipArchiveErrorDomain
|
|
130
|
+
code:SSZipArchiveErrorCodeFileInfoNotLoadable
|
|
131
|
+
userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}];
|
|
132
|
+
}
|
|
133
|
+
passwordValid = NO;
|
|
134
|
+
break;
|
|
135
|
+
} else if ((fileInfo.flag & 1) == 1) {
|
|
136
|
+
unsigned char buffer[10] = {0};
|
|
137
|
+
int readBytes = unzReadCurrentFile(zip, buffer, (unsigned)MIN(10UL,fileInfo.uncompressed_size));
|
|
138
|
+
if (readBytes < 0) {
|
|
139
|
+
// Let's assume error Z_DATA_ERROR is caused by an invalid password
|
|
140
|
+
// Let's assume other errors are caused by Content Not Readable
|
|
141
|
+
if (readBytes != Z_DATA_ERROR) {
|
|
142
|
+
if (error) {
|
|
143
|
+
*error = [NSError errorWithDomain:SSZipArchiveErrorDomain
|
|
144
|
+
code:SSZipArchiveErrorCodeFileContentNotReadable
|
|
145
|
+
userInfo:@{NSLocalizedDescriptionKey: @"failed to read contents of file entry"}];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
passwordValid = NO;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
passwordValid = YES;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
unzCloseCurrentFile(zip);
|
|
156
|
+
ret = unzGoToNextFile(zip);
|
|
157
|
+
} while (ret == UNZ_OK);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
unzClose(zip);
|
|
161
|
+
return passwordValid;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
+ (NSNumber *)payloadSizeForArchiveAtPath:(NSString *)path error:(NSError **)error {
|
|
165
|
+
if (error) {
|
|
166
|
+
*error = nil;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
zipFile zip = unzOpen(path.fileSystemRepresentation);
|
|
170
|
+
if (zip == NULL) {
|
|
171
|
+
if (error) {
|
|
172
|
+
*error = [NSError errorWithDomain:SSZipArchiveErrorDomain
|
|
173
|
+
code:SSZipArchiveErrorCodeFailedOpenZipFile
|
|
174
|
+
userInfo:@{NSLocalizedDescriptionKey: @"failed to open zip file"}];
|
|
175
|
+
}
|
|
176
|
+
return @0;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
unsigned long long totalSize = 0;
|
|
180
|
+
int ret = unzGoToFirstFile(zip);
|
|
181
|
+
if (ret == UNZ_OK) {
|
|
182
|
+
do {
|
|
183
|
+
ret = unzOpenCurrentFile(zip);
|
|
184
|
+
if (ret != UNZ_OK) {
|
|
185
|
+
if (error) {
|
|
186
|
+
*error = [NSError errorWithDomain:SSZipArchiveErrorDomain
|
|
187
|
+
code:SSZipArchiveErrorCodeFailedOpenFileInZip
|
|
188
|
+
userInfo:@{NSLocalizedDescriptionKey: @"failed to open file in zip archive"}];
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
unz_file_info fileInfo = {};
|
|
193
|
+
ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
|
194
|
+
if (ret != UNZ_OK) {
|
|
195
|
+
if (error) {
|
|
196
|
+
*error = [NSError errorWithDomain:SSZipArchiveErrorDomain
|
|
197
|
+
code:SSZipArchiveErrorCodeFileInfoNotLoadable
|
|
198
|
+
userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}];
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
totalSize += fileInfo.uncompressed_size;
|
|
204
|
+
|
|
205
|
+
unzCloseCurrentFile(zip);
|
|
206
|
+
ret = unzGoToNextFile(zip);
|
|
207
|
+
} while (ret == UNZ_OK);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
unzClose(zip);
|
|
211
|
+
|
|
212
|
+
return [NSNumber numberWithUnsignedLongLong:totalSize];
|
|
213
|
+
}
|
|
214
|
+
|
|
29
215
|
#pragma mark - Unzipping
|
|
30
216
|
|
|
31
217
|
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination
|
|
@@ -33,19 +219,24 @@
|
|
|
33
219
|
return [self unzipFileAtPath:path toDestination:destination delegate:nil];
|
|
34
220
|
}
|
|
35
221
|
|
|
36
|
-
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(NSString *)password error:(NSError **)error
|
|
222
|
+
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(nullable NSString *)password error:(NSError **)error
|
|
37
223
|
{
|
|
38
|
-
return [self unzipFileAtPath:path toDestination:destination overwrite:overwrite password:password error:error delegate:nil progressHandler:nil completionHandler:nil];
|
|
224
|
+
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:error delegate:nil progressHandler:nil completionHandler:nil];
|
|
39
225
|
}
|
|
40
226
|
|
|
41
|
-
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(id<SSZipArchiveDelegate>)delegate
|
|
227
|
+
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(nullable id<SSZipArchiveDelegate>)delegate
|
|
42
228
|
{
|
|
43
|
-
return [self unzipFileAtPath:path toDestination:destination overwrite:YES password:nil error:nil delegate:delegate progressHandler:nil completionHandler:nil];
|
|
229
|
+
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:YES password:nil error:nil delegate:delegate progressHandler:nil completionHandler:nil];
|
|
44
230
|
}
|
|
45
231
|
|
|
46
|
-
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
232
|
+
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
233
|
+
toDestination:(NSString *)destination
|
|
234
|
+
overwrite:(BOOL)overwrite
|
|
235
|
+
password:(nullable NSString *)password
|
|
236
|
+
error:(NSError **)error
|
|
237
|
+
delegate:(nullable id<SSZipArchiveDelegate>)delegate
|
|
47
238
|
{
|
|
48
|
-
return [self unzipFileAtPath:path toDestination:destination overwrite:overwrite password:password error:error delegate:delegate progressHandler:nil completionHandler:nil];
|
|
239
|
+
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:error delegate:delegate progressHandler:nil completionHandler:nil];
|
|
49
240
|
}
|
|
50
241
|
|
|
51
242
|
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
@@ -53,34 +244,102 @@
|
|
|
53
244
|
overwrite:(BOOL)overwrite
|
|
54
245
|
password:(NSString *)password
|
|
55
246
|
progressHandler:(void (^)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
|
|
56
|
-
completionHandler:(void (^)(NSString *path, BOOL succeeded, NSError *error))completionHandler
|
|
247
|
+
completionHandler:(void (^)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
|
|
57
248
|
{
|
|
58
|
-
return [self unzipFileAtPath:path toDestination:destination overwrite:overwrite password:password error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler];
|
|
249
|
+
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler];
|
|
59
250
|
}
|
|
60
251
|
|
|
61
252
|
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
62
253
|
toDestination:(NSString *)destination
|
|
63
|
-
progressHandler:(void (^)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
|
|
64
|
-
completionHandler:(void (^)(NSString *path, BOOL succeeded, NSError *error))completionHandler
|
|
254
|
+
progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
|
|
255
|
+
completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
|
|
65
256
|
{
|
|
66
|
-
return [self unzipFileAtPath:path toDestination:destination overwrite:YES password:nil error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler];
|
|
257
|
+
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:YES password:nil error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler];
|
|
67
258
|
}
|
|
68
259
|
|
|
69
260
|
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
70
261
|
toDestination:(NSString *)destination
|
|
262
|
+
preserveAttributes:(BOOL)preserveAttributes
|
|
71
263
|
overwrite:(BOOL)overwrite
|
|
72
|
-
password:(NSString *)password
|
|
264
|
+
password:(nullable NSString *)password
|
|
265
|
+
error:(NSError * *)error
|
|
266
|
+
delegate:(nullable id<SSZipArchiveDelegate>)delegate
|
|
267
|
+
{
|
|
268
|
+
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:preserveAttributes overwrite:overwrite password:password error:error delegate:delegate progressHandler:nil completionHandler:nil];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
272
|
+
toDestination:(NSString *)destination
|
|
273
|
+
preserveAttributes:(BOOL)preserveAttributes
|
|
274
|
+
overwrite:(BOOL)overwrite
|
|
275
|
+
password:(nullable NSString *)password
|
|
73
276
|
error:(NSError **)error
|
|
74
|
-
delegate:(id<SSZipArchiveDelegate>)delegate
|
|
75
|
-
progressHandler:(void (^)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
|
|
76
|
-
completionHandler:(void (^)(NSString *path, BOOL succeeded, NSError *error))completionHandler
|
|
277
|
+
delegate:(nullable id<SSZipArchiveDelegate>)delegate
|
|
278
|
+
progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
|
|
279
|
+
completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
|
|
280
|
+
{
|
|
281
|
+
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:preserveAttributes overwrite:overwrite nestedZipLevel:0 password:password error:error delegate:delegate progressHandler:progressHandler completionHandler:completionHandler];
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
285
|
+
toDestination:(NSString *)destination
|
|
286
|
+
preserveAttributes:(BOOL)preserveAttributes
|
|
287
|
+
overwrite:(BOOL)overwrite
|
|
288
|
+
nestedZipLevel:(NSInteger)nestedZipLevel
|
|
289
|
+
password:(nullable NSString *)password
|
|
290
|
+
error:(NSError **)error
|
|
291
|
+
delegate:(nullable id<SSZipArchiveDelegate>)delegate
|
|
292
|
+
progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
|
|
293
|
+
completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
|
|
77
294
|
{
|
|
295
|
+
return [self unzipFileAtPath:path
|
|
296
|
+
toDestination:destination
|
|
297
|
+
preserveAttributes:preserveAttributes
|
|
298
|
+
overwrite:overwrite
|
|
299
|
+
symlinksValidWithin:destination
|
|
300
|
+
nestedZipLevel:nestedZipLevel
|
|
301
|
+
password:password
|
|
302
|
+
error:error
|
|
303
|
+
delegate:delegate
|
|
304
|
+
progressHandler:progressHandler
|
|
305
|
+
completionHandler:completionHandler];
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
310
|
+
toDestination:(NSString *)destination
|
|
311
|
+
preserveAttributes:(BOOL)preserveAttributes
|
|
312
|
+
overwrite:(BOOL)overwrite
|
|
313
|
+
symlinksValidWithin:(nullable NSString *)symlinksValidWithin
|
|
314
|
+
nestedZipLevel:(NSInteger)nestedZipLevel
|
|
315
|
+
password:(nullable NSString *)password
|
|
316
|
+
error:(NSError **)error
|
|
317
|
+
delegate:(nullable id<SSZipArchiveDelegate>)delegate
|
|
318
|
+
progressHandler:(void (^_Nullable)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
|
|
319
|
+
completionHandler:(void (^_Nullable)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
|
|
320
|
+
{
|
|
321
|
+
// Guard against empty strings
|
|
322
|
+
if (path.length == 0 || destination.length == 0)
|
|
323
|
+
{
|
|
324
|
+
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"received invalid argument(s)"};
|
|
325
|
+
NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeInvalidArguments userInfo:userInfo];
|
|
326
|
+
if (error)
|
|
327
|
+
{
|
|
328
|
+
*error = err;
|
|
329
|
+
}
|
|
330
|
+
if (completionHandler)
|
|
331
|
+
{
|
|
332
|
+
completionHandler(nil, NO, err);
|
|
333
|
+
}
|
|
334
|
+
return NO;
|
|
335
|
+
}
|
|
336
|
+
|
|
78
337
|
// Begin opening
|
|
79
|
-
zipFile zip = unzOpen(
|
|
338
|
+
zipFile zip = unzOpen(path.fileSystemRepresentation);
|
|
80
339
|
if (zip == NULL)
|
|
81
340
|
{
|
|
82
341
|
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open zip file"};
|
|
83
|
-
NSError *err = [NSError errorWithDomain
|
|
342
|
+
NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenZipFile userInfo:userInfo];
|
|
84
343
|
if (error)
|
|
85
344
|
{
|
|
86
345
|
*error = err;
|
|
@@ -93,17 +352,19 @@
|
|
|
93
352
|
}
|
|
94
353
|
|
|
95
354
|
NSDictionary * fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
|
|
96
|
-
unsigned long long fileSize = [fileAttributes
|
|
355
|
+
unsigned long long fileSize = [[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue];
|
|
97
356
|
unsigned long long currentPosition = 0;
|
|
98
357
|
|
|
99
|
-
unz_global_info
|
|
358
|
+
unz_global_info globalInfo = {};
|
|
100
359
|
unzGetGlobalInfo(zip, &globalInfo);
|
|
101
360
|
|
|
102
361
|
// Begin unzipping
|
|
103
|
-
|
|
362
|
+
int ret = 0;
|
|
363
|
+
ret = unzGoToFirstFile(zip);
|
|
364
|
+
if (ret != UNZ_OK && ret != MZ_END_OF_LIST)
|
|
104
365
|
{
|
|
105
366
|
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open first file in zip file"};
|
|
106
|
-
NSError *err = [NSError errorWithDomain
|
|
367
|
+
NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:userInfo];
|
|
107
368
|
if (error)
|
|
108
369
|
{
|
|
109
370
|
*error = err;
|
|
@@ -112,35 +373,41 @@
|
|
|
112
373
|
{
|
|
113
374
|
completionHandler(nil, NO, err);
|
|
114
375
|
}
|
|
376
|
+
unzClose(zip);
|
|
115
377
|
return NO;
|
|
116
378
|
}
|
|
117
379
|
|
|
118
380
|
BOOL success = YES;
|
|
119
381
|
BOOL canceled = NO;
|
|
120
|
-
int
|
|
121
|
-
int crc_ret =0;
|
|
382
|
+
int crc_ret = 0;
|
|
122
383
|
unsigned char buffer[4096] = {0};
|
|
123
384
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
124
|
-
|
|
385
|
+
NSMutableArray<NSDictionary *> *directoriesModificationDates = [[NSMutableArray alloc] init];
|
|
125
386
|
|
|
126
387
|
// Message delegate
|
|
127
388
|
if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipArchiveAtPath:zipInfo:)]) {
|
|
128
389
|
[delegate zipArchiveWillUnzipArchiveAtPath:path zipInfo:globalInfo];
|
|
129
390
|
}
|
|
130
391
|
if ([delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) {
|
|
131
|
-
[delegate zipArchiveProgressEvent:
|
|
392
|
+
[delegate zipArchiveProgressEvent:currentPosition total:fileSize];
|
|
132
393
|
}
|
|
133
394
|
|
|
134
|
-
NSInteger currentFileNumber =
|
|
395
|
+
NSInteger currentFileNumber = -1;
|
|
396
|
+
NSError *unzippingError;
|
|
135
397
|
do {
|
|
398
|
+
currentFileNumber++;
|
|
399
|
+
if (ret == MZ_END_OF_LIST) {
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
136
402
|
@autoreleasepool {
|
|
137
|
-
if (
|
|
403
|
+
if (password.length == 0) {
|
|
138
404
|
ret = unzOpenCurrentFile(zip);
|
|
139
405
|
} else {
|
|
140
|
-
ret = unzOpenCurrentFilePassword(zip, [password cStringUsingEncoding:
|
|
406
|
+
ret = unzOpenCurrentFilePassword(zip, [password cStringUsingEncoding:NSUTF8StringEncoding]);
|
|
141
407
|
}
|
|
142
408
|
|
|
143
409
|
if (ret != UNZ_OK) {
|
|
410
|
+
unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:@{NSLocalizedDescriptionKey: @"failed to open file in zip file"}];
|
|
144
411
|
success = NO;
|
|
145
412
|
break;
|
|
146
413
|
}
|
|
@@ -151,6 +418,7 @@
|
|
|
151
418
|
|
|
152
419
|
ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
|
153
420
|
if (ret != UNZ_OK) {
|
|
421
|
+
unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFileInfoNotLoadable userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}];
|
|
154
422
|
success = NO;
|
|
155
423
|
unzCloseCurrentFile(zip);
|
|
156
424
|
break;
|
|
@@ -162,7 +430,8 @@
|
|
|
162
430
|
if ([delegate respondsToSelector:@selector(zipArchiveShouldUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
|
|
163
431
|
if (![delegate zipArchiveShouldUnzipFileAtIndex:currentFileNumber
|
|
164
432
|
totalFiles:(NSInteger)globalInfo.number_entry
|
|
165
|
-
archivePath:path
|
|
433
|
+
archivePath:path
|
|
434
|
+
fileInfo:fileInfo]) {
|
|
166
435
|
success = NO;
|
|
167
436
|
canceled = YES;
|
|
168
437
|
break;
|
|
@@ -177,59 +446,67 @@
|
|
|
177
446
|
}
|
|
178
447
|
|
|
179
448
|
char *filename = (char *)malloc(fileInfo.size_filename + 1);
|
|
449
|
+
if (filename == NULL)
|
|
450
|
+
{
|
|
451
|
+
success = NO;
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
|
|
180
455
|
unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
|
|
181
456
|
filename[fileInfo.size_filename] = '\0';
|
|
182
457
|
|
|
183
|
-
|
|
184
|
-
// Determine whether this is a symbolic link:
|
|
185
|
-
// - File is stored with 'version made by' value of UNIX (3),
|
|
186
|
-
// as per http://www.pkware.com/documents/casestudies/APPNOTE.TXT
|
|
187
|
-
// in the upper byte of the version field.
|
|
188
|
-
// - BSD4.4 st_mode constants are stored in the high 16 bits of the
|
|
189
|
-
// external file attributes (defacto standard, verified against libarchive)
|
|
190
|
-
//
|
|
191
|
-
// The original constants can be found here:
|
|
192
|
-
// http://minnie.tuhs.org/cgi-bin/utree.pl?file=4.4BSD/usr/include/sys/stat.h
|
|
193
|
-
//
|
|
194
|
-
const uLong ZipUNIXVersion = 3;
|
|
195
|
-
const uLong BSD_SFMT = 0170000;
|
|
196
|
-
const uLong BSD_IFLNK = 0120000;
|
|
458
|
+
BOOL fileIsSymbolicLink = _fileIsSymbolicLink(&fileInfo);
|
|
197
459
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
460
|
+
NSString * strPath = [SSZipArchive _filenameStringWithCString:filename
|
|
461
|
+
version_made_by:fileInfo.version
|
|
462
|
+
general_purpose_flag:fileInfo.flag
|
|
463
|
+
size:fileInfo.size_filename];
|
|
464
|
+
if ([strPath hasPrefix:@"__MACOSX/"]) {
|
|
465
|
+
// ignoring resource forks: https://superuser.com/questions/104500/what-is-macosx-folder
|
|
466
|
+
unzCloseCurrentFile(zip);
|
|
467
|
+
ret = unzGoToNextFile(zip);
|
|
468
|
+
free(filename);
|
|
469
|
+
continue;
|
|
201
470
|
}
|
|
202
471
|
|
|
203
472
|
// Check if it contains directory
|
|
204
|
-
NSString *strPath = @(filename);
|
|
205
473
|
BOOL isDirectory = NO;
|
|
206
474
|
if (filename[fileInfo.size_filename-1] == '/' || filename[fileInfo.size_filename-1] == '\\') {
|
|
207
475
|
isDirectory = YES;
|
|
208
476
|
}
|
|
209
477
|
free(filename);
|
|
210
478
|
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
479
|
+
// Sanitize paths in the file name.
|
|
480
|
+
strPath = [strPath _sanitizedPath];
|
|
481
|
+
if (!strPath.length) {
|
|
482
|
+
// if filename data is unsalvageable, we default to currentFileNumber
|
|
483
|
+
strPath = @(currentFileNumber).stringValue;
|
|
214
484
|
}
|
|
215
485
|
|
|
216
486
|
NSString *fullPath = [destination stringByAppendingPathComponent:strPath];
|
|
217
487
|
NSError *err = nil;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
488
|
+
NSDictionary *directoryAttr;
|
|
489
|
+
if (preserveAttributes) {
|
|
490
|
+
NSDate *modDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.mz_dos_date];
|
|
491
|
+
directoryAttr = @{NSFileCreationDate: modDate, NSFileModificationDate: modDate};
|
|
492
|
+
[directoriesModificationDates addObject: @{@"path": fullPath, @"modDate": modDate}];
|
|
493
|
+
}
|
|
221
494
|
if (isDirectory) {
|
|
222
|
-
[fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:directoryAttr
|
|
495
|
+
[fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:directoryAttr error:&err];
|
|
223
496
|
} else {
|
|
224
|
-
[fileManager createDirectoryAtPath:
|
|
497
|
+
[fileManager createDirectoryAtPath:fullPath.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:directoryAttr error:&err];
|
|
225
498
|
}
|
|
226
|
-
if (
|
|
499
|
+
if (err != nil) {
|
|
500
|
+
if ([err.domain isEqualToString:NSCocoaErrorDomain] &&
|
|
501
|
+
err.code == 640) {
|
|
502
|
+
unzippingError = err;
|
|
503
|
+
unzCloseCurrentFile(zip);
|
|
504
|
+
success = NO;
|
|
505
|
+
break;
|
|
506
|
+
}
|
|
227
507
|
NSLog(@"[SSZipArchive] Error: %@", err.localizedDescription);
|
|
228
508
|
}
|
|
229
509
|
|
|
230
|
-
if(!fileIsSymbolicLink)
|
|
231
|
-
[directoriesModificationDates addObject: @{@"path": fullPath, @"modDate": modDate}];
|
|
232
|
-
|
|
233
510
|
if ([fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite) {
|
|
234
511
|
//FIXME: couldBe CRC Check?
|
|
235
512
|
unzCloseCurrentFile(zip);
|
|
@@ -237,92 +514,208 @@
|
|
|
237
514
|
continue;
|
|
238
515
|
}
|
|
239
516
|
|
|
240
|
-
if (!fileIsSymbolicLink) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
517
|
+
if (isDirectory && !fileIsSymbolicLink) {
|
|
518
|
+
// nothing to read/write for a directory
|
|
519
|
+
} else if (!fileIsSymbolicLink) {
|
|
520
|
+
// ensure we are not creating stale file entries
|
|
521
|
+
int readBytes = unzReadCurrentFile(zip, buffer, 4096);
|
|
522
|
+
if (readBytes >= 0) {
|
|
523
|
+
FILE *fp = fopen(fullPath.fileSystemRepresentation, "wb");
|
|
524
|
+
while (fp) {
|
|
525
|
+
if (readBytes > 0) {
|
|
526
|
+
if (0 == fwrite(buffer, readBytes, 1, fp)) {
|
|
527
|
+
if (ferror(fp)) {
|
|
528
|
+
NSString *message = [NSString stringWithFormat:@"Failed to write file (check your free space)"];
|
|
529
|
+
NSLog(@"[SSZipArchive] %@", message);
|
|
530
|
+
success = NO;
|
|
531
|
+
unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedToWriteFile userInfo:@{NSLocalizedDescriptionKey: message}];
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
readBytes = unzReadCurrentFile(zip, buffer, 4096);
|
|
539
|
+
if (readBytes < 0) {
|
|
540
|
+
// Let's assume error Z_DATA_ERROR is caused by an invalid password
|
|
541
|
+
// Let's assume other errors are caused by Content Not Readable
|
|
542
|
+
success = NO;
|
|
257
543
|
}
|
|
258
544
|
}
|
|
259
545
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
// Set the original datetime property
|
|
263
|
-
if (fileInfo.dosDate != 0) {
|
|
264
|
-
NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.dosDate];
|
|
265
|
-
NSDictionary *attr = @{NSFileModificationDate: orgDate};
|
|
546
|
+
if (fp) {
|
|
547
|
+
fclose(fp);
|
|
266
548
|
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
549
|
+
if (nestedZipLevel
|
|
550
|
+
&& [fullPath.pathExtension.lowercaseString isEqualToString:@"zip"]
|
|
551
|
+
&& [self unzipFileAtPath:fullPath
|
|
552
|
+
toDestination:fullPath.stringByDeletingLastPathComponent
|
|
553
|
+
preserveAttributes:preserveAttributes
|
|
554
|
+
overwrite:overwrite
|
|
555
|
+
symlinksValidWithin:symlinksValidWithin
|
|
556
|
+
nestedZipLevel:nestedZipLevel - 1
|
|
557
|
+
password:password
|
|
558
|
+
error:nil
|
|
559
|
+
delegate:nil
|
|
560
|
+
progressHandler:nil
|
|
561
|
+
completionHandler:nil]) {
|
|
562
|
+
[directoriesModificationDates removeLastObject];
|
|
563
|
+
[[NSFileManager defaultManager] removeItemAtPath:fullPath error:nil];
|
|
564
|
+
} else if (preserveAttributes) {
|
|
565
|
+
|
|
566
|
+
// Set the original datetime property
|
|
567
|
+
if (fileInfo.mz_dos_date != 0) {
|
|
568
|
+
NSDate *orgDate = [[self class] _dateWithMSDOSFormat:(UInt32)fileInfo.mz_dos_date];
|
|
569
|
+
NSDictionary *attr = @{NSFileModificationDate: orgDate};
|
|
570
|
+
|
|
571
|
+
if (attr) {
|
|
572
|
+
if (![fileManager setAttributes:attr ofItemAtPath:fullPath error:nil]) {
|
|
573
|
+
// Can't set attributes
|
|
574
|
+
NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting modification date");
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Set the original permissions on the file (+read/write to solve #293)
|
|
580
|
+
uLong permissions = fileInfo.external_fa >> 16 | 0b110000000;
|
|
581
|
+
if (permissions != 0) {
|
|
582
|
+
// Store it into a NSNumber
|
|
583
|
+
NSNumber *permissionsValue = @(permissions);
|
|
584
|
+
|
|
585
|
+
// Retrieve any existing attributes
|
|
586
|
+
NSMutableDictionary *attrs = [[NSMutableDictionary alloc] initWithDictionary:[fileManager attributesOfItemAtPath:fullPath error:nil]];
|
|
587
|
+
|
|
588
|
+
// Set the value in the attributes dict
|
|
589
|
+
[attrs setObject:permissionsValue forKey:NSFilePosixPermissions];
|
|
590
|
+
|
|
591
|
+
// Update attributes
|
|
592
|
+
if (![fileManager setAttributes:attrs ofItemAtPath:fullPath error:nil]) {
|
|
593
|
+
// Unable to set the permissions attribute
|
|
594
|
+
NSLog(@"[SSZipArchive] Failed to set attributes - whilst setting permissions");
|
|
595
|
+
}
|
|
271
596
|
}
|
|
272
597
|
}
|
|
273
598
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
599
|
+
else
|
|
600
|
+
{
|
|
601
|
+
// if we couldn't open file descriptor we can validate global errno to see the reason
|
|
602
|
+
int errnoSave = errno;
|
|
603
|
+
BOOL isSeriousError = NO;
|
|
604
|
+
switch (errnoSave) {
|
|
605
|
+
case EISDIR:
|
|
606
|
+
// Is a directory
|
|
607
|
+
// assumed case
|
|
608
|
+
break;
|
|
609
|
+
|
|
610
|
+
case ENOSPC:
|
|
611
|
+
case EMFILE:
|
|
612
|
+
// No space left on device
|
|
613
|
+
// or
|
|
614
|
+
// Too many open files
|
|
615
|
+
isSeriousError = YES;
|
|
616
|
+
break;
|
|
617
|
+
|
|
618
|
+
default:
|
|
619
|
+
// ignore case
|
|
620
|
+
// Just log the error
|
|
621
|
+
{
|
|
622
|
+
NSError *errorObject = [NSError errorWithDomain:NSPOSIXErrorDomain
|
|
623
|
+
code:errnoSave
|
|
624
|
+
userInfo:nil];
|
|
625
|
+
NSLog(@"[SSZipArchive] Failed to open file on unzipping.(%@)", errorObject);
|
|
626
|
+
}
|
|
627
|
+
break;
|
|
291
628
|
}
|
|
292
629
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
630
|
+
if (isSeriousError) {
|
|
631
|
+
// serious case
|
|
632
|
+
unzippingError = [NSError errorWithDomain:NSPOSIXErrorDomain
|
|
633
|
+
code:errnoSave
|
|
634
|
+
userInfo:nil];
|
|
635
|
+
unzCloseCurrentFile(zip);
|
|
636
|
+
// Log the error
|
|
637
|
+
NSLog(@"[SSZipArchive] Failed to open file on unzipping.(%@)", unzippingError);
|
|
638
|
+
|
|
639
|
+
// Break unzipping
|
|
640
|
+
success = NO;
|
|
641
|
+
break;
|
|
642
|
+
}
|
|
296
643
|
}
|
|
644
|
+
} else {
|
|
645
|
+
// Let's assume error Z_DATA_ERROR is caused by an invalid password
|
|
646
|
+
// Let's assume other errors are caused by Content Not Readable
|
|
647
|
+
success = NO;
|
|
648
|
+
break;
|
|
297
649
|
}
|
|
298
650
|
}
|
|
299
651
|
else
|
|
300
652
|
{
|
|
301
653
|
// Assemble the path for the symbolic link
|
|
302
|
-
NSMutableString*
|
|
654
|
+
NSMutableString *destinationPath = [NSMutableString string];
|
|
303
655
|
int bytesRead = 0;
|
|
304
|
-
while((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0)
|
|
656
|
+
while ((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0)
|
|
305
657
|
{
|
|
306
|
-
buffer[bytesRead] =
|
|
307
|
-
[destinationPath appendString:@((const char*)buffer)];
|
|
658
|
+
buffer[bytesRead] = 0;
|
|
659
|
+
[destinationPath appendString:@((const char *)buffer)];
|
|
660
|
+
}
|
|
661
|
+
if (bytesRead < 0) {
|
|
662
|
+
// Let's assume error Z_DATA_ERROR is caused by an invalid password
|
|
663
|
+
// Let's assume other errors are caused by Content Not Readable
|
|
664
|
+
success = NO;
|
|
665
|
+
break;
|
|
308
666
|
}
|
|
309
667
|
|
|
310
|
-
//
|
|
311
|
-
|
|
312
|
-
|
|
668
|
+
// compose symlink full path
|
|
669
|
+
NSString *symlinkFullDestinationPath = destinationPath;
|
|
670
|
+
if (![symlinkFullDestinationPath isAbsolutePath]) {
|
|
671
|
+
symlinkFullDestinationPath = [[fullPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:destinationPath];
|
|
672
|
+
}
|
|
313
673
|
|
|
314
|
-
if(
|
|
315
|
-
|
|
316
|
-
NSLog(@"
|
|
674
|
+
if (symlinksValidWithin != nil && [symlinkFullDestinationPath _escapesTargetDirectory: symlinksValidWithin]) {
|
|
675
|
+
NSString *message = [NSString stringWithFormat:@"Symlink escapes target directory \"~%@ -> %@\"", strPath, destinationPath];
|
|
676
|
+
NSLog(@"[SSZipArchive] %@", message);
|
|
677
|
+
success = NO;
|
|
678
|
+
unzippingError = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeSymlinkEscapesTargetDirectory userInfo:@{NSLocalizedDescriptionKey: message}];
|
|
679
|
+
} else {
|
|
680
|
+
// Check if the symlink exists and delete it if we're overwriting
|
|
681
|
+
if (overwrite)
|
|
682
|
+
{
|
|
683
|
+
if ([fileManager fileExistsAtPath:fullPath])
|
|
684
|
+
{
|
|
685
|
+
NSError *localError = nil;
|
|
686
|
+
BOOL removeSuccess = [fileManager removeItemAtPath:fullPath error:&localError];
|
|
687
|
+
if (!removeSuccess)
|
|
688
|
+
{
|
|
689
|
+
NSString *message = [NSString stringWithFormat:@"Failed to delete existing symbolic link at \"%@\"", localError.localizedDescription];
|
|
690
|
+
NSLog(@"[SSZipArchive] %@", message);
|
|
691
|
+
success = NO;
|
|
692
|
+
unzippingError = [NSError errorWithDomain:SSZipArchiveErrorDomain code:localError.code userInfo:@{NSLocalizedDescriptionKey: message}];
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Create the symbolic link (making sure it stays relative if it was relative before)
|
|
698
|
+
int symlinkError = symlink([destinationPath cStringUsingEncoding:NSUTF8StringEncoding],
|
|
699
|
+
[fullPath cStringUsingEncoding:NSUTF8StringEncoding]);
|
|
700
|
+
|
|
701
|
+
if (symlinkError != 0)
|
|
702
|
+
{
|
|
703
|
+
// Bubble the error up to the completion handler
|
|
704
|
+
NSString *message = [NSString stringWithFormat:@"Failed to create symbolic link at \"%@\" to \"%@\" - symlink() error code: %d", fullPath, destinationPath, errno];
|
|
705
|
+
NSLog(@"[SSZipArchive] %@", message);
|
|
706
|
+
success = NO;
|
|
707
|
+
unzippingError = [NSError errorWithDomain:NSPOSIXErrorDomain code:symlinkError userInfo:@{NSLocalizedDescriptionKey: message}];
|
|
708
|
+
}
|
|
317
709
|
}
|
|
318
710
|
}
|
|
319
711
|
|
|
320
|
-
crc_ret = unzCloseCurrentFile(
|
|
321
|
-
if (crc_ret ==
|
|
322
|
-
//CRC ERROR
|
|
323
|
-
|
|
712
|
+
crc_ret = unzCloseCurrentFile(zip);
|
|
713
|
+
if (crc_ret == MZ_CRC_ERROR) {
|
|
714
|
+
// CRC ERROR
|
|
715
|
+
success = NO;
|
|
716
|
+
break;
|
|
324
717
|
}
|
|
325
|
-
ret = unzGoToNextFile(
|
|
718
|
+
ret = unzGoToNextFile(zip);
|
|
326
719
|
|
|
327
720
|
// Message delegate
|
|
328
721
|
if ([delegate respondsToSelector:@selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
|
|
@@ -333,13 +726,12 @@
|
|
|
333
726
|
archivePath:path unzippedFilePath: fullPath];
|
|
334
727
|
}
|
|
335
728
|
|
|
336
|
-
currentFileNumber++;
|
|
337
729
|
if (progressHandler)
|
|
338
730
|
{
|
|
339
731
|
progressHandler(strPath, fileInfo, currentFileNumber, globalInfo.number_entry);
|
|
340
732
|
}
|
|
341
733
|
}
|
|
342
|
-
} while(ret == UNZ_OK &&
|
|
734
|
+
} while (ret == UNZ_OK && success);
|
|
343
735
|
|
|
344
736
|
// Close
|
|
345
737
|
unzClose(zip);
|
|
@@ -347,20 +739,18 @@
|
|
|
347
739
|
// The process of decompressing the .zip archive causes the modification times on the folders
|
|
348
740
|
// to be set to the present time. So, when we are done, they need to be explicitly set.
|
|
349
741
|
// set the modification date on all of the directories.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
742
|
+
if (success && preserveAttributes) {
|
|
743
|
+
NSError * err = nil;
|
|
744
|
+
for (NSDictionary * d in directoriesModificationDates) {
|
|
745
|
+
if (![[NSFileManager defaultManager] setAttributes:@{NSFileModificationDate: [d objectForKey:@"modDate"]} ofItemAtPath:[d objectForKey:@"path"] error:&err]) {
|
|
746
|
+
NSLog(@"[SSZipArchive] Set attributes failed for directory: %@.", [d objectForKey:@"path"]);
|
|
747
|
+
}
|
|
748
|
+
if (err) {
|
|
749
|
+
NSLog(@"[SSZipArchive] Error setting directory file modification date attribute: %@", err.localizedDescription);
|
|
750
|
+
}
|
|
357
751
|
}
|
|
358
752
|
}
|
|
359
753
|
|
|
360
|
-
#if !__has_feature(objc_arc)
|
|
361
|
-
[directoriesModificationDates release];
|
|
362
|
-
#endif
|
|
363
|
-
|
|
364
754
|
// Message delegate
|
|
365
755
|
if (success && [delegate respondsToSelector:@selector(zipArchiveDidUnzipArchiveAtPath:zipInfo:unzippedPath:)]) {
|
|
366
756
|
[delegate zipArchiveDidUnzipArchiveAtPath:path zipInfo:globalInfo unzippedPath:destination];
|
|
@@ -370,94 +760,277 @@
|
|
|
370
760
|
[delegate zipArchiveProgressEvent:fileSize total:fileSize];
|
|
371
761
|
}
|
|
372
762
|
|
|
763
|
+
NSError *retErr = nil;
|
|
764
|
+
if (crc_ret == MZ_CRC_ERROR)
|
|
765
|
+
{
|
|
766
|
+
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"crc check failed for file"};
|
|
767
|
+
retErr = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFileInfoNotLoadable userInfo:userInfo];
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (error) {
|
|
771
|
+
if (unzippingError) {
|
|
772
|
+
*error = unzippingError;
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
*error = retErr;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
373
778
|
if (completionHandler)
|
|
374
779
|
{
|
|
375
|
-
|
|
780
|
+
if (unzippingError) {
|
|
781
|
+
completionHandler(path, success, unzippingError);
|
|
782
|
+
}
|
|
783
|
+
else
|
|
784
|
+
{
|
|
785
|
+
completionHandler(path, success, retErr);
|
|
786
|
+
}
|
|
376
787
|
}
|
|
377
788
|
return success;
|
|
378
789
|
}
|
|
379
790
|
|
|
380
791
|
#pragma mark - Zipping
|
|
381
|
-
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)paths
|
|
792
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray<NSString *> *)paths
|
|
382
793
|
{
|
|
383
794
|
return [SSZipArchive createZipFileAtPath:path withFilesAtPaths:paths withPassword:nil];
|
|
384
795
|
}
|
|
385
|
-
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath{
|
|
796
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath {
|
|
386
797
|
return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath withPassword:nil];
|
|
387
798
|
}
|
|
388
799
|
|
|
389
|
-
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)
|
|
390
|
-
return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:
|
|
800
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory {
|
|
801
|
+
return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:keepParentDirectory withPassword:nil];
|
|
391
802
|
}
|
|
392
803
|
|
|
393
|
-
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray *)paths withPassword:(NSString *)password
|
|
804
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray<NSString *> *)paths withPassword:(NSString *)password {
|
|
805
|
+
return [self createZipFileAtPath:path withFilesAtPaths:paths withPassword:password progressHandler:nil];
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray<NSString *> *)paths withPassword:(NSString *)password progressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler
|
|
394
809
|
{
|
|
395
|
-
BOOL success = NO;
|
|
396
810
|
SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path];
|
|
397
|
-
|
|
811
|
+
BOOL success = [zipArchive open];
|
|
812
|
+
if (success) {
|
|
813
|
+
NSUInteger total = paths.count, complete = 0;
|
|
398
814
|
for (NSString *filePath in paths) {
|
|
399
|
-
[zipArchive writeFile:filePath withPassword:password];
|
|
815
|
+
success &= [zipArchive writeFile:filePath withPassword:password];
|
|
816
|
+
if (progressHandler) {
|
|
817
|
+
complete++;
|
|
818
|
+
progressHandler(complete, total);
|
|
819
|
+
}
|
|
400
820
|
}
|
|
401
|
-
success
|
|
821
|
+
success &= [zipArchive close];
|
|
402
822
|
}
|
|
403
|
-
|
|
404
|
-
#if !__has_feature(objc_arc)
|
|
405
|
-
[zipArchive release];
|
|
406
|
-
#endif
|
|
407
|
-
|
|
408
823
|
return success;
|
|
409
824
|
}
|
|
410
825
|
|
|
411
|
-
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath
|
|
412
|
-
return [
|
|
826
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath withPassword:(nullable NSString *)password {
|
|
827
|
+
return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:NO withPassword:password];
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory withPassword:(nullable NSString *)password {
|
|
832
|
+
return [SSZipArchive createZipFileAtPath:path
|
|
833
|
+
withContentsOfDirectory:directoryPath
|
|
834
|
+
keepParentDirectory:keepParentDirectory
|
|
835
|
+
withPassword:password
|
|
836
|
+
andProgressHandler:nil
|
|
837
|
+
];
|
|
413
838
|
}
|
|
414
839
|
|
|
840
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path
|
|
841
|
+
withContentsOfDirectory:(NSString *)directoryPath
|
|
842
|
+
keepParentDirectory:(BOOL)keepParentDirectory
|
|
843
|
+
withPassword:(nullable NSString *)password
|
|
844
|
+
andProgressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler {
|
|
845
|
+
return [self createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:keepParentDirectory compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES progressHandler:progressHandler];
|
|
846
|
+
}
|
|
415
847
|
|
|
416
|
-
+ (BOOL)createZipFileAtPath:(NSString *)path
|
|
417
|
-
|
|
848
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path
|
|
849
|
+
withContentsOfDirectory:(NSString *)directoryPath
|
|
850
|
+
keepParentDirectory:(BOOL)keepParentDirectory
|
|
851
|
+
compressionLevel:(int)compressionLevel
|
|
852
|
+
password:(nullable NSString *)password
|
|
853
|
+
AES:(BOOL)aes
|
|
854
|
+
progressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler {
|
|
418
855
|
|
|
419
|
-
NSFileManager *fileManager = nil;
|
|
420
856
|
SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path];
|
|
421
|
-
|
|
422
|
-
if (
|
|
423
|
-
// use a local
|
|
424
|
-
fileManager = [[NSFileManager alloc] init];
|
|
857
|
+
BOOL success = [zipArchive open];
|
|
858
|
+
if (success) {
|
|
859
|
+
// use a local fileManager (queue/thread compatibility)
|
|
860
|
+
NSFileManager *fileManager = [[NSFileManager alloc] init];
|
|
425
861
|
NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:directoryPath];
|
|
426
|
-
NSString *
|
|
427
|
-
|
|
428
|
-
|
|
862
|
+
NSArray<NSString *> *allObjects = dirEnumerator.allObjects;
|
|
863
|
+
NSUInteger total = allObjects.count, complete = 0;
|
|
864
|
+
if (keepParentDirectory && !total) {
|
|
865
|
+
allObjects = @[@""];
|
|
866
|
+
total = 1;
|
|
867
|
+
}
|
|
868
|
+
for (__strong NSString *fileName in allObjects) {
|
|
429
869
|
NSString *fullFilePath = [directoryPath stringByAppendingPathComponent:fileName];
|
|
870
|
+
if ([fullFilePath isEqualToString:path]) {
|
|
871
|
+
NSLog(@"[SSZipArchive] the archive path and the file path: %@ are the same, which is forbidden.", fullFilePath);
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
if (keepParentDirectory) {
|
|
876
|
+
fileName = [directoryPath.lastPathComponent stringByAppendingPathComponent:fileName];
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
BOOL isDir;
|
|
430
880
|
[fileManager fileExistsAtPath:fullFilePath isDirectory:&isDir];
|
|
431
881
|
if (!isDir) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
882
|
+
// file
|
|
883
|
+
success &= [zipArchive writeFileAtPath:fullFilePath withFileName:fileName compressionLevel:compressionLevel password:password AES:aes];
|
|
884
|
+
} else {
|
|
885
|
+
// directory
|
|
886
|
+
if (![fileManager enumeratorAtPath:fullFilePath].nextObject) {
|
|
887
|
+
// empty directory
|
|
888
|
+
success &= [zipArchive writeFolderAtPath:fullFilePath withFolderName:fileName withPassword:password];
|
|
435
889
|
}
|
|
436
|
-
[zipArchive writeFileAtPath:fullFilePath withFileName:fileName withPassword:password];
|
|
437
890
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
891
|
+
if (progressHandler) {
|
|
892
|
+
complete++;
|
|
893
|
+
progressHandler(complete, total);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
success &= [zipArchive close];
|
|
897
|
+
}
|
|
898
|
+
return success;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray<NSString *> *)paths withPassword:(nullable NSString *)password keepSymlinks:(BOOL)keeplinks {
|
|
902
|
+
if (!keeplinks) {
|
|
903
|
+
return [SSZipArchive createZipFileAtPath:path withFilesAtPaths:paths withPassword:password];
|
|
904
|
+
} else {
|
|
905
|
+
SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path];
|
|
906
|
+
BOOL success = [zipArchive open];
|
|
907
|
+
if (success) {
|
|
908
|
+
for (NSString *filePath in paths) {
|
|
909
|
+
//is symlink
|
|
910
|
+
if (mz_os_is_symlink(filePath.fileSystemRepresentation) == MZ_OK) {
|
|
911
|
+
success &= [zipArchive writeSymlinkFileAtPath:filePath withFileName:nil compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES];
|
|
912
|
+
} else {
|
|
913
|
+
success &= [zipArchive writeFile:filePath withPassword:password];
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
success &= [zipArchive close];
|
|
917
|
+
}
|
|
918
|
+
return success;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
+ (BOOL)createZipFileAtPath:(NSString *)path
|
|
923
|
+
withContentsOfDirectory:(NSString *)directoryPath
|
|
924
|
+
keepParentDirectory:(BOOL)keepParentDirectory
|
|
925
|
+
compressionLevel:(int)compressionLevel
|
|
926
|
+
password:(nullable NSString *)password
|
|
927
|
+
AES:(BOOL)aes
|
|
928
|
+
progressHandler:(void(^ _Nullable)(NSUInteger entryNumber, NSUInteger total))progressHandler
|
|
929
|
+
keepSymlinks:(BOOL)keeplinks {
|
|
930
|
+
if (!keeplinks) {
|
|
931
|
+
return [SSZipArchive createZipFileAtPath:path
|
|
932
|
+
withContentsOfDirectory:directoryPath
|
|
933
|
+
keepParentDirectory:keepParentDirectory
|
|
934
|
+
compressionLevel:compressionLevel
|
|
935
|
+
password:password
|
|
936
|
+
AES:aes
|
|
937
|
+
progressHandler:progressHandler];
|
|
938
|
+
} else {
|
|
939
|
+
SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path];
|
|
940
|
+
BOOL success = [zipArchive open];
|
|
941
|
+
if (success) {
|
|
942
|
+
// use a local fileManager (queue/thread compatibility)
|
|
943
|
+
NSFileManager *fileManager = [[NSFileManager alloc] init];
|
|
944
|
+
NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:directoryPath];
|
|
945
|
+
NSArray<NSString *> *allObjects = dirEnumerator.allObjects;
|
|
946
|
+
NSUInteger total = allObjects.count, complete = 0;
|
|
947
|
+
if (keepParentDirectory && !total) {
|
|
948
|
+
allObjects = @[@""];
|
|
949
|
+
total = 1;
|
|
950
|
+
}
|
|
951
|
+
for (__strong NSString *fileName in allObjects) {
|
|
952
|
+
NSString *fullFilePath = [directoryPath stringByAppendingPathComponent:fileName];
|
|
953
|
+
|
|
954
|
+
if (keepParentDirectory) {
|
|
955
|
+
fileName = [directoryPath.lastPathComponent stringByAppendingPathComponent:fileName];
|
|
956
|
+
}
|
|
957
|
+
//is symlink
|
|
958
|
+
BOOL isSymlink = NO;
|
|
959
|
+
if (mz_os_is_symlink(fullFilePath.fileSystemRepresentation) == MZ_OK)
|
|
960
|
+
isSymlink = YES;
|
|
961
|
+
BOOL isDir;
|
|
962
|
+
[fileManager fileExistsAtPath:fullFilePath isDirectory:&isDir];
|
|
963
|
+
if (!isDir || isSymlink) {
|
|
964
|
+
// file or symlink
|
|
965
|
+
if (!isSymlink) {
|
|
966
|
+
success &= [zipArchive writeFileAtPath:fullFilePath withFileName:fileName compressionLevel:compressionLevel password:password AES:aes];
|
|
967
|
+
} else {
|
|
968
|
+
success &= [zipArchive writeSymlinkFileAtPath:fullFilePath withFileName:fileName compressionLevel:compressionLevel password:password AES:aes];
|
|
969
|
+
}
|
|
970
|
+
} else {
|
|
971
|
+
// directory
|
|
972
|
+
if (![fileManager enumeratorAtPath:fullFilePath].nextObject) {
|
|
973
|
+
// empty directory
|
|
974
|
+
success &= [zipArchive writeFolderAtPath:fullFilePath withFolderName:fileName withPassword:password];
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (progressHandler) {
|
|
978
|
+
complete++;
|
|
979
|
+
progressHandler(complete, total);
|
|
446
980
|
}
|
|
447
981
|
}
|
|
982
|
+
success &= [zipArchive close];
|
|
448
983
|
}
|
|
449
|
-
success
|
|
984
|
+
return success;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
- (BOOL)writeSymlinkFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes
|
|
989
|
+
{
|
|
990
|
+
NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened");
|
|
991
|
+
//read symlink
|
|
992
|
+
char link_path[1024];
|
|
993
|
+
int32_t err = MZ_OK;
|
|
994
|
+
err = mz_os_read_symlink(path.fileSystemRepresentation, link_path, sizeof(link_path));
|
|
995
|
+
if (err != MZ_OK) {
|
|
996
|
+
NSLog(@"[SSZipArchive] Failed to read sylink");
|
|
997
|
+
return NO;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if (!fileName) {
|
|
1001
|
+
fileName = path.lastPathComponent;
|
|
450
1002
|
}
|
|
451
1003
|
|
|
452
|
-
|
|
453
|
-
[
|
|
454
|
-
[zipArchive release];
|
|
455
|
-
#endif
|
|
1004
|
+
zip_fileinfo zipInfo = {};
|
|
1005
|
+
[SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path];
|
|
456
1006
|
|
|
457
|
-
|
|
1007
|
+
//unpdate zipInfo.external_fa
|
|
1008
|
+
uint32_t target_attrib = 0;
|
|
1009
|
+
uint32_t src_attrib = 0;
|
|
1010
|
+
uint32_t src_sys = 0;
|
|
1011
|
+
mz_os_get_file_attribs(path.fileSystemRepresentation, &src_attrib);
|
|
1012
|
+
src_sys = MZ_HOST_SYSTEM(MZ_VERSION_MADEBY);
|
|
1013
|
+
|
|
1014
|
+
if ((src_sys != MZ_HOST_SYSTEM_MSDOS) && (src_sys != MZ_HOST_SYSTEM_WINDOWS_NTFS)) {
|
|
1015
|
+
/* High bytes are OS specific attributes, low byte is always DOS attributes */
|
|
1016
|
+
if (mz_zip_attrib_convert(src_sys, src_attrib, MZ_HOST_SYSTEM_MSDOS, &target_attrib) == MZ_OK)
|
|
1017
|
+
zipInfo.external_fa = target_attrib;
|
|
1018
|
+
zipInfo.external_fa |= (src_attrib << 16);
|
|
1019
|
+
} else {
|
|
1020
|
+
zipInfo.external_fa = src_attrib;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
uint16_t version_madeby = 3 << 8;//UNIX
|
|
1024
|
+
int error = zipOpenNewFileInZip5(_zip, fileName.fileSystemRepresentation, &zipInfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, compressionLevel, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, password.UTF8String, 0, aes, version_madeby, 0, 0);
|
|
1025
|
+
zipWriteInFileInZip(_zip, link_path, (uint32_t)strlen(link_path));
|
|
1026
|
+
zipCloseFileInZip(_zip);
|
|
1027
|
+
return error == ZIP_OK;
|
|
458
1028
|
}
|
|
459
1029
|
|
|
1030
|
+
// disabling `init` because designated initializer is `initWithPath:`
|
|
1031
|
+
- (instancetype)init { @throw nil; }
|
|
460
1032
|
|
|
1033
|
+
// designated initializer
|
|
461
1034
|
- (instancetype)initWithPath:(NSString *)path
|
|
462
1035
|
{
|
|
463
1036
|
if ((self = [super init])) {
|
|
@@ -467,153 +1040,92 @@
|
|
|
467
1040
|
}
|
|
468
1041
|
|
|
469
1042
|
|
|
470
|
-
#if !__has_feature(objc_arc)
|
|
471
|
-
- (void)dealloc
|
|
472
|
-
{
|
|
473
|
-
[_path release];
|
|
474
|
-
[super dealloc];
|
|
475
|
-
}
|
|
476
|
-
#endif
|
|
477
|
-
|
|
478
|
-
|
|
479
1043
|
- (BOOL)open
|
|
480
1044
|
{
|
|
481
|
-
NSAssert((_zip == NULL), @"Attempting open an archive which is already open");
|
|
482
|
-
_zip = zipOpen(
|
|
1045
|
+
NSAssert((_zip == NULL), @"Attempting to open an archive which is already open");
|
|
1046
|
+
_zip = zipOpen(_path.fileSystemRepresentation, APPEND_STATUS_CREATE);
|
|
483
1047
|
return (NULL != _zip);
|
|
484
1048
|
}
|
|
485
1049
|
|
|
486
|
-
|
|
487
|
-
- (void)zipInfo:(zip_fileinfo*)zipInfo setDate:(NSDate*)date
|
|
1050
|
+
- (BOOL)openForAppending
|
|
488
1051
|
{
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
#else
|
|
493
|
-
uint flags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
|
|
494
|
-
#endif
|
|
495
|
-
NSDateComponents *components = [currentCalendar components:flags fromDate:date];
|
|
496
|
-
zipInfo->tmz_date.tm_sec = (unsigned int)components.second;
|
|
497
|
-
zipInfo->tmz_date.tm_min = (unsigned int)components.minute;
|
|
498
|
-
zipInfo->tmz_date.tm_hour = (unsigned int)components.hour;
|
|
499
|
-
zipInfo->tmz_date.tm_mday = (unsigned int)components.day;
|
|
500
|
-
zipInfo->tmz_date.tm_mon = (unsigned int)components.month - 1;
|
|
501
|
-
zipInfo->tmz_date.tm_year = (unsigned int)components.year;
|
|
1052
|
+
NSAssert((_zip == NULL), @"Attempting to open an archive which is already open");
|
|
1053
|
+
_zip = zipOpen(_path.fileSystemRepresentation, APPEND_STATUS_ADDINZIP);
|
|
1054
|
+
return (NULL != _zip);
|
|
502
1055
|
}
|
|
503
1056
|
|
|
504
|
-
- (BOOL)writeFolderAtPath:(NSString *)path withFolderName:(NSString *)folderName withPassword:(NSString *)password
|
|
1057
|
+
- (BOOL)writeFolderAtPath:(NSString *)path withFolderName:(NSString *)folderName withPassword:(nullable NSString *)password
|
|
505
1058
|
{
|
|
506
1059
|
NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened");
|
|
507
1060
|
|
|
508
|
-
zip_fileinfo zipInfo = {
|
|
1061
|
+
zip_fileinfo zipInfo = {};
|
|
509
1062
|
|
|
510
|
-
|
|
511
|
-
if( attr )
|
|
512
|
-
{
|
|
513
|
-
NSDate *fileDate = (NSDate *)attr[NSFileModificationDate];
|
|
514
|
-
if( fileDate )
|
|
515
|
-
{
|
|
516
|
-
[self zipInfo:&zipInfo setDate: fileDate ];
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// Write permissions into the external attributes, for details on this see here: http://unix.stackexchange.com/a/14727
|
|
520
|
-
// Get the permissions value from the files attributes
|
|
521
|
-
NSNumber *permissionsValue = (NSNumber *)attr[NSFilePosixPermissions];
|
|
522
|
-
if (permissionsValue) {
|
|
523
|
-
// Get the short value for the permissions
|
|
524
|
-
short permissionsShort = permissionsValue.shortValue;
|
|
525
|
-
|
|
526
|
-
// Convert this into an octal by adding 010000, 010000 being the flag for a regular file
|
|
527
|
-
NSInteger permissionsOctal = 0100000 + permissionsShort;
|
|
528
|
-
|
|
529
|
-
// Convert this into a long value
|
|
530
|
-
uLong permissionsLong = @(permissionsOctal).unsignedLongValue;
|
|
531
|
-
|
|
532
|
-
// Store this into the external file attributes once it has been shifted 16 places left to form part of the second from last byte
|
|
533
|
-
zipInfo.external_fa = permissionsLong << 16L;
|
|
534
|
-
}
|
|
535
|
-
}
|
|
1063
|
+
[SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path];
|
|
536
1064
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
zipWriteInFileInZip(_zip, &len, 0);
|
|
1065
|
+
int error = _zipOpenEntry(_zip, [folderName stringByAppendingString:@"/"], &zipInfo, Z_NO_COMPRESSION, password, NO);
|
|
1066
|
+
const void *buffer = NULL;
|
|
1067
|
+
zipWriteInFileInZip(_zip, buffer, 0);
|
|
541
1068
|
zipCloseFileInZip(_zip);
|
|
542
|
-
return
|
|
1069
|
+
return error == ZIP_OK;
|
|
543
1070
|
}
|
|
544
1071
|
|
|
545
|
-
- (BOOL)writeFile:(NSString *)path withPassword:(NSString *)password
|
|
1072
|
+
- (BOOL)writeFile:(NSString *)path withPassword:(nullable NSString *)password
|
|
546
1073
|
{
|
|
547
1074
|
return [self writeFileAtPath:path withFileName:nil withPassword:password];
|
|
548
1075
|
}
|
|
549
1076
|
|
|
1077
|
+
- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName withPassword:(nullable NSString *)password
|
|
1078
|
+
{
|
|
1079
|
+
return [self writeFileAtPath:path withFileName:fileName compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES];
|
|
1080
|
+
}
|
|
1081
|
+
|
|
550
1082
|
// supports writing files with logical folder/directory structure
|
|
551
1083
|
// *path* is the absolute path of the file that will be compressed
|
|
552
1084
|
// *fileName* is the relative name of the file how it is stored within the zip e.g. /folder/subfolder/text1.txt
|
|
553
|
-
- (BOOL)writeFileAtPath:(NSString *)path withFileName:(NSString *)fileName
|
|
1085
|
+
- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes
|
|
554
1086
|
{
|
|
555
1087
|
NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened");
|
|
556
1088
|
|
|
557
|
-
FILE *input = fopen(
|
|
1089
|
+
FILE *input = fopen(path.fileSystemRepresentation, "r");
|
|
558
1090
|
if (NULL == input) {
|
|
559
1091
|
return NO;
|
|
560
1092
|
}
|
|
561
1093
|
|
|
562
|
-
const char *afileName;
|
|
563
1094
|
if (!fileName) {
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
else {
|
|
567
|
-
afileName = [fileName UTF8String];
|
|
1095
|
+
fileName = path.lastPathComponent;
|
|
568
1096
|
}
|
|
569
1097
|
|
|
570
|
-
zip_fileinfo zipInfo = {
|
|
1098
|
+
zip_fileinfo zipInfo = {};
|
|
571
1099
|
|
|
572
|
-
|
|
573
|
-
|
|
1100
|
+
[SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path];
|
|
1101
|
+
|
|
1102
|
+
void *buffer = malloc(CHUNK);
|
|
1103
|
+
if (buffer == NULL)
|
|
574
1104
|
{
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
{
|
|
578
|
-
[self zipInfo:&zipInfo setDate: fileDate ];
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// Write permissions into the external attributes, for details on this see here: http://unix.stackexchange.com/a/14727
|
|
582
|
-
// Get the permissions value from the files attributes
|
|
583
|
-
NSNumber *permissionsValue = (NSNumber *)attr[NSFilePosixPermissions];
|
|
584
|
-
if (permissionsValue) {
|
|
585
|
-
// Get the short value for the permissions
|
|
586
|
-
short permissionsShort = permissionsValue.shortValue;
|
|
587
|
-
|
|
588
|
-
// Convert this into an octal by adding 010000, 010000 being the flag for a regular file
|
|
589
|
-
NSInteger permissionsOctal = 0100000 + permissionsShort;
|
|
590
|
-
|
|
591
|
-
// Convert this into a long value
|
|
592
|
-
uLong permissionsLong = @(permissionsOctal).unsignedLongValue;
|
|
593
|
-
|
|
594
|
-
// Store this into the external file attributes once it has been shifted 16 places left to form part of the second from last byte
|
|
595
|
-
zipInfo.external_fa = permissionsLong << 16L;
|
|
596
|
-
}
|
|
1105
|
+
fclose(input);
|
|
1106
|
+
return NO;
|
|
597
1107
|
}
|
|
598
1108
|
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
void *buffer = malloc(CHUNK);
|
|
602
|
-
unsigned int len = 0;
|
|
1109
|
+
int error = _zipOpenEntry(_zip, fileName, &zipInfo, compressionLevel, password, aes);
|
|
603
1110
|
|
|
604
|
-
while (!feof(input))
|
|
1111
|
+
while (!feof(input) && !ferror(input))
|
|
605
1112
|
{
|
|
606
|
-
len = (unsigned int) fread(buffer, 1, CHUNK, input);
|
|
1113
|
+
unsigned int len = (unsigned int) fread(buffer, 1, CHUNK, input);
|
|
607
1114
|
zipWriteInFileInZip(_zip, buffer, len);
|
|
608
1115
|
}
|
|
609
1116
|
|
|
610
1117
|
zipCloseFileInZip(_zip);
|
|
611
1118
|
free(buffer);
|
|
612
1119
|
fclose(input);
|
|
613
|
-
return
|
|
1120
|
+
return error == ZIP_OK;
|
|
614
1121
|
}
|
|
615
1122
|
|
|
616
|
-
- (BOOL)writeData:(NSData *)data filename:(NSString *)filename withPassword:(NSString *)password
|
|
1123
|
+
- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename withPassword:(nullable NSString *)password
|
|
1124
|
+
{
|
|
1125
|
+
return [self writeData:data filename:filename compressionLevel:Z_DEFAULT_COMPRESSION password:password AES:YES];
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
- (BOOL)writeData:(NSData *)data filename:(nullable NSString *)filename compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes
|
|
617
1129
|
{
|
|
618
1130
|
if (!_zip) {
|
|
619
1131
|
return NO;
|
|
@@ -621,27 +1133,158 @@
|
|
|
621
1133
|
if (!data) {
|
|
622
1134
|
return NO;
|
|
623
1135
|
}
|
|
624
|
-
zip_fileinfo zipInfo = {
|
|
625
|
-
[
|
|
1136
|
+
zip_fileinfo zipInfo = {};
|
|
1137
|
+
[SSZipArchive zipInfo:&zipInfo setDate:[NSDate date]];
|
|
626
1138
|
|
|
627
|
-
|
|
1139
|
+
int error = _zipOpenEntry(_zip, filename, &zipInfo, compressionLevel, password, aes);
|
|
628
1140
|
|
|
629
1141
|
zipWriteInFileInZip(_zip, data.bytes, (unsigned int)data.length);
|
|
630
1142
|
|
|
631
1143
|
zipCloseFileInZip(_zip);
|
|
632
|
-
return
|
|
1144
|
+
return error == ZIP_OK;
|
|
633
1145
|
}
|
|
634
1146
|
|
|
635
|
-
|
|
636
1147
|
- (BOOL)close
|
|
637
1148
|
{
|
|
638
1149
|
NSAssert((_zip != NULL), @"[SSZipArchive] Attempting to close an archive which was never opened");
|
|
639
|
-
zipClose(_zip, NULL);
|
|
640
|
-
|
|
1150
|
+
int error = zipClose(_zip, NULL);
|
|
1151
|
+
_zip = nil;
|
|
1152
|
+
return error == ZIP_OK;
|
|
641
1153
|
}
|
|
642
1154
|
|
|
643
1155
|
#pragma mark - Private
|
|
644
1156
|
|
|
1157
|
+
+ (NSString *)_filenameStringWithCString:(const char *)filename
|
|
1158
|
+
version_made_by:(uint16_t)version_made_by
|
|
1159
|
+
general_purpose_flag:(uint16_t)flag
|
|
1160
|
+
size:(uint16_t)size_filename {
|
|
1161
|
+
|
|
1162
|
+
// Respect Language encoding flag only reading filename as UTF-8 when this is set
|
|
1163
|
+
// when file entry created on dos system.
|
|
1164
|
+
//
|
|
1165
|
+
// https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
|
|
1166
|
+
// Bit 11: Language encoding flag (EFS). If this bit is set,
|
|
1167
|
+
// the filename and comment fields for this file
|
|
1168
|
+
// MUST be encoded using UTF-8. (see APPENDIX D)
|
|
1169
|
+
uint16_t made_by = version_made_by >> 8;
|
|
1170
|
+
BOOL made_on_dos = made_by == 0;
|
|
1171
|
+
BOOL languageEncoding = (flag & (1 << 11)) != 0;
|
|
1172
|
+
if (!languageEncoding && made_on_dos) {
|
|
1173
|
+
// APPNOTE.TXT D.1:
|
|
1174
|
+
// D.2 If general purpose bit 11 is unset, the file name and comment should conform
|
|
1175
|
+
// to the original ZIP character encoding. If general purpose bit 11 is set, the
|
|
1176
|
+
// filename and comment must support The Unicode Standard, Version 4.1.0 or
|
|
1177
|
+
// greater using the character encoding form defined by the UTF-8 storage
|
|
1178
|
+
// specification. The Unicode Standard is published by the The Unicode
|
|
1179
|
+
// Consortium (www.unicode.org). UTF-8 encoded data stored within ZIP files
|
|
1180
|
+
// is expected to not include a byte order mark (BOM).
|
|
1181
|
+
|
|
1182
|
+
// Code Page 437 corresponds to kCFStringEncodingDOSLatinUS
|
|
1183
|
+
NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingDOSLatinUS);
|
|
1184
|
+
NSString* strPath = [NSString stringWithCString:filename encoding:encoding];
|
|
1185
|
+
if (strPath) {
|
|
1186
|
+
return strPath;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// attempting unicode encoding
|
|
1191
|
+
NSString * strPath = @(filename);
|
|
1192
|
+
if (strPath) {
|
|
1193
|
+
return strPath;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// if filename is non-unicode, detect and transform Encoding
|
|
1197
|
+
NSData *data = [NSData dataWithBytes:(const void *)filename length:sizeof(unsigned char) * size_filename];
|
|
1198
|
+
// Testing availability of @available (https://stackoverflow.com/a/46927445/1033581)
|
|
1199
|
+
#if __clang_major__ < 9
|
|
1200
|
+
// Xcode 8-
|
|
1201
|
+
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_9_2) {
|
|
1202
|
+
#else
|
|
1203
|
+
// Xcode 9+
|
|
1204
|
+
if (@available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *)) {
|
|
1205
|
+
#endif
|
|
1206
|
+
// supported encodings are in [NSString availableStringEncodings]
|
|
1207
|
+
[NSString stringEncodingForData:data encodingOptions:nil convertedString:&strPath usedLossyConversion:nil];
|
|
1208
|
+
} else {
|
|
1209
|
+
// fallback to a simple manual detect for macOS 10.9 or older
|
|
1210
|
+
NSArray<NSNumber *> *encodings = @[@(kCFStringEncodingGB_18030_2000), @(kCFStringEncodingShiftJIS)];
|
|
1211
|
+
for (NSNumber *encoding in encodings) {
|
|
1212
|
+
strPath = [NSString stringWithCString:filename encoding:(NSStringEncoding)CFStringConvertEncodingToNSStringEncoding(encoding.unsignedIntValue)];
|
|
1213
|
+
if (strPath) {
|
|
1214
|
+
break;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (strPath) {
|
|
1219
|
+
return strPath;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// if filename encoding is non-detected, we default to something based on data
|
|
1223
|
+
// _hexString is more readable than _base64RFC4648 for debugging unknown encodings
|
|
1224
|
+
strPath = [data _hexString];
|
|
1225
|
+
return strPath;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
+ (void)zipInfo:(zip_fileinfo *)zipInfo setAttributesOfItemAtPath:(NSString *)path
|
|
1229
|
+
{
|
|
1230
|
+
NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:path error: nil];
|
|
1231
|
+
if (attr)
|
|
1232
|
+
{
|
|
1233
|
+
NSDate *fileDate = (NSDate *)[attr objectForKey:NSFileModificationDate];
|
|
1234
|
+
if (fileDate)
|
|
1235
|
+
{
|
|
1236
|
+
[self zipInfo:zipInfo setDate:fileDate];
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// Write permissions into the external attributes, for details on this see here: https://unix.stackexchange.com/a/14727
|
|
1240
|
+
// Get the permissions value from the files attributes
|
|
1241
|
+
NSNumber *permissionsValue = (NSNumber *)[attr objectForKey:NSFilePosixPermissions];
|
|
1242
|
+
if (permissionsValue != nil) {
|
|
1243
|
+
// Get the short value for the permissions
|
|
1244
|
+
short permissionsShort = permissionsValue.shortValue;
|
|
1245
|
+
|
|
1246
|
+
// Convert this into an octal by adding 010000, 010000 being the flag for a regular file
|
|
1247
|
+
NSInteger permissionsOctal = 0100000 + permissionsShort;
|
|
1248
|
+
|
|
1249
|
+
// Convert this into a long value
|
|
1250
|
+
uLong permissionsLong = @(permissionsOctal).unsignedLongValue;
|
|
1251
|
+
|
|
1252
|
+
// Store this into the external file attributes once it has been shifted 16 places left to form part of the second from last byte
|
|
1253
|
+
|
|
1254
|
+
// Casted back to an unsigned int to match type of external_fa in minizip
|
|
1255
|
+
zipInfo->external_fa = (unsigned int)(permissionsLong << 16L);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
+ (void)zipInfo:(zip_fileinfo *)zipInfo setDate:(NSDate *)date
|
|
1261
|
+
{
|
|
1262
|
+
NSCalendar *currentCalendar = SSZipArchive._gregorian;
|
|
1263
|
+
NSCalendarUnit flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
|
|
1264
|
+
NSDateComponents *components = [currentCalendar components:flags fromDate:date];
|
|
1265
|
+
struct tm tmz_date;
|
|
1266
|
+
tmz_date.tm_sec = (unsigned int)components.second;
|
|
1267
|
+
tmz_date.tm_min = (unsigned int)components.minute;
|
|
1268
|
+
tmz_date.tm_hour = (unsigned int)components.hour;
|
|
1269
|
+
tmz_date.tm_mday = (unsigned int)components.day;
|
|
1270
|
+
// ISO/IEC 9899 struct tm is 0-indexed for January but NSDateComponents for gregorianCalendar is 1-indexed for January
|
|
1271
|
+
tmz_date.tm_mon = (unsigned int)components.month - 1;
|
|
1272
|
+
// ISO/IEC 9899 struct tm is 0-indexed for AD 1900 but NSDateComponents for gregorianCalendar is 1-indexed for AD 1
|
|
1273
|
+
tmz_date.tm_year = (unsigned int)components.year - 1900;
|
|
1274
|
+
zipInfo->mz_dos_date = mz_zip_tm_to_dosdate(&tmz_date);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
+ (NSCalendar *)_gregorian
|
|
1278
|
+
{
|
|
1279
|
+
static NSCalendar *gregorian;
|
|
1280
|
+
static dispatch_once_t onceToken;
|
|
1281
|
+
dispatch_once(&onceToken, ^{
|
|
1282
|
+
gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
return gregorian;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
645
1288
|
// Format from http://newsgroups.derkeiler.com/Archive/Comp/comp.os.msdos.programmer/2009-04/msg00060.html
|
|
646
1289
|
// Two consecutive words, or a longword, YYYYYYYMMMMDDDDD hhhhhmmmmmmsssss
|
|
647
1290
|
// YYYYYYY is years from 1980 = 0
|
|
@@ -651,6 +1294,9 @@
|
|
|
651
1294
|
// 7423 = 0111 0100 0010 0011 - 01110 100001 00011 = 14 33 3 = 14:33:06
|
|
652
1295
|
+ (NSDate *)_dateWithMSDOSFormat:(UInt32)msdosDateTime
|
|
653
1296
|
{
|
|
1297
|
+
// the whole `_dateWithMSDOSFormat:` method is equivalent but faster than this one line,
|
|
1298
|
+
// essentially because `mktime` is slow:
|
|
1299
|
+
//NSDate *date = [NSDate dateWithTimeIntervalSince1970:dosdate_to_time_t(msdosDateTime)];
|
|
654
1300
|
static const UInt32 kYearMask = 0xFE000000;
|
|
655
1301
|
static const UInt32 kMonthMask = 0x1E00000;
|
|
656
1302
|
static const UInt32 kDayMask = 0x1F0000;
|
|
@@ -658,34 +1304,193 @@
|
|
|
658
1304
|
static const UInt32 kMinuteMask = 0x7E0;
|
|
659
1305
|
static const UInt32 kSecondMask = 0x1F;
|
|
660
1306
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
1307
|
+
NSAssert(0xFFFFFFFF == (kYearMask | kMonthMask | kDayMask | kHourMask | kMinuteMask | kSecondMask), @"[SSZipArchive] MSDOS date masks don't add up");
|
|
1308
|
+
|
|
1309
|
+
NSDateComponents *components = [[NSDateComponents alloc] init];
|
|
1310
|
+
components.year = 1980 + ((msdosDateTime & kYearMask) >> 25);
|
|
1311
|
+
components.month = (msdosDateTime & kMonthMask) >> 21;
|
|
1312
|
+
components.day = (msdosDateTime & kDayMask) >> 16;
|
|
1313
|
+
components.hour = (msdosDateTime & kHourMask) >> 11;
|
|
1314
|
+
components.minute = (msdosDateTime & kMinuteMask) >> 5;
|
|
1315
|
+
components.second = (msdosDateTime & kSecondMask) * 2;
|
|
1316
|
+
|
|
1317
|
+
NSDate *date = [self._gregorian dateFromComponents:components];
|
|
1318
|
+
return date;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
@end
|
|
1322
|
+
|
|
1323
|
+
int _zipOpenEntry(zipFile entry, NSString *name, const zip_fileinfo *zipfi, int level, NSString *password, BOOL aes)
|
|
1324
|
+
{
|
|
1325
|
+
// https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
|
|
1326
|
+
uint16_t made_on_darwin = 19 << 8;
|
|
1327
|
+
//MZ_ZIP_FLAG_UTF8
|
|
1328
|
+
uint16_t flag_base = 1 << 11;
|
|
1329
|
+
return zipOpenNewFileInZip5(entry, name.fileSystemRepresentation, zipfi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, level, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, password.UTF8String, 0, aes, made_on_darwin, flag_base, 1);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
#pragma mark - Private tools for file info
|
|
1333
|
+
|
|
1334
|
+
BOOL _fileIsSymbolicLink(const unz_file_info *fileInfo)
|
|
1335
|
+
{
|
|
1336
|
+
//
|
|
1337
|
+
// Determine whether this is a symbolic link:
|
|
1338
|
+
// - File is stored with 'version made by' value of UNIX (3),
|
|
1339
|
+
// as per https://www.pkware.com/documents/casestudies/APPNOTE.TXT
|
|
1340
|
+
// in the upper byte of the version field.
|
|
1341
|
+
// - BSD4.4 st_mode constants are stored in the high 16 bits of the
|
|
1342
|
+
// external file attributes (defacto standard, verified against libarchive)
|
|
1343
|
+
//
|
|
1344
|
+
// The original constants can be found here:
|
|
1345
|
+
// https://minnie.tuhs.org/cgi-bin/utree.pl?file=4.4BSD/usr/include/sys/stat.h
|
|
1346
|
+
//
|
|
1347
|
+
const uLong ZipUNIXVersion = 3;
|
|
1348
|
+
const uLong BSD_SFMT = 0170000;
|
|
1349
|
+
const uLong BSD_IFLNK = 0120000;
|
|
1350
|
+
|
|
1351
|
+
BOOL fileIsSymbolicLink = ((fileInfo->version >> 8) == ZipUNIXVersion) && BSD_IFLNK == (BSD_SFMT & (fileInfo->external_fa >> 16));
|
|
1352
|
+
return fileIsSymbolicLink;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
#pragma mark - Private tools for unreadable encodings
|
|
1356
|
+
|
|
1357
|
+
@implementation NSData (SSZipArchive)
|
|
1358
|
+
|
|
1359
|
+
// `base64EncodedStringWithOptions` uses a base64 alphabet with '+' and '/'.
|
|
1360
|
+
// we got those alternatives to make it compatible with filenames: https://en.wikipedia.org/wiki/Base64
|
|
1361
|
+
// * modified Base64 encoding for IMAP mailbox names (RFC 3501): uses '+' and ','
|
|
1362
|
+
// * modified Base64 for URL and filenames (RFC 4648): uses '-' and '_'
|
|
1363
|
+
- (NSString *)_base64RFC4648
|
|
1364
|
+
{
|
|
1365
|
+
NSString *strName = [self base64EncodedStringWithOptions:0];
|
|
1366
|
+
strName = [strName stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
|
|
1367
|
+
strName = [strName stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
|
|
1368
|
+
return strName;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// initWithBytesNoCopy from NSProgrammer, Jan 25 '12: https://stackoverflow.com/a/9009321/1033581
|
|
1372
|
+
// hexChars from Peter, Aug 19 '14: https://stackoverflow.com/a/25378464/1033581
|
|
1373
|
+
// not implemented as too lengthy: a potential mapping improvement from Moose, Nov 3 '15: https://stackoverflow.com/a/33501154/1033581
|
|
1374
|
+
- (NSString *)_hexString
|
|
1375
|
+
{
|
|
1376
|
+
const char *hexChars = "0123456789ABCDEF";
|
|
1377
|
+
NSUInteger length = self.length;
|
|
1378
|
+
const unsigned char *bytes = self.bytes;
|
|
1379
|
+
char *chars = malloc(length * 2);
|
|
1380
|
+
if (chars == NULL) {
|
|
1381
|
+
// we directly raise an exception instead of using NSAssert to make sure assertion is not disabled as this is irrecoverable
|
|
1382
|
+
[NSException raise:@"NSInternalInconsistencyException" format:@"failed malloc" arguments:nil];
|
|
1383
|
+
return nil;
|
|
1384
|
+
}
|
|
1385
|
+
char *s = chars;
|
|
1386
|
+
NSUInteger i = length;
|
|
1387
|
+
while (i--) {
|
|
1388
|
+
*s++ = hexChars[*bytes >> 4];
|
|
1389
|
+
*s++ = hexChars[*bytes & 0xF];
|
|
1390
|
+
bytes++;
|
|
1391
|
+
}
|
|
1392
|
+
NSString *str = [[NSString alloc] initWithBytesNoCopy:chars
|
|
1393
|
+
length:length * 2
|
|
1394
|
+
encoding:NSASCIIStringEncoding
|
|
1395
|
+
freeWhenDone:YES];
|
|
1396
|
+
return str;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
@end
|
|
1400
|
+
|
|
1401
|
+
#pragma mark Private tools for security
|
|
1402
|
+
|
|
1403
|
+
@implementation NSString (SSZipArchive)
|
|
1404
|
+
|
|
1405
|
+
// One implementation alternative would be to use the algorithm found at mz_path_resolve from https://github.com/nmoinvaz/minizip/blob/dev/mz_os.c,
|
|
1406
|
+
// but making sure to work with unichar values and not ascii values to avoid breaking Unicode characters containing 2E ('.') or 2F ('/') in their decomposition
|
|
1407
|
+
/// Sanitize path traversal characters to prevent directory backtracking. Ignoring these characters mimicks the default behavior of the Unarchiving tool on macOS.
|
|
1408
|
+
- (NSString *)_sanitizedPath
|
|
1409
|
+
{
|
|
1410
|
+
// Change Windows paths to Unix paths: https://en.wikipedia.org/wiki/Path_(computing)
|
|
1411
|
+
// Possible improvement: only do this if the archive was created on a non-Unix system
|
|
1412
|
+
NSString *strPath = [self stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
|
|
1413
|
+
|
|
1414
|
+
// Percent-encode file path (where path is defined by https://tools.ietf.org/html/rfc8089)
|
|
1415
|
+
// The key part is to allow characters "." and "/" and disallow "%".
|
|
1416
|
+
// CharacterSet.urlPathAllowed seems to do the job
|
|
1417
|
+
#if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 || __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 || __WATCH_OS_VERSION_MIN_REQUIRED >= 20000 || __TV_OS_VERSION_MIN_REQUIRED >= 90000)
|
|
1418
|
+
strPath = [strPath stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLPathAllowedCharacterSet];
|
|
666
1419
|
#else
|
|
667
|
-
|
|
1420
|
+
// Testing availability of @available (https://stackoverflow.com/a/46927445/1033581)
|
|
1421
|
+
#if __clang_major__ < 9
|
|
1422
|
+
// Xcode 8-
|
|
1423
|
+
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4) {
|
|
1424
|
+
#else
|
|
1425
|
+
// Xcode 9+
|
|
1426
|
+
if (@available(macOS 10.9, iOS 7.0, watchOS 2.0, tvOS 9.0, *)) {
|
|
1427
|
+
#endif
|
|
1428
|
+
strPath = [strPath stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLPathAllowedCharacterSet];
|
|
1429
|
+
} else {
|
|
1430
|
+
strPath = [strPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
1431
|
+
}
|
|
668
1432
|
#endif
|
|
669
|
-
});
|
|
670
1433
|
|
|
671
|
-
|
|
1434
|
+
// `NSString.stringByAddingPercentEncodingWithAllowedCharacters:` may theorically fail: https://stackoverflow.com/questions/33558933/
|
|
1435
|
+
// But because we auto-detect encoding using `NSString.stringEncodingForData:encodingOptions:convertedString:usedLossyConversion:`,
|
|
1436
|
+
// we likely already prevent UTF-16, UTF-32 and invalid Unicode in the form of unpaired surrogate chars: https://stackoverflow.com/questions/53043876/
|
|
1437
|
+
// To be on the safe side, we will still perform a guard check.
|
|
1438
|
+
if (strPath == nil) {
|
|
1439
|
+
return nil;
|
|
1440
|
+
}
|
|
672
1441
|
|
|
673
|
-
|
|
1442
|
+
// Add scheme "file:///" to support sanitation on names with a colon like "file:a/../../../usr/bin"
|
|
1443
|
+
strPath = [@"file:///" stringByAppendingString:strPath];
|
|
674
1444
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
[
|
|
679
|
-
[components setMinute:(msdosDateTime & kMinuteMask) >> 5];
|
|
680
|
-
[components setSecond:(msdosDateTime & kSecondMask) * 2];
|
|
1445
|
+
// Sanitize path traversal characters to prevent directory backtracking. Ignoring these characters mimicks the default behavior of the Unarchiving tool on macOS.
|
|
1446
|
+
// "../../../../../../../../../../../tmp/test.txt" -> "tmp/test.txt"
|
|
1447
|
+
// "a/b/../c.txt" -> "a/c.txt"
|
|
1448
|
+
strPath = [NSURL URLWithString:strPath].standardizedURL.absoluteString;
|
|
681
1449
|
|
|
682
|
-
|
|
1450
|
+
// Remove the "file:///" scheme
|
|
1451
|
+
strPath = strPath.length < 8 ? @"" : [strPath substringFromIndex:8];
|
|
683
1452
|
|
|
684
|
-
|
|
685
|
-
|
|
1453
|
+
// Remove the percent-encoding
|
|
1454
|
+
#if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 || __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 || __WATCH_OS_VERSION_MIN_REQUIRED >= 20000 || __TV_OS_VERSION_MIN_REQUIRED >= 90000)
|
|
1455
|
+
strPath = strPath.stringByRemovingPercentEncoding;
|
|
1456
|
+
#else
|
|
1457
|
+
// Testing availability of @available (https://stackoverflow.com/a/46927445/1033581)
|
|
1458
|
+
#if __clang_major__ < 9
|
|
1459
|
+
// Xcode 8-
|
|
1460
|
+
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber10_8_4) {
|
|
1461
|
+
#else
|
|
1462
|
+
// Xcode 9+
|
|
1463
|
+
if (@available(macOS 10.9, iOS 7.0, watchOS 2.0, tvOS 9.0, *)) {
|
|
1464
|
+
#endif
|
|
1465
|
+
strPath = strPath.stringByRemovingPercentEncoding;
|
|
1466
|
+
} else {
|
|
1467
|
+
strPath = [strPath stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
1468
|
+
}
|
|
686
1469
|
#endif
|
|
687
1470
|
|
|
688
|
-
return
|
|
1471
|
+
return strPath;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
/// Detects if the path represented in this string is pointing outside of the targetDirectory passed as argument.
|
|
1475
|
+
///
|
|
1476
|
+
/// Helps detecting and avoiding a security vulnerability described here:
|
|
1477
|
+
/// https://nvd.nist.gov/vuln/detail/CVE-2022-36943
|
|
1478
|
+
- (BOOL)_escapesTargetDirectory:(NSString *)targetDirectory {
|
|
1479
|
+
NSString *standardizedPath = [[self stringByStandardizingPath] stringByResolvingSymlinksInPath];
|
|
1480
|
+
NSString *standardizedTargetPath = [[targetDirectory stringByStandardizingPath] stringByResolvingSymlinksInPath];
|
|
1481
|
+
|
|
1482
|
+
NSArray *targetPathComponents = [standardizedTargetPath pathComponents];
|
|
1483
|
+
NSArray *pathComponents = [standardizedPath pathComponents];
|
|
1484
|
+
|
|
1485
|
+
if (pathComponents.count < targetPathComponents.count) return YES;
|
|
1486
|
+
|
|
1487
|
+
for (int idx = 0; idx < targetPathComponents.count; idx++) {
|
|
1488
|
+
if (![pathComponents[idx] isEqual: targetPathComponents[idx]]) {
|
|
1489
|
+
return YES;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
return NO;
|
|
689
1494
|
}
|
|
690
1495
|
|
|
691
|
-
@end
|
|
1496
|
+
@end
|