@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.
Files changed (191) hide show
  1. package/CodePush.js +22 -19
  2. package/LICENSE.md +1 -1
  3. package/README.md +159 -298
  4. package/android/app/.gradle/config.properties +2 -0
  5. package/android/app/build.gradle +1 -1
  6. package/android/app/local.properties +8 -0
  7. package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +27 -21
  8. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushBuilder.java +5 -5
  9. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushConstants.java +1 -1
  10. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushDialog.java +1 -1
  11. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInstallMode.java +2 -2
  12. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPublicKeyException.java +2 -2
  13. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidUpdateException.java +1 -1
  14. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushMalformedDataException.java +2 -2
  15. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +5 -5
  16. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNotInitializedException.java +2 -2
  17. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushTelemetryManager.java +14 -14
  18. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUnknownException.java +2 -2
  19. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +1 -1
  20. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateState.java +2 -2
  21. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java +1 -1
  22. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java +1 -1
  23. package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgress.java +1 -1
  24. package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressCallback.java +1 -1
  25. package/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java +1 -1
  26. package/android/app/src/main/java/com/microsoft/codepush/react/ReactInstanceHolder.java +1 -1
  27. package/android/app/src/main/java/com/microsoft/codepush/react/SettingsManager.java +1 -1
  28. package/android/app/src/main/java/com/microsoft/codepush/react/TLSSocketFactory.java +1 -1
  29. package/android/build.gradle +1 -1
  30. package/android/codepush.gradle +3 -3
  31. package/docs/advanced-usage.md +56 -0
  32. package/docs/api-android.md +12 -75
  33. package/docs/api-ios.md +5 -17
  34. package/docs/api-js.md +18 -55
  35. package/docs/setup-android.md +15 -397
  36. package/docs/setup-ios.md +24 -198
  37. package/docs/setup-windows.md +7 -74
  38. package/ios/CodePush/CodePush.h +4 -4
  39. package/ios/CodePush/CodePush.m +8 -8
  40. package/ios/CodePush/CodePushConfig.m +14 -11
  41. package/ios/CodePush/CodePushPackage.m +60 -60
  42. package/ios/CodePush/CodePushTelemetryManager.m +13 -13
  43. package/ios/CodePush.xcodeproj/project.pbxproj +1 -562
  44. package/package.json +27 -24
  45. package/react-native.config.js +1 -1
  46. package/scripts/generateBundledResourcesHash.js +1 -1
  47. package/scripts/getFilesInFolder.js +1 -1
  48. package/scripts/recordFilesBeforeBundleCommand.js +1 -1
  49. package/typings/react-native-code-push.d.ts +22 -22
  50. package/windows/CodePush/CodePushConfig.cpp +3 -3
  51. package/windows/CodePush/CodePushConfig.h +3 -3
  52. package/windows/CodePush/CodePushNativeModule.cpp +27 -27
  53. package/windows/CodePush/CodePushNativeModule.h +4 -4
  54. package/windows/CodePush/CodePushTelemetryManager.cpp +12 -12
  55. package/windows/CodePush/CodePushTelemetryManager.h +1 -1
  56. package/.azurepipelines/build-rn-code-push-1es.yml +0 -104
  57. package/.azurepipelines/test-rn-code-push.yml +0 -94
  58. package/.config/CredScanSuppressions.json +0 -14
  59. package/SECURITY.md +0 -41
  60. package/docs/multi-deployment-testing-android.md +0 -148
  61. package/docs/multi-deployment-testing-ios.md +0 -59
  62. package/ios/CodePush/Base64/Base64/MF_Base64Additions.h +0 -34
  63. package/ios/CodePush/Base64/Base64/MF_Base64Additions.m +0 -252
  64. package/ios/CodePush/Base64/README.md +0 -47
  65. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithm.h +0 -69
  66. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.h +0 -16
  67. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.m +0 -51
  68. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.h +0 -15
  69. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.m +0 -55
  70. package/ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.h +0 -24
  71. package/ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.m +0 -41
  72. package/ios/CodePush/JWT/Core/Algorithms/HSFamily/JWTAlgorithmHSBase.h +0 -28
  73. package/ios/CodePush/JWT/Core/Algorithms/HSFamily/JWTAlgorithmHSBase.m +0 -205
  74. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolder.h +0 -103
  75. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolder.m +0 -322
  76. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolderChain.h +0 -37
  77. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolderChain.m +0 -145
  78. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTAlgorithmRSBase.h +0 -35
  79. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTAlgorithmRSBase.m +0 -551
  80. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTRSAlgorithm.h +0 -23
  81. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKey.h +0 -43
  82. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKey.m +0 -230
  83. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKeyExtractor.h +0 -31
  84. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKeyExtractor.m +0 -113
  85. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoSecurity.h +0 -38
  86. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoSecurity.m +0 -500
  87. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaim.h +0 -18
  88. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaim.m +0 -214
  89. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.h +0 -23
  90. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.m +0 -29
  91. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.h +0 -19
  92. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.m +0 -68
  93. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.h +0 -18
  94. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.m +0 -72
  95. package/ios/CodePush/JWT/Core/Coding/JWTCoding+ResultTypes.h +0 -67
  96. package/ios/CodePush/JWT/Core/Coding/JWTCoding+ResultTypes.m +0 -111
  97. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionOne.h +0 -119
  98. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionOne.m +0 -307
  99. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionThree.h +0 -94
  100. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionThree.m +0 -619
  101. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionTwo.h +0 -164
  102. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionTwo.m +0 -514
  103. package/ios/CodePush/JWT/Core/Coding/JWTCoding.h +0 -24
  104. package/ios/CodePush/JWT/Core/Coding/JWTCoding.m +0 -11
  105. package/ios/CodePush/JWT/Core/FrameworkSupplement/JWT.h +0 -52
  106. package/ios/CodePush/JWT/Core/FrameworkSupplement/Map.modulemap +0 -5
  107. package/ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.h +0 -28
  108. package/ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.m +0 -70
  109. package/ios/CodePush/JWT/Core/Supplement/JWTDeprecations.h +0 -22
  110. package/ios/CodePush/JWT/Core/Supplement/JWTErrorDescription.h +0 -34
  111. package/ios/CodePush/JWT/Core/Supplement/JWTErrorDescription.m +0 -73
  112. package/ios/CodePush/JWT/LICENSE +0 -19
  113. package/ios/CodePush/JWT/README.md +0 -489
  114. package/ios/CodePush/SSZipArchive/Info.plist +0 -26
  115. package/ios/CodePush/SSZipArchive/README.md +0 -1
  116. package/ios/CodePush/SSZipArchive/SSZipArchive.h +0 -178
  117. package/ios/CodePush/SSZipArchive/SSZipArchive.m +0 -1496
  118. package/ios/CodePush/SSZipArchive/SSZipCommon.h +0 -71
  119. package/ios/CodePush/SSZipArchive/Supporting Files/PrivacyInfo.xcprivacy +0 -23
  120. package/ios/CodePush/SSZipArchive/include/ZipArchive.h +0 -25
  121. package/ios/CodePush/SSZipArchive/minizip/LICENSE +0 -17
  122. package/ios/CodePush/SSZipArchive/minizip/mz.h +0 -273
  123. package/ios/CodePush/SSZipArchive/minizip/mz_compat.c +0 -1306
  124. package/ios/CodePush/SSZipArchive/minizip/mz_compat.h +0 -346
  125. package/ios/CodePush/SSZipArchive/minizip/mz_crypt.c +0 -187
  126. package/ios/CodePush/SSZipArchive/minizip/mz_crypt.h +0 -65
  127. package/ios/CodePush/SSZipArchive/minizip/mz_crypt_apple.c +0 -526
  128. package/ios/CodePush/SSZipArchive/minizip/mz_os.c +0 -348
  129. package/ios/CodePush/SSZipArchive/minizip/mz_os.h +0 -176
  130. package/ios/CodePush/SSZipArchive/minizip/mz_os_posix.c +0 -350
  131. package/ios/CodePush/SSZipArchive/minizip/mz_strm.c +0 -556
  132. package/ios/CodePush/SSZipArchive/minizip/mz_strm.h +0 -132
  133. package/ios/CodePush/SSZipArchive/minizip/mz_strm_buf.c +0 -383
  134. package/ios/CodePush/SSZipArchive/minizip/mz_strm_buf.h +0 -42
  135. package/ios/CodePush/SSZipArchive/minizip/mz_strm_mem.c +0 -269
  136. package/ios/CodePush/SSZipArchive/minizip/mz_strm_mem.h +0 -48
  137. package/ios/CodePush/SSZipArchive/minizip/mz_strm_os.h +0 -40
  138. package/ios/CodePush/SSZipArchive/minizip/mz_strm_os_posix.c +0 -203
  139. package/ios/CodePush/SSZipArchive/minizip/mz_strm_pkcrypt.c +0 -334
  140. package/ios/CodePush/SSZipArchive/minizip/mz_strm_pkcrypt.h +0 -46
  141. package/ios/CodePush/SSZipArchive/minizip/mz_strm_split.c +0 -429
  142. package/ios/CodePush/SSZipArchive/minizip/mz_strm_split.h +0 -43
  143. package/ios/CodePush/SSZipArchive/minizip/mz_strm_wzaes.c +0 -360
  144. package/ios/CodePush/SSZipArchive/minizip/mz_strm_wzaes.h +0 -46
  145. package/ios/CodePush/SSZipArchive/minizip/mz_strm_zlib.c +0 -389
  146. package/ios/CodePush/SSZipArchive/minizip/mz_strm_zlib.h +0 -43
  147. package/ios/CodePush/SSZipArchive/minizip/mz_zip.c +0 -2782
  148. package/ios/CodePush/SSZipArchive/minizip/mz_zip.h +0 -262
  149. package/ios/CodePush/SSZipArchive/minizip/mz_zip_rw.c +0 -1942
  150. package/ios/CodePush/SSZipArchive/minizip/mz_zip_rw.h +0 -285
  151. package/scripts/postlink/android/postlink.js +0 -87
  152. package/scripts/postlink/ios/postlink.js +0 -116
  153. package/scripts/postlink/run.js +0 -11
  154. package/scripts/postunlink/android/postunlink.js +0 -74
  155. package/scripts/postunlink/ios/postunlink.js +0 -87
  156. package/scripts/postunlink/run.js +0 -11
  157. package/scripts/tools/linkToolsAndroid.js +0 -57
  158. package/scripts/tools/linkToolsIos.js +0 -130
  159. package/windows-legacy/CodePush/CodePush.csproj +0 -128
  160. package/windows-legacy/CodePush/CodePushUtils.cs +0 -47
  161. package/windows-legacy/CodePush/FileUtils.cs +0 -40
  162. package/windows-legacy/CodePush/Properties/AssemblyInfo.cs +0 -29
  163. package/windows-legacy/CodePush/Properties/CodePush.rd.xml +0 -33
  164. package/windows-legacy/CodePush/UpdateManager.cs +0 -305
  165. package/windows-legacy/CodePush/UpdateUtils.cs +0 -46
  166. package/windows-legacy/CodePush.Net46/Adapters/Http/HttpProgress.cs +0 -28
  167. package/windows-legacy/CodePush.Net46/Adapters/Storage/ApplicationDataContainer.cs +0 -106
  168. package/windows-legacy/CodePush.Net46/CodePush.Net46.csproj +0 -103
  169. package/windows-legacy/CodePush.Net46/CodePushUtils.cs +0 -158
  170. package/windows-legacy/CodePush.Net46/FileUtils.cs +0 -55
  171. package/windows-legacy/CodePush.Net46/Properties/AssemblyInfo.cs +0 -36
  172. package/windows-legacy/CodePush.Net46/UpdateManager.cs +0 -330
  173. package/windows-legacy/CodePush.Net46/UpdateUtils.cs +0 -70
  174. package/windows-legacy/CodePush.Net46/packages.config +0 -5
  175. package/windows-legacy/CodePush.Net46.Test/ApplicationDataContainerTest.cs +0 -105
  176. package/windows-legacy/CodePush.Net46.Test/CodePush.Net46.Test.csproj +0 -137
  177. package/windows-legacy/CodePush.Net46.Test/Properties/AssemblyInfo.cs +0 -36
  178. package/windows-legacy/CodePush.Net46.Test/TelemetryManagerTest.cs +0 -117
  179. package/windows-legacy/CodePush.Net46.Test/app.config +0 -11
  180. package/windows-legacy/CodePush.Net46.Test/packages.config +0 -4
  181. package/windows-legacy/CodePush.Shared/CodePush.Shared.projitems +0 -22
  182. package/windows-legacy/CodePush.Shared/CodePush.Shared.shproj +0 -13
  183. package/windows-legacy/CodePush.Shared/CodePushConstants.cs +0 -35
  184. package/windows-legacy/CodePush.Shared/CodePushNativeModule.cs +0 -329
  185. package/windows-legacy/CodePush.Shared/CodePushReactPackage.cs +0 -235
  186. package/windows-legacy/CodePush.Shared/CodePushUtils.cs +0 -70
  187. package/windows-legacy/CodePush.Shared/InstallMode.cs +0 -9
  188. package/windows-legacy/CodePush.Shared/MinimumBackgroundListener.cs +0 -44
  189. package/windows-legacy/CodePush.Shared/SettingsManager.cs +0 -148
  190. package/windows-legacy/CodePush.Shared/TelemetryManager.cs +0 -250
  191. 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