@appzung/react-native-code-push 9.0.2 → 10.0.0-rc1
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.js +22 -19
- package/LICENSE.md +1 -1
- package/README.md +159 -298
- package/android/app/.gradle/config.properties +2 -0
- package/android/app/build.gradle +1 -1
- package/android/app/local.properties +8 -0
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +27 -21
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushBuilder.java +5 -5
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushConstants.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushDialog.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInstallMode.java +2 -2
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPublicKeyException.java +2 -2
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidUpdateException.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushMalformedDataException.java +2 -2
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +5 -5
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNotInitializedException.java +2 -2
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushTelemetryManager.java +14 -14
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUnknownException.java +2 -2
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateState.java +2 -2
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgress.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressCallback.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/ReactInstanceHolder.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/SettingsManager.java +1 -1
- package/android/app/src/main/java/com/microsoft/codepush/react/TLSSocketFactory.java +1 -1
- package/android/build.gradle +1 -1
- package/android/codepush.gradle +3 -3
- package/docs/advanced-usage.md +56 -0
- package/docs/api-android.md +12 -75
- package/docs/api-ios.md +5 -17
- package/docs/api-js.md +18 -55
- package/docs/setup-android.md +15 -397
- package/docs/setup-ios.md +24 -198
- package/docs/setup-windows.md +7 -74
- package/ios/CodePush/CodePush.h +4 -4
- package/ios/CodePush/CodePush.m +8 -8
- package/ios/CodePush/CodePushConfig.m +14 -11
- package/ios/CodePush/CodePushPackage.m +60 -60
- package/ios/CodePush/CodePushTelemetryManager.m +13 -13
- package/ios/CodePush.xcodeproj/project.pbxproj +1 -562
- package/package.json +27 -24
- package/react-native.config.js +1 -1
- package/scripts/generateBundledResourcesHash.js +1 -1
- package/scripts/getFilesInFolder.js +1 -1
- package/scripts/recordFilesBeforeBundleCommand.js +1 -1
- package/typings/react-native-code-push.d.ts +22 -22
- package/windows/CodePush/CodePushConfig.cpp +3 -3
- package/windows/CodePush/CodePushConfig.h +3 -3
- package/windows/CodePush/CodePushNativeModule.cpp +27 -27
- package/windows/CodePush/CodePushNativeModule.h +4 -4
- package/windows/CodePush/CodePushTelemetryManager.cpp +12 -12
- package/windows/CodePush/CodePushTelemetryManager.h +1 -1
- package/.azurepipelines/build-rn-code-push-1es.yml +0 -104
- package/.azurepipelines/test-rn-code-push.yml +0 -94
- package/.config/CredScanSuppressions.json +0 -14
- package/SECURITY.md +0 -41
- package/docs/multi-deployment-testing-android.md +0 -148
- package/docs/multi-deployment-testing-ios.md +0 -59
- package/ios/CodePush/Base64/Base64/MF_Base64Additions.h +0 -34
- package/ios/CodePush/Base64/Base64/MF_Base64Additions.m +0 -252
- package/ios/CodePush/Base64/README.md +0 -47
- package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithm.h +0 -69
- package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.h +0 -16
- package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.m +0 -51
- package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.h +0 -15
- package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.m +0 -55
- package/ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.h +0 -24
- package/ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.m +0 -41
- package/ios/CodePush/JWT/Core/Algorithms/HSFamily/JWTAlgorithmHSBase.h +0 -28
- package/ios/CodePush/JWT/Core/Algorithms/HSFamily/JWTAlgorithmHSBase.m +0 -205
- package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolder.h +0 -103
- package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolder.m +0 -322
- package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolderChain.h +0 -37
- package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolderChain.m +0 -145
- package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTAlgorithmRSBase.h +0 -35
- package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTAlgorithmRSBase.m +0 -551
- package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTRSAlgorithm.h +0 -23
- package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKey.h +0 -43
- package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKey.m +0 -230
- package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKeyExtractor.h +0 -31
- package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKeyExtractor.m +0 -113
- package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoSecurity.h +0 -38
- package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoSecurity.m +0 -500
- package/ios/CodePush/JWT/Core/ClaimSet/JWTClaim.h +0 -18
- package/ios/CodePush/JWT/Core/ClaimSet/JWTClaim.m +0 -214
- package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.h +0 -23
- package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.m +0 -29
- package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.h +0 -19
- package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.m +0 -68
- package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.h +0 -18
- package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.m +0 -72
- package/ios/CodePush/JWT/Core/Coding/JWTCoding+ResultTypes.h +0 -67
- package/ios/CodePush/JWT/Core/Coding/JWTCoding+ResultTypes.m +0 -111
- package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionOne.h +0 -119
- package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionOne.m +0 -307
- package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionThree.h +0 -94
- package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionThree.m +0 -619
- package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionTwo.h +0 -164
- package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionTwo.m +0 -514
- package/ios/CodePush/JWT/Core/Coding/JWTCoding.h +0 -24
- package/ios/CodePush/JWT/Core/Coding/JWTCoding.m +0 -11
- package/ios/CodePush/JWT/Core/FrameworkSupplement/JWT.h +0 -52
- package/ios/CodePush/JWT/Core/FrameworkSupplement/Map.modulemap +0 -5
- package/ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.h +0 -28
- package/ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.m +0 -70
- package/ios/CodePush/JWT/Core/Supplement/JWTDeprecations.h +0 -22
- package/ios/CodePush/JWT/Core/Supplement/JWTErrorDescription.h +0 -34
- package/ios/CodePush/JWT/Core/Supplement/JWTErrorDescription.m +0 -73
- package/ios/CodePush/JWT/LICENSE +0 -19
- package/ios/CodePush/JWT/README.md +0 -489
- package/ios/CodePush/SSZipArchive/Info.plist +0 -26
- package/ios/CodePush/SSZipArchive/README.md +0 -1
- package/ios/CodePush/SSZipArchive/SSZipArchive.h +0 -178
- package/ios/CodePush/SSZipArchive/SSZipArchive.m +0 -1496
- package/ios/CodePush/SSZipArchive/SSZipCommon.h +0 -71
- package/ios/CodePush/SSZipArchive/Supporting Files/PrivacyInfo.xcprivacy +0 -23
- package/ios/CodePush/SSZipArchive/include/ZipArchive.h +0 -25
- package/ios/CodePush/SSZipArchive/minizip/LICENSE +0 -17
- package/ios/CodePush/SSZipArchive/minizip/mz.h +0 -273
- package/ios/CodePush/SSZipArchive/minizip/mz_compat.c +0 -1306
- package/ios/CodePush/SSZipArchive/minizip/mz_compat.h +0 -346
- package/ios/CodePush/SSZipArchive/minizip/mz_crypt.c +0 -187
- package/ios/CodePush/SSZipArchive/minizip/mz_crypt.h +0 -65
- package/ios/CodePush/SSZipArchive/minizip/mz_crypt_apple.c +0 -526
- package/ios/CodePush/SSZipArchive/minizip/mz_os.c +0 -348
- package/ios/CodePush/SSZipArchive/minizip/mz_os.h +0 -176
- package/ios/CodePush/SSZipArchive/minizip/mz_os_posix.c +0 -350
- package/ios/CodePush/SSZipArchive/minizip/mz_strm.c +0 -556
- package/ios/CodePush/SSZipArchive/minizip/mz_strm.h +0 -132
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_buf.c +0 -383
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_buf.h +0 -42
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_mem.c +0 -269
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_mem.h +0 -48
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_os.h +0 -40
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_os_posix.c +0 -203
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_pkcrypt.c +0 -334
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_pkcrypt.h +0 -46
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_split.c +0 -429
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_split.h +0 -43
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_wzaes.c +0 -360
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_wzaes.h +0 -46
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_zlib.c +0 -389
- package/ios/CodePush/SSZipArchive/minizip/mz_strm_zlib.h +0 -43
- package/ios/CodePush/SSZipArchive/minizip/mz_zip.c +0 -2782
- package/ios/CodePush/SSZipArchive/minizip/mz_zip.h +0 -262
- package/ios/CodePush/SSZipArchive/minizip/mz_zip_rw.c +0 -1942
- package/ios/CodePush/SSZipArchive/minizip/mz_zip_rw.h +0 -285
- package/scripts/postlink/android/postlink.js +0 -87
- package/scripts/postlink/ios/postlink.js +0 -116
- package/scripts/postlink/run.js +0 -11
- package/scripts/postunlink/android/postunlink.js +0 -74
- package/scripts/postunlink/ios/postunlink.js +0 -87
- package/scripts/postunlink/run.js +0 -11
- package/scripts/tools/linkToolsAndroid.js +0 -57
- package/scripts/tools/linkToolsIos.js +0 -130
- package/windows-legacy/CodePush/CodePush.csproj +0 -128
- package/windows-legacy/CodePush/CodePushUtils.cs +0 -47
- package/windows-legacy/CodePush/FileUtils.cs +0 -40
- package/windows-legacy/CodePush/Properties/AssemblyInfo.cs +0 -29
- package/windows-legacy/CodePush/Properties/CodePush.rd.xml +0 -33
- package/windows-legacy/CodePush/UpdateManager.cs +0 -305
- package/windows-legacy/CodePush/UpdateUtils.cs +0 -46
- package/windows-legacy/CodePush.Net46/Adapters/Http/HttpProgress.cs +0 -28
- package/windows-legacy/CodePush.Net46/Adapters/Storage/ApplicationDataContainer.cs +0 -106
- package/windows-legacy/CodePush.Net46/CodePush.Net46.csproj +0 -103
- package/windows-legacy/CodePush.Net46/CodePushUtils.cs +0 -158
- package/windows-legacy/CodePush.Net46/FileUtils.cs +0 -55
- package/windows-legacy/CodePush.Net46/Properties/AssemblyInfo.cs +0 -36
- package/windows-legacy/CodePush.Net46/UpdateManager.cs +0 -330
- package/windows-legacy/CodePush.Net46/UpdateUtils.cs +0 -70
- package/windows-legacy/CodePush.Net46/packages.config +0 -5
- package/windows-legacy/CodePush.Net46.Test/ApplicationDataContainerTest.cs +0 -105
- package/windows-legacy/CodePush.Net46.Test/CodePush.Net46.Test.csproj +0 -137
- package/windows-legacy/CodePush.Net46.Test/Properties/AssemblyInfo.cs +0 -36
- package/windows-legacy/CodePush.Net46.Test/TelemetryManagerTest.cs +0 -117
- package/windows-legacy/CodePush.Net46.Test/app.config +0 -11
- package/windows-legacy/CodePush.Net46.Test/packages.config +0 -4
- package/windows-legacy/CodePush.Shared/CodePush.Shared.projitems +0 -22
- package/windows-legacy/CodePush.Shared/CodePush.Shared.shproj +0 -13
- package/windows-legacy/CodePush.Shared/CodePushConstants.cs +0 -35
- package/windows-legacy/CodePush.Shared/CodePushNativeModule.cs +0 -329
- package/windows-legacy/CodePush.Shared/CodePushReactPackage.cs +0 -235
- package/windows-legacy/CodePush.Shared/CodePushUtils.cs +0 -70
- package/windows-legacy/CodePush.Shared/InstallMode.cs +0 -9
- package/windows-legacy/CodePush.Shared/MinimumBackgroundListener.cs +0 -44
- package/windows-legacy/CodePush.Shared/SettingsManager.cs +0 -148
- package/windows-legacy/CodePush.Shared/TelemetryManager.cs +0 -250
- package/windows-legacy/CodePush.Shared/UpdateState.cs +0 -9
|
@@ -1,1496 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// SSZipArchive.m
|
|
3
|
-
// SSZipArchive
|
|
4
|
-
//
|
|
5
|
-
// Created by Sam Soffes on 7/21/10.
|
|
6
|
-
//
|
|
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>
|
|
13
|
-
#include <sys/stat.h>
|
|
14
|
-
|
|
15
|
-
NSString *const SSZipArchiveErrorDomain = @"SSZipArchiveErrorDomain";
|
|
16
|
-
|
|
17
|
-
#define CHUNK 16384
|
|
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
|
-
|
|
37
|
-
@interface SSZipArchive ()
|
|
38
|
-
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
|
39
|
-
@end
|
|
40
|
-
|
|
41
|
-
@implementation SSZipArchive
|
|
42
|
-
{
|
|
43
|
-
/// path for zip file
|
|
44
|
-
NSString *_path;
|
|
45
|
-
zipFile _zip;
|
|
46
|
-
}
|
|
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
|
-
|
|
215
|
-
#pragma mark - Unzipping
|
|
216
|
-
|
|
217
|
-
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination
|
|
218
|
-
{
|
|
219
|
-
return [self unzipFileAtPath:path toDestination:destination delegate:nil];
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination overwrite:(BOOL)overwrite password:(nullable NSString *)password error:(NSError **)error
|
|
223
|
-
{
|
|
224
|
-
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:error delegate:nil progressHandler:nil completionHandler:nil];
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
+ (BOOL)unzipFileAtPath:(NSString *)path toDestination:(NSString *)destination delegate:(nullable id<SSZipArchiveDelegate>)delegate
|
|
228
|
-
{
|
|
229
|
-
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:YES password:nil error:nil delegate:delegate progressHandler:nil completionHandler:nil];
|
|
230
|
-
}
|
|
231
|
-
|
|
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
|
|
238
|
-
{
|
|
239
|
-
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:error delegate:delegate progressHandler:nil completionHandler:nil];
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
243
|
-
toDestination:(NSString *)destination
|
|
244
|
-
overwrite:(BOOL)overwrite
|
|
245
|
-
password:(NSString *)password
|
|
246
|
-
progressHandler:(void (^)(NSString *entry, unz_file_info zipInfo, long entryNumber, long total))progressHandler
|
|
247
|
-
completionHandler:(void (^)(NSString *path, BOOL succeeded, NSError * _Nullable error))completionHandler
|
|
248
|
-
{
|
|
249
|
-
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:overwrite password:password error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler];
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
253
|
-
toDestination:(NSString *)destination
|
|
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
|
|
256
|
-
{
|
|
257
|
-
return [self unzipFileAtPath:path toDestination:destination preserveAttributes:YES overwrite:YES password:nil error:nil delegate:nil progressHandler:progressHandler completionHandler:completionHandler];
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
+ (BOOL)unzipFileAtPath:(NSString *)path
|
|
261
|
-
toDestination:(NSString *)destination
|
|
262
|
-
preserveAttributes:(BOOL)preserveAttributes
|
|
263
|
-
overwrite:(BOOL)overwrite
|
|
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
|
|
276
|
-
error:(NSError **)error
|
|
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
|
|
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
|
-
|
|
337
|
-
// Begin opening
|
|
338
|
-
zipFile zip = unzOpen(path.fileSystemRepresentation);
|
|
339
|
-
if (zip == NULL)
|
|
340
|
-
{
|
|
341
|
-
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open zip file"};
|
|
342
|
-
NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenZipFile userInfo:userInfo];
|
|
343
|
-
if (error)
|
|
344
|
-
{
|
|
345
|
-
*error = err;
|
|
346
|
-
}
|
|
347
|
-
if (completionHandler)
|
|
348
|
-
{
|
|
349
|
-
completionHandler(nil, NO, err);
|
|
350
|
-
}
|
|
351
|
-
return NO;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
NSDictionary * fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
|
|
355
|
-
unsigned long long fileSize = [[fileAttributes objectForKey:NSFileSize] unsignedLongLongValue];
|
|
356
|
-
unsigned long long currentPosition = 0;
|
|
357
|
-
|
|
358
|
-
unz_global_info globalInfo = {};
|
|
359
|
-
unzGetGlobalInfo(zip, &globalInfo);
|
|
360
|
-
|
|
361
|
-
// Begin unzipping
|
|
362
|
-
int ret = 0;
|
|
363
|
-
ret = unzGoToFirstFile(zip);
|
|
364
|
-
if (ret != UNZ_OK && ret != MZ_END_OF_LIST)
|
|
365
|
-
{
|
|
366
|
-
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: @"failed to open first file in zip file"};
|
|
367
|
-
NSError *err = [NSError errorWithDomain:SSZipArchiveErrorDomain code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:userInfo];
|
|
368
|
-
if (error)
|
|
369
|
-
{
|
|
370
|
-
*error = err;
|
|
371
|
-
}
|
|
372
|
-
if (completionHandler)
|
|
373
|
-
{
|
|
374
|
-
completionHandler(nil, NO, err);
|
|
375
|
-
}
|
|
376
|
-
unzClose(zip);
|
|
377
|
-
return NO;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
BOOL success = YES;
|
|
381
|
-
BOOL canceled = NO;
|
|
382
|
-
int crc_ret = 0;
|
|
383
|
-
unsigned char buffer[4096] = {0};
|
|
384
|
-
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
385
|
-
NSMutableArray<NSDictionary *> *directoriesModificationDates = [[NSMutableArray alloc] init];
|
|
386
|
-
|
|
387
|
-
// Message delegate
|
|
388
|
-
if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipArchiveAtPath:zipInfo:)]) {
|
|
389
|
-
[delegate zipArchiveWillUnzipArchiveAtPath:path zipInfo:globalInfo];
|
|
390
|
-
}
|
|
391
|
-
if ([delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) {
|
|
392
|
-
[delegate zipArchiveProgressEvent:currentPosition total:fileSize];
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
NSInteger currentFileNumber = -1;
|
|
396
|
-
NSError *unzippingError;
|
|
397
|
-
do {
|
|
398
|
-
currentFileNumber++;
|
|
399
|
-
if (ret == MZ_END_OF_LIST) {
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
@autoreleasepool {
|
|
403
|
-
if (password.length == 0) {
|
|
404
|
-
ret = unzOpenCurrentFile(zip);
|
|
405
|
-
} else {
|
|
406
|
-
ret = unzOpenCurrentFilePassword(zip, [password cStringUsingEncoding:NSUTF8StringEncoding]);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (ret != UNZ_OK) {
|
|
410
|
-
unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFailedOpenFileInZip userInfo:@{NSLocalizedDescriptionKey: @"failed to open file in zip file"}];
|
|
411
|
-
success = NO;
|
|
412
|
-
break;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// Reading data and write to file
|
|
416
|
-
unz_file_info fileInfo;
|
|
417
|
-
memset(&fileInfo, 0, sizeof(unz_file_info));
|
|
418
|
-
|
|
419
|
-
ret = unzGetCurrentFileInfo(zip, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
|
420
|
-
if (ret != UNZ_OK) {
|
|
421
|
-
unzippingError = [NSError errorWithDomain:@"SSZipArchiveErrorDomain" code:SSZipArchiveErrorCodeFileInfoNotLoadable userInfo:@{NSLocalizedDescriptionKey: @"failed to retrieve info for file"}];
|
|
422
|
-
success = NO;
|
|
423
|
-
unzCloseCurrentFile(zip);
|
|
424
|
-
break;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
currentPosition += fileInfo.compressed_size;
|
|
428
|
-
|
|
429
|
-
// Message delegate
|
|
430
|
-
if ([delegate respondsToSelector:@selector(zipArchiveShouldUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
|
|
431
|
-
if (![delegate zipArchiveShouldUnzipFileAtIndex:currentFileNumber
|
|
432
|
-
totalFiles:(NSInteger)globalInfo.number_entry
|
|
433
|
-
archivePath:path
|
|
434
|
-
fileInfo:fileInfo]) {
|
|
435
|
-
success = NO;
|
|
436
|
-
canceled = YES;
|
|
437
|
-
break;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
if ([delegate respondsToSelector:@selector(zipArchiveWillUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
|
|
441
|
-
[delegate zipArchiveWillUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry
|
|
442
|
-
archivePath:path fileInfo:fileInfo];
|
|
443
|
-
}
|
|
444
|
-
if ([delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) {
|
|
445
|
-
[delegate zipArchiveProgressEvent:(NSInteger)currentPosition total:(NSInteger)fileSize];
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
char *filename = (char *)malloc(fileInfo.size_filename + 1);
|
|
449
|
-
if (filename == NULL)
|
|
450
|
-
{
|
|
451
|
-
success = NO;
|
|
452
|
-
break;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
unzGetCurrentFileInfo(zip, &fileInfo, filename, fileInfo.size_filename + 1, NULL, 0, NULL, 0);
|
|
456
|
-
filename[fileInfo.size_filename] = '\0';
|
|
457
|
-
|
|
458
|
-
BOOL fileIsSymbolicLink = _fileIsSymbolicLink(&fileInfo);
|
|
459
|
-
|
|
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;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// Check if it contains directory
|
|
473
|
-
BOOL isDirectory = NO;
|
|
474
|
-
if (filename[fileInfo.size_filename-1] == '/' || filename[fileInfo.size_filename-1] == '\\') {
|
|
475
|
-
isDirectory = YES;
|
|
476
|
-
}
|
|
477
|
-
free(filename);
|
|
478
|
-
|
|
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;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
NSString *fullPath = [destination stringByAppendingPathComponent:strPath];
|
|
487
|
-
NSError *err = nil;
|
|
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
|
-
}
|
|
494
|
-
if (isDirectory) {
|
|
495
|
-
[fileManager createDirectoryAtPath:fullPath withIntermediateDirectories:YES attributes:directoryAttr error:&err];
|
|
496
|
-
} else {
|
|
497
|
-
[fileManager createDirectoryAtPath:fullPath.stringByDeletingLastPathComponent withIntermediateDirectories:YES attributes:directoryAttr error:&err];
|
|
498
|
-
}
|
|
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
|
-
}
|
|
507
|
-
NSLog(@"[SSZipArchive] Error: %@", err.localizedDescription);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if ([fileManager fileExistsAtPath:fullPath] && !isDirectory && !overwrite) {
|
|
511
|
-
//FIXME: couldBe CRC Check?
|
|
512
|
-
unzCloseCurrentFile(zip);
|
|
513
|
-
ret = unzGoToNextFile(zip);
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
516
|
-
|
|
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;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
if (fp) {
|
|
547
|
-
fclose(fp);
|
|
548
|
-
|
|
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
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
}
|
|
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;
|
|
628
|
-
}
|
|
629
|
-
|
|
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
|
-
}
|
|
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;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
else
|
|
652
|
-
{
|
|
653
|
-
// Assemble the path for the symbolic link
|
|
654
|
-
NSMutableString *destinationPath = [NSMutableString string];
|
|
655
|
-
int bytesRead = 0;
|
|
656
|
-
while ((bytesRead = unzReadCurrentFile(zip, buffer, 4096)) > 0)
|
|
657
|
-
{
|
|
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;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// compose symlink full path
|
|
669
|
-
NSString *symlinkFullDestinationPath = destinationPath;
|
|
670
|
-
if (![symlinkFullDestinationPath isAbsolutePath]) {
|
|
671
|
-
symlinkFullDestinationPath = [[fullPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:destinationPath];
|
|
672
|
-
}
|
|
673
|
-
|
|
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
|
-
}
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
crc_ret = unzCloseCurrentFile(zip);
|
|
713
|
-
if (crc_ret == MZ_CRC_ERROR) {
|
|
714
|
-
// CRC ERROR
|
|
715
|
-
success = NO;
|
|
716
|
-
break;
|
|
717
|
-
}
|
|
718
|
-
ret = unzGoToNextFile(zip);
|
|
719
|
-
|
|
720
|
-
// Message delegate
|
|
721
|
-
if ([delegate respondsToSelector:@selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:fileInfo:)]) {
|
|
722
|
-
[delegate zipArchiveDidUnzipFileAtIndex:currentFileNumber totalFiles:(NSInteger)globalInfo.number_entry
|
|
723
|
-
archivePath:path fileInfo:fileInfo];
|
|
724
|
-
} else if ([delegate respondsToSelector: @selector(zipArchiveDidUnzipFileAtIndex:totalFiles:archivePath:unzippedFilePath:)]) {
|
|
725
|
-
[delegate zipArchiveDidUnzipFileAtIndex: currentFileNumber totalFiles: (NSInteger)globalInfo.number_entry
|
|
726
|
-
archivePath:path unzippedFilePath: fullPath];
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
if (progressHandler)
|
|
730
|
-
{
|
|
731
|
-
progressHandler(strPath, fileInfo, currentFileNumber, globalInfo.number_entry);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
} while (ret == UNZ_OK && success);
|
|
735
|
-
|
|
736
|
-
// Close
|
|
737
|
-
unzClose(zip);
|
|
738
|
-
|
|
739
|
-
// The process of decompressing the .zip archive causes the modification times on the folders
|
|
740
|
-
// to be set to the present time. So, when we are done, they need to be explicitly set.
|
|
741
|
-
// set the modification date on all of the directories.
|
|
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
|
-
}
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
// Message delegate
|
|
755
|
-
if (success && [delegate respondsToSelector:@selector(zipArchiveDidUnzipArchiveAtPath:zipInfo:unzippedPath:)]) {
|
|
756
|
-
[delegate zipArchiveDidUnzipArchiveAtPath:path zipInfo:globalInfo unzippedPath:destination];
|
|
757
|
-
}
|
|
758
|
-
// final progress event = 100%
|
|
759
|
-
if (!canceled && [delegate respondsToSelector:@selector(zipArchiveProgressEvent:total:)]) {
|
|
760
|
-
[delegate zipArchiveProgressEvent:fileSize total:fileSize];
|
|
761
|
-
}
|
|
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
|
-
}
|
|
778
|
-
if (completionHandler)
|
|
779
|
-
{
|
|
780
|
-
if (unzippingError) {
|
|
781
|
-
completionHandler(path, success, unzippingError);
|
|
782
|
-
}
|
|
783
|
-
else
|
|
784
|
-
{
|
|
785
|
-
completionHandler(path, success, retErr);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
return success;
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
#pragma mark - Zipping
|
|
792
|
-
+ (BOOL)createZipFileAtPath:(NSString *)path withFilesAtPaths:(NSArray<NSString *> *)paths
|
|
793
|
-
{
|
|
794
|
-
return [SSZipArchive createZipFileAtPath:path withFilesAtPaths:paths withPassword:nil];
|
|
795
|
-
}
|
|
796
|
-
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath {
|
|
797
|
-
return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath withPassword:nil];
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
+ (BOOL)createZipFileAtPath:(NSString *)path withContentsOfDirectory:(NSString *)directoryPath keepParentDirectory:(BOOL)keepParentDirectory {
|
|
801
|
-
return [SSZipArchive createZipFileAtPath:path withContentsOfDirectory:directoryPath keepParentDirectory:keepParentDirectory withPassword:nil];
|
|
802
|
-
}
|
|
803
|
-
|
|
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
|
|
809
|
-
{
|
|
810
|
-
SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path];
|
|
811
|
-
BOOL success = [zipArchive open];
|
|
812
|
-
if (success) {
|
|
813
|
-
NSUInteger total = paths.count, complete = 0;
|
|
814
|
-
for (NSString *filePath in paths) {
|
|
815
|
-
success &= [zipArchive writeFile:filePath withPassword:password];
|
|
816
|
-
if (progressHandler) {
|
|
817
|
-
complete++;
|
|
818
|
-
progressHandler(complete, total);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
success &= [zipArchive close];
|
|
822
|
-
}
|
|
823
|
-
return success;
|
|
824
|
-
}
|
|
825
|
-
|
|
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
|
-
];
|
|
838
|
-
}
|
|
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
|
-
}
|
|
847
|
-
|
|
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 {
|
|
855
|
-
|
|
856
|
-
SSZipArchive *zipArchive = [[SSZipArchive alloc] initWithPath:path];
|
|
857
|
-
BOOL success = [zipArchive open];
|
|
858
|
-
if (success) {
|
|
859
|
-
// use a local fileManager (queue/thread compatibility)
|
|
860
|
-
NSFileManager *fileManager = [[NSFileManager alloc] init];
|
|
861
|
-
NSDirectoryEnumerator *dirEnumerator = [fileManager enumeratorAtPath:directoryPath];
|
|
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) {
|
|
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;
|
|
880
|
-
[fileManager fileExistsAtPath:fullFilePath isDirectory:&isDir];
|
|
881
|
-
if (!isDir) {
|
|
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];
|
|
889
|
-
}
|
|
890
|
-
}
|
|
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);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
success &= [zipArchive close];
|
|
983
|
-
}
|
|
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;
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
zip_fileinfo zipInfo = {};
|
|
1005
|
-
[SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path];
|
|
1006
|
-
|
|
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;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
// disabling `init` because designated initializer is `initWithPath:`
|
|
1031
|
-
- (instancetype)init { @throw nil; }
|
|
1032
|
-
|
|
1033
|
-
// designated initializer
|
|
1034
|
-
- (instancetype)initWithPath:(NSString *)path
|
|
1035
|
-
{
|
|
1036
|
-
if ((self = [super init])) {
|
|
1037
|
-
_path = [path copy];
|
|
1038
|
-
}
|
|
1039
|
-
return self;
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
- (BOOL)open
|
|
1044
|
-
{
|
|
1045
|
-
NSAssert((_zip == NULL), @"Attempting to open an archive which is already open");
|
|
1046
|
-
_zip = zipOpen(_path.fileSystemRepresentation, APPEND_STATUS_CREATE);
|
|
1047
|
-
return (NULL != _zip);
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
- (BOOL)openForAppending
|
|
1051
|
-
{
|
|
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);
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
- (BOOL)writeFolderAtPath:(NSString *)path withFolderName:(NSString *)folderName withPassword:(nullable NSString *)password
|
|
1058
|
-
{
|
|
1059
|
-
NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened");
|
|
1060
|
-
|
|
1061
|
-
zip_fileinfo zipInfo = {};
|
|
1062
|
-
|
|
1063
|
-
[SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path];
|
|
1064
|
-
|
|
1065
|
-
int error = _zipOpenEntry(_zip, [folderName stringByAppendingString:@"/"], &zipInfo, Z_NO_COMPRESSION, password, NO);
|
|
1066
|
-
const void *buffer = NULL;
|
|
1067
|
-
zipWriteInFileInZip(_zip, buffer, 0);
|
|
1068
|
-
zipCloseFileInZip(_zip);
|
|
1069
|
-
return error == ZIP_OK;
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
- (BOOL)writeFile:(NSString *)path withPassword:(nullable NSString *)password
|
|
1073
|
-
{
|
|
1074
|
-
return [self writeFileAtPath:path withFileName:nil withPassword:password];
|
|
1075
|
-
}
|
|
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
|
-
|
|
1082
|
-
// supports writing files with logical folder/directory structure
|
|
1083
|
-
// *path* is the absolute path of the file that will be compressed
|
|
1084
|
-
// *fileName* is the relative name of the file how it is stored within the zip e.g. /folder/subfolder/text1.txt
|
|
1085
|
-
- (BOOL)writeFileAtPath:(NSString *)path withFileName:(nullable NSString *)fileName compressionLevel:(int)compressionLevel password:(nullable NSString *)password AES:(BOOL)aes
|
|
1086
|
-
{
|
|
1087
|
-
NSAssert((_zip != NULL), @"Attempting to write to an archive which was never opened");
|
|
1088
|
-
|
|
1089
|
-
FILE *input = fopen(path.fileSystemRepresentation, "r");
|
|
1090
|
-
if (NULL == input) {
|
|
1091
|
-
return NO;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
if (!fileName) {
|
|
1095
|
-
fileName = path.lastPathComponent;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
zip_fileinfo zipInfo = {};
|
|
1099
|
-
|
|
1100
|
-
[SSZipArchive zipInfo:&zipInfo setAttributesOfItemAtPath:path];
|
|
1101
|
-
|
|
1102
|
-
void *buffer = malloc(CHUNK);
|
|
1103
|
-
if (buffer == NULL)
|
|
1104
|
-
{
|
|
1105
|
-
fclose(input);
|
|
1106
|
-
return NO;
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
int error = _zipOpenEntry(_zip, fileName, &zipInfo, compressionLevel, password, aes);
|
|
1110
|
-
|
|
1111
|
-
while (!feof(input) && !ferror(input))
|
|
1112
|
-
{
|
|
1113
|
-
unsigned int len = (unsigned int) fread(buffer, 1, CHUNK, input);
|
|
1114
|
-
zipWriteInFileInZip(_zip, buffer, len);
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
zipCloseFileInZip(_zip);
|
|
1118
|
-
free(buffer);
|
|
1119
|
-
fclose(input);
|
|
1120
|
-
return error == ZIP_OK;
|
|
1121
|
-
}
|
|
1122
|
-
|
|
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
|
|
1129
|
-
{
|
|
1130
|
-
if (!_zip) {
|
|
1131
|
-
return NO;
|
|
1132
|
-
}
|
|
1133
|
-
if (!data) {
|
|
1134
|
-
return NO;
|
|
1135
|
-
}
|
|
1136
|
-
zip_fileinfo zipInfo = {};
|
|
1137
|
-
[SSZipArchive zipInfo:&zipInfo setDate:[NSDate date]];
|
|
1138
|
-
|
|
1139
|
-
int error = _zipOpenEntry(_zip, filename, &zipInfo, compressionLevel, password, aes);
|
|
1140
|
-
|
|
1141
|
-
zipWriteInFileInZip(_zip, data.bytes, (unsigned int)data.length);
|
|
1142
|
-
|
|
1143
|
-
zipCloseFileInZip(_zip);
|
|
1144
|
-
return error == ZIP_OK;
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
- (BOOL)close
|
|
1148
|
-
{
|
|
1149
|
-
NSAssert((_zip != NULL), @"[SSZipArchive] Attempting to close an archive which was never opened");
|
|
1150
|
-
int error = zipClose(_zip, NULL);
|
|
1151
|
-
_zip = nil;
|
|
1152
|
-
return error == ZIP_OK;
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
#pragma mark - Private
|
|
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
|
-
|
|
1288
|
-
// Format from http://newsgroups.derkeiler.com/Archive/Comp/comp.os.msdos.programmer/2009-04/msg00060.html
|
|
1289
|
-
// Two consecutive words, or a longword, YYYYYYYMMMMDDDDD hhhhhmmmmmmsssss
|
|
1290
|
-
// YYYYYYY is years from 1980 = 0
|
|
1291
|
-
// sssss is (seconds/2).
|
|
1292
|
-
//
|
|
1293
|
-
// 3658 = 0011 0110 0101 1000 = 0011011 0010 11000 = 27 2 24 = 2007-02-24
|
|
1294
|
-
// 7423 = 0111 0100 0010 0011 - 01110 100001 00011 = 14 33 3 = 14:33:06
|
|
1295
|
-
+ (NSDate *)_dateWithMSDOSFormat:(UInt32)msdosDateTime
|
|
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)];
|
|
1300
|
-
static const UInt32 kYearMask = 0xFE000000;
|
|
1301
|
-
static const UInt32 kMonthMask = 0x1E00000;
|
|
1302
|
-
static const UInt32 kDayMask = 0x1F0000;
|
|
1303
|
-
static const UInt32 kHourMask = 0xF800;
|
|
1304
|
-
static const UInt32 kMinuteMask = 0x7E0;
|
|
1305
|
-
static const UInt32 kSecondMask = 0x1F;
|
|
1306
|
-
|
|
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];
|
|
1419
|
-
#else
|
|
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
|
-
}
|
|
1432
|
-
#endif
|
|
1433
|
-
|
|
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
|
-
}
|
|
1441
|
-
|
|
1442
|
-
// Add scheme "file:///" to support sanitation on names with a colon like "file:a/../../../usr/bin"
|
|
1443
|
-
strPath = [@"file:///" stringByAppendingString:strPath];
|
|
1444
|
-
|
|
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;
|
|
1449
|
-
|
|
1450
|
-
// Remove the "file:///" scheme
|
|
1451
|
-
strPath = strPath.length < 8 ? @"" : [strPath substringFromIndex:8];
|
|
1452
|
-
|
|
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
|
-
}
|
|
1469
|
-
#endif
|
|
1470
|
-
|
|
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;
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
@end
|