@capgo/capacitor-updater 6.11.1 → 6.12.0
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/Package.swift +3 -3
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +9 -196
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +85 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipherV2.java +147 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +24 -4
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +2 -0
- package/ios/Plugin/CapacitorUpdater.swift +124 -495
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +106 -96
- package/ios/Plugin/CryptoCipher.swift +77 -0
- package/ios/Plugin/CryptoCipherV2.swift +95 -1
- package/ios/Plugin/InternalUtils.swift +258 -0
- package/package.json +2 -1
|
@@ -5,262 +5,19 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import Foundation
|
|
8
|
+
#if canImport(ZipArchive)
|
|
9
|
+
import ZipArchive
|
|
10
|
+
#else
|
|
8
11
|
import SSZipArchive
|
|
12
|
+
#endif
|
|
9
13
|
import Alamofire
|
|
10
|
-
import zlib
|
|
11
|
-
import CryptoKit
|
|
12
14
|
import Compression
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
extension URL {
|
|
20
|
-
var isDirectory: Bool {
|
|
21
|
-
(try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
|
|
22
|
-
}
|
|
23
|
-
var exist: Bool {
|
|
24
|
-
return FileManager().fileExists(atPath: self.path)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
struct SetChannelDec: Decodable {
|
|
28
|
-
let status: String?
|
|
29
|
-
let error: String?
|
|
30
|
-
let message: String?
|
|
31
|
-
}
|
|
32
|
-
public class SetChannel: NSObject {
|
|
33
|
-
var status: String = ""
|
|
34
|
-
var error: String = ""
|
|
35
|
-
var message: String = ""
|
|
36
|
-
}
|
|
37
|
-
extension SetChannel {
|
|
38
|
-
func toDict() -> [String: Any] {
|
|
39
|
-
var dict: [String: Any] = [String: Any]()
|
|
40
|
-
let otherSelf: Mirror = Mirror(reflecting: self)
|
|
41
|
-
for child: Mirror.Child in otherSelf.children {
|
|
42
|
-
if let key: String = child.label {
|
|
43
|
-
dict[key] = child.value
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return dict
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
struct GetChannelDec: Decodable {
|
|
50
|
-
let channel: String?
|
|
51
|
-
let status: String?
|
|
52
|
-
let error: String?
|
|
53
|
-
let message: String?
|
|
54
|
-
let allowSet: Bool?
|
|
55
|
-
}
|
|
56
|
-
public class GetChannel: NSObject {
|
|
57
|
-
var channel: String = ""
|
|
58
|
-
var status: String = ""
|
|
59
|
-
var error: String = ""
|
|
60
|
-
var message: String = ""
|
|
61
|
-
var allowSet: Bool = true
|
|
62
|
-
}
|
|
63
|
-
extension GetChannel {
|
|
64
|
-
func toDict() -> [String: Any] {
|
|
65
|
-
var dict: [String: Any] = [String: Any]()
|
|
66
|
-
let otherSelf: Mirror = Mirror(reflecting: self)
|
|
67
|
-
for child: Mirror.Child in otherSelf.children {
|
|
68
|
-
if let key: String = child.label {
|
|
69
|
-
dict[key] = child.value
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return dict
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
struct InfoObject: Codable {
|
|
76
|
-
let platform: String?
|
|
77
|
-
let device_id: String?
|
|
78
|
-
let app_id: String?
|
|
79
|
-
let custom_id: String?
|
|
80
|
-
let version_build: String?
|
|
81
|
-
let version_code: String?
|
|
82
|
-
let version_os: String?
|
|
83
|
-
var version_name: String?
|
|
84
|
-
var old_version_name: String?
|
|
85
|
-
let plugin_version: String?
|
|
86
|
-
let is_emulator: Bool?
|
|
87
|
-
let is_prod: Bool?
|
|
88
|
-
var action: String?
|
|
89
|
-
var channel: String?
|
|
90
|
-
var defaultChannel: String?
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
public struct ManifestEntry: Codable {
|
|
94
|
-
let file_name: String?
|
|
95
|
-
let file_hash: String?
|
|
96
|
-
let download_url: String?
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
extension ManifestEntry {
|
|
100
|
-
func toDict() -> [String: Any] {
|
|
101
|
-
var dict: [String: Any] = [String: Any]()
|
|
102
|
-
let mirror = Mirror(reflecting: self)
|
|
103
|
-
for child in mirror.children {
|
|
104
|
-
if let key = child.label {
|
|
105
|
-
dict[key] = child.value
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return dict
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
struct AppVersionDec: Decodable {
|
|
113
|
-
let version: String?
|
|
114
|
-
let checksum: String?
|
|
115
|
-
let url: String?
|
|
116
|
-
let message: String?
|
|
117
|
-
let error: String?
|
|
118
|
-
let session_key: String?
|
|
119
|
-
let major: Bool?
|
|
120
|
-
let data: [String: String]?
|
|
121
|
-
let manifest: [ManifestEntry]?
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
public class AppVersion: NSObject {
|
|
125
|
-
var version: String = ""
|
|
126
|
-
var checksum: String = ""
|
|
127
|
-
var url: String = ""
|
|
128
|
-
var message: String?
|
|
129
|
-
var error: String?
|
|
130
|
-
var sessionKey: String?
|
|
131
|
-
var major: Bool?
|
|
132
|
-
var data: [String: String]?
|
|
133
|
-
var manifest: [ManifestEntry]?
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
extension AppVersion {
|
|
137
|
-
func toDict() -> [String: Any] {
|
|
138
|
-
var dict: [String: Any] = [String: Any]()
|
|
139
|
-
let otherSelf: Mirror = Mirror(reflecting: self)
|
|
140
|
-
for child: Mirror.Child in otherSelf.children {
|
|
141
|
-
if let key: String = child.label {
|
|
142
|
-
if key == "manifest", let manifestEntries = child.value as? [ManifestEntry] {
|
|
143
|
-
dict[key] = manifestEntries.map { $0.toDict() }
|
|
144
|
-
} else {
|
|
145
|
-
dict[key] = child.value
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return dict
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
extension OperatingSystemVersion {
|
|
154
|
-
func getFullVersion(separator: String = ".") -> String {
|
|
155
|
-
return "\(majorVersion)\(separator)\(minorVersion)\(separator)\(patchVersion)"
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
extension Bundle {
|
|
159
|
-
var versionName: String? {
|
|
160
|
-
return infoDictionary?["CFBundleShortVersionString"] as? String
|
|
161
|
-
}
|
|
162
|
-
var versionCode: String? {
|
|
163
|
-
return infoDictionary?["CFBundleVersion"] as? String
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
extension ISO8601DateFormatter {
|
|
168
|
-
convenience init(_ formatOptions: Options) {
|
|
169
|
-
self.init()
|
|
170
|
-
self.formatOptions = formatOptions
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
extension Formatter {
|
|
174
|
-
static let iso8601withFractionalSeconds: ISO8601DateFormatter = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
|
|
175
|
-
}
|
|
176
|
-
extension Date {
|
|
177
|
-
var iso8601withFractionalSeconds: String { return Formatter.iso8601withFractionalSeconds.string(from: self) }
|
|
178
|
-
}
|
|
179
|
-
extension String {
|
|
180
|
-
|
|
181
|
-
var fileURL: URL {
|
|
182
|
-
return URL(fileURLWithPath: self)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
var lastPathComponent: String {
|
|
186
|
-
get {
|
|
187
|
-
return fileURL.lastPathComponent
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
var iso8601withFractionalSeconds: Date? {
|
|
191
|
-
return Formatter.iso8601withFractionalSeconds.date(from: self)
|
|
192
|
-
}
|
|
193
|
-
func trim(using characterSet: CharacterSet = .whitespacesAndNewlines) -> String {
|
|
194
|
-
return trimmingCharacters(in: characterSet)
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
enum CustomError: Error {
|
|
199
|
-
// Throw when an unzip fail
|
|
200
|
-
case cannotUnzip
|
|
201
|
-
case cannotWrite
|
|
202
|
-
case cannotDecode
|
|
203
|
-
case cannotUnflat
|
|
204
|
-
case cannotCreateDirectory
|
|
205
|
-
case cannotDeleteDirectory
|
|
206
|
-
case cannotDecryptSessionKey
|
|
207
|
-
case invalidBase64
|
|
208
|
-
|
|
209
|
-
// Throw in all other cases
|
|
210
|
-
case unexpected(code: Int)
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
extension CustomError: LocalizedError {
|
|
214
|
-
public var errorDescription: String? {
|
|
215
|
-
switch self {
|
|
216
|
-
case .cannotUnzip:
|
|
217
|
-
return NSLocalizedString(
|
|
218
|
-
"The file cannot be unzip",
|
|
219
|
-
comment: "Invalid zip"
|
|
220
|
-
)
|
|
221
|
-
case .cannotCreateDirectory:
|
|
222
|
-
return NSLocalizedString(
|
|
223
|
-
"The folder cannot be created",
|
|
224
|
-
comment: "Invalid folder"
|
|
225
|
-
)
|
|
226
|
-
case .cannotDeleteDirectory:
|
|
227
|
-
return NSLocalizedString(
|
|
228
|
-
"The folder cannot be deleted",
|
|
229
|
-
comment: "Invalid folder"
|
|
230
|
-
)
|
|
231
|
-
case .cannotUnflat:
|
|
232
|
-
return NSLocalizedString(
|
|
233
|
-
"The file cannot be unflat",
|
|
234
|
-
comment: "Invalid folder"
|
|
235
|
-
)
|
|
236
|
-
case .unexpected:
|
|
237
|
-
return NSLocalizedString(
|
|
238
|
-
"An unexpected error occurred.",
|
|
239
|
-
comment: "Unexpected Error"
|
|
240
|
-
)
|
|
241
|
-
case .cannotDecode:
|
|
242
|
-
return NSLocalizedString(
|
|
243
|
-
"Decoding the zip failed with this key",
|
|
244
|
-
comment: "Invalid public key"
|
|
245
|
-
)
|
|
246
|
-
case .cannotWrite:
|
|
247
|
-
return NSLocalizedString(
|
|
248
|
-
"Cannot write to the destination",
|
|
249
|
-
comment: "Invalid destination"
|
|
250
|
-
)
|
|
251
|
-
case .cannotDecryptSessionKey:
|
|
252
|
-
return NSLocalizedString(
|
|
253
|
-
"Decrypting the session key failed",
|
|
254
|
-
comment: "Invalid session key"
|
|
255
|
-
)
|
|
256
|
-
case .invalidBase64:
|
|
257
|
-
return NSLocalizedString(
|
|
258
|
-
"Decrypting the base64 failed",
|
|
259
|
-
comment: "Invalid checksum key"
|
|
260
|
-
)
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
16
|
+
#if canImport(ZipArchive)
|
|
17
|
+
typealias ZipArchiveHelper = ZipArchive
|
|
18
|
+
#else
|
|
19
|
+
typealias ZipArchiveHelper = SSZipArchive
|
|
20
|
+
#endif
|
|
264
21
|
|
|
265
22
|
@objc public class CapacitorUpdater: NSObject {
|
|
266
23
|
|
|
@@ -277,7 +34,7 @@ extension CustomError: LocalizedError {
|
|
|
277
34
|
// Add this line to declare cacheFolder
|
|
278
35
|
private let cacheFolder: URL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("capgo_downloads")
|
|
279
36
|
|
|
280
|
-
public let TAG: String = "✨ Capacitor-updater:"
|
|
37
|
+
static public let TAG: String = "✨ Capacitor-updater:"
|
|
281
38
|
public let CAP_SERVER_PATH: String = "serverBasePath"
|
|
282
39
|
public var versionBuild: String = ""
|
|
283
40
|
public var customId: String = ""
|
|
@@ -358,7 +115,7 @@ extension CustomError: LocalizedError {
|
|
|
358
115
|
do {
|
|
359
116
|
try FileManager.default.createDirectory(atPath: source.path, withIntermediateDirectories: true, attributes: nil)
|
|
360
117
|
} catch {
|
|
361
|
-
print("\(
|
|
118
|
+
print("\(CapacitorUpdater.TAG) Cannot createDirectory \(source.path)")
|
|
362
119
|
throw CustomError.cannotCreateDirectory
|
|
363
120
|
}
|
|
364
121
|
}
|
|
@@ -368,7 +125,7 @@ extension CustomError: LocalizedError {
|
|
|
368
125
|
do {
|
|
369
126
|
try FileManager.default.removeItem(atPath: source.path)
|
|
370
127
|
} catch {
|
|
371
|
-
print("\(
|
|
128
|
+
print("\(CapacitorUpdater.TAG) File not removed. \(source.path)")
|
|
372
129
|
throw CustomError.cannotDeleteDirectory
|
|
373
130
|
}
|
|
374
131
|
}
|
|
@@ -385,105 +142,14 @@ extension CustomError: LocalizedError {
|
|
|
385
142
|
return false
|
|
386
143
|
}
|
|
387
144
|
} catch {
|
|
388
|
-
print("\(
|
|
145
|
+
print("\(CapacitorUpdater.TAG) File not moved. source: \(source.path) dest: \(dest.path)")
|
|
389
146
|
throw CustomError.cannotUnflat
|
|
390
147
|
}
|
|
391
148
|
}
|
|
392
149
|
|
|
393
|
-
private func decryptFileV2(filePath: URL, sessionKey: String, version: String) throws {
|
|
394
|
-
if self.publicKey.isEmpty || sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
|
|
395
|
-
print("\(self.TAG) Cannot find public key or sessionKey")
|
|
396
|
-
return
|
|
397
|
-
}
|
|
398
|
-
do {
|
|
399
|
-
guard let rsaPublicKey: RSAPublicKey = .load(rsaPublicKey: self.publicKey) else {
|
|
400
|
-
print("cannot decode publicKey", self.publicKey)
|
|
401
|
-
throw CustomError.cannotDecode
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
let sessionKeyArray: [String] = sessionKey.components(separatedBy: ":")
|
|
405
|
-
guard let ivData: Data = Data(base64Encoded: sessionKeyArray[0]) else {
|
|
406
|
-
print("cannot decode sessionKey", sessionKey)
|
|
407
|
-
throw CustomError.cannotDecode
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
guard let sessionKeyDataEncrypted = Data(base64Encoded: sessionKeyArray[1]) else {
|
|
411
|
-
throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
guard let sessionKeyDataDecrypted = rsaPublicKey.decrypt(data: sessionKeyDataEncrypted) else {
|
|
415
|
-
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted)
|
|
419
|
-
|
|
420
|
-
guard let encryptedData = try? Data(contentsOf: filePath) else {
|
|
421
|
-
throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
|
|
425
|
-
throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
try decryptedData.write(to: filePath)
|
|
429
|
-
|
|
430
|
-
} catch {
|
|
431
|
-
print("\(self.TAG) Cannot decode: \(filePath.path)", error)
|
|
432
|
-
self.sendStats(action: "decrypt_fail", versionName: version)
|
|
433
|
-
throw CustomError.cannotDecode
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
private func decryptFile(filePath: URL, sessionKey: String, version: String) throws {
|
|
438
|
-
if self.privateKey.isEmpty {
|
|
439
|
-
print("\(self.TAG) Cannot found privateKey")
|
|
440
|
-
return
|
|
441
|
-
} else if sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
|
|
442
|
-
print("\(self.TAG) Cannot found sessionKey")
|
|
443
|
-
return
|
|
444
|
-
}
|
|
445
|
-
do {
|
|
446
|
-
guard let rsaPrivateKey: RSAPrivateKey = .load(rsaPrivateKey: self.privateKey) else {
|
|
447
|
-
print("cannot decode privateKey", self.privateKey)
|
|
448
|
-
throw CustomError.cannotDecode
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
let sessionKeyArray: [String] = sessionKey.components(separatedBy: ":")
|
|
452
|
-
guard let ivData: Data = Data(base64Encoded: sessionKeyArray[0]) else {
|
|
453
|
-
print("cannot decode sessionKey", sessionKey)
|
|
454
|
-
throw CustomError.cannotDecode
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
guard let sessionKeyDataEncrypted = Data(base64Encoded: sessionKeyArray[1]) else {
|
|
458
|
-
throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
guard let sessionKeyDataDecrypted = rsaPrivateKey.decrypt(data: sessionKeyDataEncrypted) else {
|
|
462
|
-
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted)
|
|
466
|
-
|
|
467
|
-
guard let encryptedData = try? Data(contentsOf: filePath) else {
|
|
468
|
-
throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
|
|
472
|
-
throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
try decryptedData.write(to: filePath)
|
|
476
|
-
|
|
477
|
-
} catch {
|
|
478
|
-
print("\(self.TAG) Cannot decode: \(filePath.path)", error)
|
|
479
|
-
self.sendStats(action: "decrypt_fail", versionName: version)
|
|
480
|
-
throw CustomError.cannotDecode
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
150
|
private func unzipProgressHandler(entry: String, zipInfo: unz_file_info, entryNumber: Int, total: Int, destUnZip: URL, id: String, unzipError: inout NSError?) {
|
|
485
151
|
if entry.contains("\\") {
|
|
486
|
-
print("\(
|
|
152
|
+
print("\(CapacitorUpdater.TAG) unzip: Windows path is not supported, please use unix path as required by zip RFC: \(entry)")
|
|
487
153
|
self.sendStats(action: "windows_path_fail")
|
|
488
154
|
}
|
|
489
155
|
|
|
@@ -527,7 +193,7 @@ extension CustomError: LocalizedError {
|
|
|
527
193
|
let semaphore = DispatchSemaphore(value: 0)
|
|
528
194
|
var unzipError: NSError?
|
|
529
195
|
|
|
530
|
-
let success =
|
|
196
|
+
let success = ZipArchiveHelper.unzipFile(atPath: sourceZip.path,
|
|
531
197
|
toDestination: destUnZip.path,
|
|
532
198
|
preserveAttributes: true,
|
|
533
199
|
overwrite: true,
|
|
@@ -586,7 +252,7 @@ extension CustomError: LocalizedError {
|
|
|
586
252
|
if let channel = channel {
|
|
587
253
|
parameters.defaultChannel = channel
|
|
588
254
|
}
|
|
589
|
-
print("\(
|
|
255
|
+
print("\(CapacitorUpdater.TAG) Auto-update parameters: \(parameters)")
|
|
590
256
|
let request = AF.request(url, method: .post, parameters: parameters, encoder: JSONParameterEncoder.default, requestModifier: { $0.timeoutInterval = self.timeout })
|
|
591
257
|
|
|
592
258
|
request.validate().responseDecodable(of: AppVersionDec.self) { response in
|
|
@@ -620,7 +286,7 @@ extension CustomError: LocalizedError {
|
|
|
620
286
|
latest.manifest = manifest
|
|
621
287
|
}
|
|
622
288
|
case let .failure(error):
|
|
623
|
-
print("\(
|
|
289
|
+
print("\(CapacitorUpdater.TAG) Error getting Latest", response.value ?? "", error )
|
|
624
290
|
latest.message = "Error getting Latest \(String(describing: response.value))"
|
|
625
291
|
latest.error = "response_error"
|
|
626
292
|
}
|
|
@@ -633,63 +299,7 @@ extension CustomError: LocalizedError {
|
|
|
633
299
|
private func setCurrentBundle(bundle: String) {
|
|
634
300
|
UserDefaults.standard.set(bundle, forKey: self.CAP_SERVER_PATH)
|
|
635
301
|
UserDefaults.standard.synchronize()
|
|
636
|
-
print("\(
|
|
637
|
-
}
|
|
638
|
-
private func calcChecksum(filePath: URL) -> String {
|
|
639
|
-
let bufferSize = 1024 * 1024 * 5 // 5 MB
|
|
640
|
-
var checksum = uLong(0)
|
|
641
|
-
|
|
642
|
-
do {
|
|
643
|
-
let fileHandle = try FileHandle(forReadingFrom: filePath)
|
|
644
|
-
defer {
|
|
645
|
-
fileHandle.closeFile()
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
while autoreleasepool(invoking: {
|
|
649
|
-
let fileData = fileHandle.readData(ofLength: bufferSize)
|
|
650
|
-
if fileData.count > 0 {
|
|
651
|
-
checksum = fileData.withUnsafeBytes {
|
|
652
|
-
crc32(checksum, $0.bindMemory(to: Bytef.self).baseAddress, uInt(fileData.count))
|
|
653
|
-
}
|
|
654
|
-
return true // Continue
|
|
655
|
-
} else {
|
|
656
|
-
return false // End of file
|
|
657
|
-
}
|
|
658
|
-
}) {}
|
|
659
|
-
|
|
660
|
-
return String(format: "%08X", checksum).lowercased()
|
|
661
|
-
} catch {
|
|
662
|
-
print("\(self.TAG) Cannot get checksum: \(filePath.path)", error)
|
|
663
|
-
return ""
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
private func calcChecksumV2(filePath: URL) -> String {
|
|
668
|
-
let bufferSize = 1024 * 1024 * 5 // 5 MB
|
|
669
|
-
var sha256 = SHA256()
|
|
670
|
-
|
|
671
|
-
do {
|
|
672
|
-
let fileHandle = try FileHandle(forReadingFrom: filePath)
|
|
673
|
-
defer {
|
|
674
|
-
fileHandle.closeFile()
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
while autoreleasepool(invoking: {
|
|
678
|
-
let fileData = fileHandle.readData(ofLength: bufferSize)
|
|
679
|
-
if fileData.count > 0 {
|
|
680
|
-
sha256.update(data: fileData)
|
|
681
|
-
return true // Continue
|
|
682
|
-
} else {
|
|
683
|
-
return false // End of file
|
|
684
|
-
}
|
|
685
|
-
}) {}
|
|
686
|
-
|
|
687
|
-
let digest = sha256.finalize()
|
|
688
|
-
return digest.compactMap { String(format: "%02x", $0) }.joined()
|
|
689
|
-
} catch {
|
|
690
|
-
print("\(self.TAG) Cannot get checksum: \(filePath.path)", error)
|
|
691
|
-
return ""
|
|
692
|
-
}
|
|
302
|
+
print("\(CapacitorUpdater.TAG) Current bundle set to: \((bundle ).isEmpty ? BundleInfo.ID_BUILTIN : bundle)")
|
|
693
303
|
}
|
|
694
304
|
|
|
695
305
|
private var tempDataPath: URL {
|
|
@@ -701,35 +311,14 @@ extension CustomError: LocalizedError {
|
|
|
701
311
|
}
|
|
702
312
|
private var tempData = Data()
|
|
703
313
|
|
|
704
|
-
public func decryptChecksum(checksum: String, version: String) throws -> String {
|
|
705
|
-
if self.publicKey.isEmpty {
|
|
706
|
-
return checksum
|
|
707
|
-
}
|
|
708
|
-
do {
|
|
709
|
-
let checksumBytes: Data = Data(base64Encoded: checksum)!
|
|
710
|
-
guard let rsaPublicKey: RSAPublicKey = .load(rsaPublicKey: self.publicKey) else {
|
|
711
|
-
print("cannot decode publicKey", self.publicKey)
|
|
712
|
-
throw CustomError.cannotDecode
|
|
713
|
-
}
|
|
714
|
-
guard let decryptedChecksum = try? rsaPublicKey.decrypt(data: checksumBytes) else {
|
|
715
|
-
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
716
|
-
}
|
|
717
|
-
return decryptedChecksum.base64EncodedString()
|
|
718
|
-
} catch {
|
|
719
|
-
print("\(self.TAG) Cannot decrypt checksum: \(checksum)", error)
|
|
720
|
-
self.sendStats(action: "decrypt_fail", versionName: version)
|
|
721
|
-
throw CustomError.cannotDecode
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
314
|
private func verifyChecksum(file: URL, expectedHash: String) -> Bool {
|
|
726
|
-
let actualHash =
|
|
315
|
+
let actualHash = CryptoCipherV2.calcChecksum(filePath: file)
|
|
727
316
|
return actualHash == expectedHash
|
|
728
317
|
}
|
|
729
318
|
|
|
730
319
|
public func downloadManifest(manifest: [ManifestEntry], version: String, sessionKey: String) throws -> BundleInfo {
|
|
731
320
|
let id = self.randomString(length: 10)
|
|
732
|
-
print("\(
|
|
321
|
+
print("\(CapacitorUpdater.TAG) downloadManifest start \(id)")
|
|
733
322
|
let destFolder = self.getBundleDirectory(id: id)
|
|
734
323
|
let builtinFolder = Bundle.main.bundleURL.appendingPathComponent("public")
|
|
735
324
|
|
|
@@ -751,10 +340,19 @@ extension CustomError: LocalizedError {
|
|
|
751
340
|
|
|
752
341
|
for entry in manifest {
|
|
753
342
|
guard let fileName = entry.file_name,
|
|
754
|
-
|
|
343
|
+
var fileHash = entry.file_hash,
|
|
755
344
|
let downloadUrl = entry.download_url else {
|
|
756
345
|
continue
|
|
757
346
|
}
|
|
347
|
+
|
|
348
|
+
if (!self.hasOldPrivateKeyPropertyInConfig && !self.publicKey.isEmpty && !sessionKey.isEmpty) {
|
|
349
|
+
do {
|
|
350
|
+
fileHash = try CryptoCipherV2.decryptChecksum(checksum: fileHash, publicKey: self.publicKey, version: version)
|
|
351
|
+
} catch {
|
|
352
|
+
downloadError = error
|
|
353
|
+
print("\(CapacitorUpdater.TAG) CryptoCipherV2.decryptChecksum error \(id) \(fileName) error: \(error)")
|
|
354
|
+
}
|
|
355
|
+
}
|
|
758
356
|
|
|
759
357
|
let fileNameWithoutPath = (fileName as NSString).lastPathComponent
|
|
760
358
|
let cacheFileName = "\(fileHash)_\(fileNameWithoutPath)"
|
|
@@ -769,13 +367,13 @@ extension CustomError: LocalizedError {
|
|
|
769
367
|
|
|
770
368
|
if FileManager.default.fileExists(atPath: builtinFilePath.path) && verifyChecksum(file: builtinFilePath, expectedHash: fileHash) {
|
|
771
369
|
try FileManager.default.copyItem(at: builtinFilePath, to: destFilePath)
|
|
772
|
-
print("\(
|
|
370
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(fileName) using builtin file \(id)")
|
|
773
371
|
completedFiles += 1
|
|
774
372
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
775
373
|
dispatchGroup.leave()
|
|
776
374
|
} else if FileManager.default.fileExists(atPath: cacheFilePath.path) && verifyChecksum(file: cacheFilePath, expectedHash: fileHash) {
|
|
777
375
|
try FileManager.default.copyItem(at: cacheFilePath, to: destFilePath)
|
|
778
|
-
print("\(
|
|
376
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(fileName) copy from cache \(id)")
|
|
779
377
|
completedFiles += 1
|
|
780
378
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
781
379
|
dispatchGroup.leave()
|
|
@@ -787,25 +385,51 @@ extension CustomError: LocalizedError {
|
|
|
787
385
|
switch response.result {
|
|
788
386
|
case .success(let data):
|
|
789
387
|
do {
|
|
388
|
+
// Add decryption step if public key is set and sessionKey is provided
|
|
389
|
+
var finalData = data
|
|
390
|
+
if !self.publicKey.isEmpty && !sessionKey.isEmpty {
|
|
391
|
+
let tempFile = self.cacheFolder.appendingPathComponent("temp_\(UUID().uuidString)")
|
|
392
|
+
try finalData.write(to: tempFile)
|
|
393
|
+
do {
|
|
394
|
+
try CryptoCipherV2.decryptFile(filePath: tempFile, publicKey: self.publicKey, sessionKey: sessionKey, version: version)
|
|
395
|
+
} catch {
|
|
396
|
+
self.sendStats(action: "decrypt_fail", versionName: version)
|
|
397
|
+
throw error
|
|
398
|
+
}
|
|
399
|
+
// TODO: try and do self.sendStats(action: "decrypt_fail", versionName: version) if fail
|
|
400
|
+
finalData = try Data(contentsOf: tempFile)
|
|
401
|
+
try FileManager.default.removeItem(at: tempFile)
|
|
402
|
+
}
|
|
403
|
+
|
|
790
404
|
// Decompress the Brotli data
|
|
791
|
-
guard let decompressedData = self.decompressBrotli(data:
|
|
405
|
+
guard let decompressedData = self.decompressBrotli(data: finalData) else {
|
|
792
406
|
throw NSError(domain: "BrotliDecompressionError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to decompress Brotli data"])
|
|
793
407
|
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
408
|
+
finalData = decompressedData
|
|
409
|
+
|
|
410
|
+
try finalData.write(to: destFilePath)
|
|
411
|
+
if (!self.hasOldPrivateKeyPropertyInConfig && !self.publicKey.isEmpty && !sessionKey.isEmpty) {
|
|
412
|
+
// assume that calcChecksum != null
|
|
413
|
+
let calculatedChecksum = CryptoCipherV2.calcChecksum(filePath: destFilePath)
|
|
414
|
+
if (calculatedChecksum != fileHash) {
|
|
415
|
+
throw NSError(domain: "ChecksumError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Computed checksum is not equal to required checksum (\(calculatedChecksum) != \(fileHash))"])
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Save decrypted data to cache and destination
|
|
420
|
+
try finalData.write(to: cacheFilePath)
|
|
421
|
+
|
|
798
422
|
|
|
799
423
|
completedFiles += 1
|
|
800
424
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
801
|
-
print("\(
|
|
425
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) downloaded, decompressed, decrypted, and cached")
|
|
802
426
|
} catch {
|
|
803
427
|
downloadError = error
|
|
804
|
-
print("\(
|
|
428
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) error: \(error)")
|
|
805
429
|
}
|
|
806
430
|
case .failure(let error):
|
|
807
431
|
downloadError = error
|
|
808
|
-
print("\(
|
|
432
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) download error: \(error)")
|
|
809
433
|
}
|
|
810
434
|
}
|
|
811
435
|
}
|
|
@@ -824,7 +448,7 @@ extension CustomError: LocalizedError {
|
|
|
824
448
|
let updatedBundle = bundleInfo.setStatus(status: BundleStatus.PENDING.localizedString)
|
|
825
449
|
self.saveBundleInfo(id: id, bundle: updatedBundle)
|
|
826
450
|
|
|
827
|
-
print("\(
|
|
451
|
+
print("\(CapacitorUpdater.TAG) downloadManifest done \(id)")
|
|
828
452
|
return updatedBundle
|
|
829
453
|
}
|
|
830
454
|
|
|
@@ -836,7 +460,7 @@ extension CustomError: LocalizedError {
|
|
|
836
460
|
let streamPointer = UnsafeMutablePointer<compression_stream>.allocate(capacity: 1)
|
|
837
461
|
var status = compression_stream_init(streamPointer, COMPRESSION_STREAM_DECODE, COMPRESSION_BROTLI)
|
|
838
462
|
guard status != COMPRESSION_STATUS_ERROR else {
|
|
839
|
-
print("\(
|
|
463
|
+
print("\(CapacitorUpdater.TAG) Unable to initialize the decompression stream.")
|
|
840
464
|
return nil
|
|
841
465
|
}
|
|
842
466
|
|
|
@@ -858,7 +482,7 @@ extension CustomError: LocalizedError {
|
|
|
858
482
|
if let baseAddress = rawBufferPointer.baseAddress {
|
|
859
483
|
streamPointer.pointee.src_ptr = baseAddress.assumingMemoryBound(to: UInt8.self)
|
|
860
484
|
} else {
|
|
861
|
-
print("\(
|
|
485
|
+
print("\(CapacitorUpdater.TAG) Error: Unable to get base address of input data")
|
|
862
486
|
status = COMPRESSION_STATUS_ERROR
|
|
863
487
|
return
|
|
864
488
|
}
|
|
@@ -879,7 +503,7 @@ extension CustomError: LocalizedError {
|
|
|
879
503
|
if status == COMPRESSION_STATUS_END {
|
|
880
504
|
break
|
|
881
505
|
} else if status == COMPRESSION_STATUS_ERROR {
|
|
882
|
-
print("\(
|
|
506
|
+
print("\(CapacitorUpdater.TAG) Error during Brotli decompression")
|
|
883
507
|
return nil
|
|
884
508
|
}
|
|
885
509
|
|
|
@@ -917,7 +541,7 @@ extension CustomError: LocalizedError {
|
|
|
917
541
|
let monitor = ClosureEventMonitor()
|
|
918
542
|
monitor.requestDidCompleteTaskWithError = { (_, _, error) in
|
|
919
543
|
if error != nil {
|
|
920
|
-
print("\(
|
|
544
|
+
print("\(CapacitorUpdater.TAG) Downloading failed - ClosureEventMonitor activated")
|
|
921
545
|
mainError = error as NSError?
|
|
922
546
|
}
|
|
923
547
|
}
|
|
@@ -948,11 +572,11 @@ extension CustomError: LocalizedError {
|
|
|
948
572
|
}
|
|
949
573
|
|
|
950
574
|
} else {
|
|
951
|
-
print("\(
|
|
575
|
+
print("\(CapacitorUpdater.TAG) Download failed")
|
|
952
576
|
}
|
|
953
577
|
|
|
954
578
|
case .complete:
|
|
955
|
-
print("\(
|
|
579
|
+
print("\(CapacitorUpdater.TAG) Download complete, total received bytes: \(totalReceivedBytes)")
|
|
956
580
|
self.notifyDownload(id: id, percent: 70, ignoreMultipleOfTen: true)
|
|
957
581
|
semaphore.signal()
|
|
958
582
|
}
|
|
@@ -974,7 +598,7 @@ extension CustomError: LocalizedError {
|
|
|
974
598
|
reachabilityManager?.stopListening()
|
|
975
599
|
|
|
976
600
|
if mainError != nil {
|
|
977
|
-
print("\(
|
|
601
|
+
print("\(CapacitorUpdater.TAG) Failed to download: \(String(describing: mainError))")
|
|
978
602
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum))
|
|
979
603
|
throw mainError!
|
|
980
604
|
}
|
|
@@ -982,14 +606,19 @@ extension CustomError: LocalizedError {
|
|
|
982
606
|
let finalPath = tempDataPath.deletingLastPathComponent().appendingPathComponent("\(id)")
|
|
983
607
|
do {
|
|
984
608
|
var checksumDecrypted = checksum
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
609
|
+
do {
|
|
610
|
+
if !self.hasOldPrivateKeyPropertyInConfig {
|
|
611
|
+
try CryptoCipherV2.decryptFile(filePath: tempDataPath, publicKey: self.publicKey, sessionKey: sessionKey, version: version)
|
|
612
|
+
} else {
|
|
613
|
+
try CryptoCipher.decryptFile(filePath: tempDataPath, privateKey: self.privateKey, sessionKey: sessionKey, version: version)
|
|
614
|
+
}
|
|
615
|
+
} catch {
|
|
616
|
+
self.sendStats(action: "decrypt_fail", versionName: version)
|
|
617
|
+
throw error
|
|
989
618
|
}
|
|
990
619
|
try FileManager.default.moveItem(at: tempDataPath, to: finalPath)
|
|
991
620
|
} catch {
|
|
992
|
-
print("\(
|
|
621
|
+
print("\(CapacitorUpdater.TAG) Failed decrypt file : \(error)")
|
|
993
622
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum))
|
|
994
623
|
cleanDownloadData()
|
|
995
624
|
throw error
|
|
@@ -997,15 +626,15 @@ extension CustomError: LocalizedError {
|
|
|
997
626
|
|
|
998
627
|
do {
|
|
999
628
|
if !self.hasOldPrivateKeyPropertyInConfig && !sessionKey.isEmpty {
|
|
1000
|
-
checksum =
|
|
629
|
+
checksum = CryptoCipherV2.calcChecksum(filePath: finalPath)
|
|
1001
630
|
} else {
|
|
1002
|
-
checksum =
|
|
631
|
+
checksum = CryptoCipher.calcChecksum(filePath: finalPath)
|
|
1003
632
|
}
|
|
1004
|
-
print("\(
|
|
633
|
+
print("\(CapacitorUpdater.TAG) Downloading: 80% (unzipping)")
|
|
1005
634
|
try self.saveDownloaded(sourceZip: finalPath, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory), notify: true)
|
|
1006
635
|
|
|
1007
636
|
} catch {
|
|
1008
|
-
print("\(
|
|
637
|
+
print("\(CapacitorUpdater.TAG) Failed to unzip file: \(error)")
|
|
1009
638
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum))
|
|
1010
639
|
cleanDownloadData()
|
|
1011
640
|
// todo: cleanup zip attempts
|
|
@@ -1013,25 +642,25 @@ extension CustomError: LocalizedError {
|
|
|
1013
642
|
}
|
|
1014
643
|
|
|
1015
644
|
self.notifyDownload(id: id, percent: 90)
|
|
1016
|
-
print("\(
|
|
645
|
+
print("\(CapacitorUpdater.TAG) Downloading: 90% (wrapping up)")
|
|
1017
646
|
let info = BundleInfo(id: id, version: version, status: BundleStatus.PENDING, downloaded: Date(), checksum: checksum)
|
|
1018
647
|
self.saveBundleInfo(id: id, bundle: info)
|
|
1019
648
|
self.cleanDownloadData()
|
|
1020
649
|
self.notifyDownload(id: id, percent: 100)
|
|
1021
|
-
print("\(
|
|
650
|
+
print("\(CapacitorUpdater.TAG) Downloading: 100% (complete)")
|
|
1022
651
|
return info
|
|
1023
652
|
}
|
|
1024
653
|
private func ensureResumableFilesExist() {
|
|
1025
654
|
let fileManager = FileManager.default
|
|
1026
655
|
if !fileManager.fileExists(atPath: tempDataPath.path) {
|
|
1027
656
|
if !fileManager.createFile(atPath: tempDataPath.path, contents: Data()) {
|
|
1028
|
-
print("\(
|
|
657
|
+
print("\(CapacitorUpdater.TAG) Cannot ensure that a file at \(tempDataPath.path) exists")
|
|
1029
658
|
}
|
|
1030
659
|
}
|
|
1031
660
|
|
|
1032
661
|
if !fileManager.fileExists(atPath: updateInfo.path) {
|
|
1033
662
|
if !fileManager.createFile(atPath: updateInfo.path, contents: Data()) {
|
|
1034
|
-
print("\(
|
|
663
|
+
print("\(CapacitorUpdater.TAG) Cannot ensure that a file at \(updateInfo.path) exists")
|
|
1035
664
|
}
|
|
1036
665
|
}
|
|
1037
666
|
}
|
|
@@ -1043,7 +672,7 @@ extension CustomError: LocalizedError {
|
|
|
1043
672
|
do {
|
|
1044
673
|
try fileManager.removeItem(at: tempDataPath)
|
|
1045
674
|
} catch {
|
|
1046
|
-
print("\(
|
|
675
|
+
print("\(CapacitorUpdater.TAG) Could not delete file at \(tempDataPath): \(error)")
|
|
1047
676
|
}
|
|
1048
677
|
}
|
|
1049
678
|
// Deleting update.dat
|
|
@@ -1051,7 +680,7 @@ extension CustomError: LocalizedError {
|
|
|
1051
680
|
do {
|
|
1052
681
|
try fileManager.removeItem(at: updateInfo)
|
|
1053
682
|
} catch {
|
|
1054
|
-
print("\(
|
|
683
|
+
print("\(CapacitorUpdater.TAG) Could not delete file at \(updateInfo): \(error)")
|
|
1055
684
|
}
|
|
1056
685
|
}
|
|
1057
686
|
}
|
|
@@ -1079,7 +708,7 @@ extension CustomError: LocalizedError {
|
|
|
1079
708
|
do {
|
|
1080
709
|
try "\(version)".write(to: updateInfo, atomically: true, encoding: .utf8)
|
|
1081
710
|
} catch {
|
|
1082
|
-
print("\(
|
|
711
|
+
print("\(CapacitorUpdater.TAG) Failed to save progress: \(error)")
|
|
1083
712
|
}
|
|
1084
713
|
}
|
|
1085
714
|
private func getLocalUpdateVersion() -> String { // Return the version that was tried to be downloaded on last download attempt
|
|
@@ -1101,7 +730,7 @@ extension CustomError: LocalizedError {
|
|
|
1101
730
|
return fileSize.int64Value
|
|
1102
731
|
}
|
|
1103
732
|
} catch {
|
|
1104
|
-
print("\(
|
|
733
|
+
print("\(CapacitorUpdater.TAG) Could not retrieve already downloaded data size : \(error)")
|
|
1105
734
|
}
|
|
1106
735
|
return 0
|
|
1107
736
|
}
|
|
@@ -1111,7 +740,7 @@ extension CustomError: LocalizedError {
|
|
|
1111
740
|
do {
|
|
1112
741
|
let files: [String] = try FileManager.default.contentsOfDirectory(atPath: dest.path)
|
|
1113
742
|
var res: [BundleInfo] = []
|
|
1114
|
-
print("\(
|
|
743
|
+
print("\(CapacitorUpdater.TAG) list File : \(dest.path)")
|
|
1115
744
|
if dest.exist {
|
|
1116
745
|
for id: String in files {
|
|
1117
746
|
res.append(self.getBundleInfo(id: id))
|
|
@@ -1119,7 +748,7 @@ extension CustomError: LocalizedError {
|
|
|
1119
748
|
}
|
|
1120
749
|
return res
|
|
1121
750
|
} catch {
|
|
1122
|
-
print("\(
|
|
751
|
+
print("\(CapacitorUpdater.TAG) No version available \(dest.path)")
|
|
1123
752
|
return []
|
|
1124
753
|
}
|
|
1125
754
|
}
|
|
@@ -1127,7 +756,7 @@ extension CustomError: LocalizedError {
|
|
|
1127
756
|
public func delete(id: String, removeInfo: Bool) -> Bool {
|
|
1128
757
|
let deleted: BundleInfo = self.getBundleInfo(id: id)
|
|
1129
758
|
if deleted.isBuiltin() || self.getCurrentBundleId() == id {
|
|
1130
|
-
print("\(
|
|
759
|
+
print("\(CapacitorUpdater.TAG) Cannot delete \(id)")
|
|
1131
760
|
return false
|
|
1132
761
|
}
|
|
1133
762
|
|
|
@@ -1136,7 +765,7 @@ extension CustomError: LocalizedError {
|
|
|
1136
765
|
!next.isDeleted() &&
|
|
1137
766
|
!next.isErrorStatus() &&
|
|
1138
767
|
next.getId() == id {
|
|
1139
|
-
print("\(
|
|
768
|
+
print("\(CapacitorUpdater.TAG) Cannot delete the next bundle \(id)")
|
|
1140
769
|
return false
|
|
1141
770
|
}
|
|
1142
771
|
|
|
@@ -1144,7 +773,7 @@ extension CustomError: LocalizedError {
|
|
|
1144
773
|
do {
|
|
1145
774
|
try FileManager.default.removeItem(atPath: destPersist.path)
|
|
1146
775
|
} catch {
|
|
1147
|
-
print("\(
|
|
776
|
+
print("\(CapacitorUpdater.TAG) Folder \(destPersist.path), not removed.")
|
|
1148
777
|
return false
|
|
1149
778
|
}
|
|
1150
779
|
if removeInfo {
|
|
@@ -1152,7 +781,7 @@ extension CustomError: LocalizedError {
|
|
|
1152
781
|
} else {
|
|
1153
782
|
self.saveBundleInfo(id: id, bundle: deleted.setStatus(status: BundleStatus.DELETED.localizedString))
|
|
1154
783
|
}
|
|
1155
|
-
print("\(
|
|
784
|
+
print("\(CapacitorUpdater.TAG) bundle delete \(deleted.getVersionName())")
|
|
1156
785
|
self.sendStats(action: "delete", versionName: deleted.getVersionName())
|
|
1157
786
|
return true
|
|
1158
787
|
}
|
|
@@ -1205,7 +834,7 @@ extension CustomError: LocalizedError {
|
|
|
1205
834
|
public func autoReset() {
|
|
1206
835
|
let currentBundle: BundleInfo = self.getCurrentBundle()
|
|
1207
836
|
if !currentBundle.isBuiltin() && !self.bundleExists(id: currentBundle.getId()) {
|
|
1208
|
-
print("\(
|
|
837
|
+
print("\(CapacitorUpdater.TAG) Folder at bundle path does not exist. Triggering reset.")
|
|
1209
838
|
self.reset()
|
|
1210
839
|
}
|
|
1211
840
|
}
|
|
@@ -1215,7 +844,7 @@ extension CustomError: LocalizedError {
|
|
|
1215
844
|
}
|
|
1216
845
|
|
|
1217
846
|
public func reset(isInternal: Bool) {
|
|
1218
|
-
print("\(
|
|
847
|
+
print("\(CapacitorUpdater.TAG) reset: \(isInternal)")
|
|
1219
848
|
let currentBundleName = self.getCurrentBundle().getVersionName()
|
|
1220
849
|
self.setCurrentBundle(bundle: "")
|
|
1221
850
|
self.setFallbackBundle(fallback: Optional<BundleInfo>.none)
|
|
@@ -1228,14 +857,14 @@ extension CustomError: LocalizedError {
|
|
|
1228
857
|
public func setSuccess(bundle: BundleInfo, autoDeletePrevious: Bool) {
|
|
1229
858
|
self.setBundleStatus(id: bundle.getId(), status: BundleStatus.SUCCESS)
|
|
1230
859
|
let fallback: BundleInfo = self.getFallbackBundle()
|
|
1231
|
-
print("\(
|
|
1232
|
-
print("\(
|
|
860
|
+
print("\(CapacitorUpdater.TAG) Fallback bundle is: \(fallback.toString())")
|
|
861
|
+
print("\(CapacitorUpdater.TAG) Version successfully loaded: \(bundle.toString())")
|
|
1233
862
|
if autoDeletePrevious && !fallback.isBuiltin() && fallback.getId() != bundle.getId() {
|
|
1234
863
|
let res = self.delete(id: fallback.getId())
|
|
1235
864
|
if res {
|
|
1236
|
-
print("\(
|
|
865
|
+
print("\(CapacitorUpdater.TAG) Deleted previous bundle: \(fallback.toString())")
|
|
1237
866
|
} else {
|
|
1238
|
-
print("\(
|
|
867
|
+
print("\(CapacitorUpdater.TAG) Failed to delete previous bundle: \(fallback.toString())")
|
|
1239
868
|
}
|
|
1240
869
|
}
|
|
1241
870
|
self.setFallbackBundle(fallback: bundle)
|
|
@@ -1248,7 +877,7 @@ extension CustomError: LocalizedError {
|
|
|
1248
877
|
func unsetChannel() -> SetChannel {
|
|
1249
878
|
let setChannel: SetChannel = SetChannel()
|
|
1250
879
|
if (self.channelUrl ).isEmpty {
|
|
1251
|
-
print("\(
|
|
880
|
+
print("\(CapacitorUpdater.TAG) Channel URL is not set")
|
|
1252
881
|
setChannel.message = "Channel URL is not set"
|
|
1253
882
|
setChannel.error = "missing_config"
|
|
1254
883
|
return setChannel
|
|
@@ -1271,7 +900,7 @@ extension CustomError: LocalizedError {
|
|
|
1271
900
|
setChannel.message = message
|
|
1272
901
|
}
|
|
1273
902
|
case let .failure(error):
|
|
1274
|
-
print("\(
|
|
903
|
+
print("\(CapacitorUpdater.TAG) Error unset Channel", response.value ?? "", error)
|
|
1275
904
|
setChannel.message = "Error unset Channel \(String(describing: response.value))"
|
|
1276
905
|
setChannel.error = "response_error"
|
|
1277
906
|
}
|
|
@@ -1284,7 +913,7 @@ extension CustomError: LocalizedError {
|
|
|
1284
913
|
func setChannel(channel: String) -> SetChannel {
|
|
1285
914
|
let setChannel: SetChannel = SetChannel()
|
|
1286
915
|
if (self.channelUrl ).isEmpty {
|
|
1287
|
-
print("\(
|
|
916
|
+
print("\(CapacitorUpdater.TAG) Channel URL is not set")
|
|
1288
917
|
setChannel.message = "Channel URL is not set"
|
|
1289
918
|
setChannel.error = "missing_config"
|
|
1290
919
|
return setChannel
|
|
@@ -1308,7 +937,7 @@ extension CustomError: LocalizedError {
|
|
|
1308
937
|
setChannel.message = message
|
|
1309
938
|
}
|
|
1310
939
|
case let .failure(error):
|
|
1311
|
-
print("\(
|
|
940
|
+
print("\(CapacitorUpdater.TAG) Error set Channel", response.value ?? "", error)
|
|
1312
941
|
setChannel.message = "Error set Channel \(String(describing: response.value))"
|
|
1313
942
|
setChannel.error = "response_error"
|
|
1314
943
|
}
|
|
@@ -1321,7 +950,7 @@ extension CustomError: LocalizedError {
|
|
|
1321
950
|
func getChannel() -> GetChannel {
|
|
1322
951
|
let getChannel: GetChannel = GetChannel()
|
|
1323
952
|
if (self.channelUrl ).isEmpty {
|
|
1324
|
-
print("\(
|
|
953
|
+
print("\(CapacitorUpdater.TAG) Channel URL is not set")
|
|
1325
954
|
getChannel.message = "Channel URL is not set"
|
|
1326
955
|
getChannel.error = "missing_config"
|
|
1327
956
|
return getChannel
|
|
@@ -1360,7 +989,7 @@ extension CustomError: LocalizedError {
|
|
|
1360
989
|
}
|
|
1361
990
|
}
|
|
1362
991
|
|
|
1363
|
-
print("\(
|
|
992
|
+
print("\(CapacitorUpdater.TAG) Error get Channel", response.value ?? "", error)
|
|
1364
993
|
getChannel.message = "Error get Channel \(String(describing: response.value)))"
|
|
1365
994
|
getChannel.error = "response_error"
|
|
1366
995
|
}
|
|
@@ -1395,9 +1024,9 @@ extension CustomError: LocalizedError {
|
|
|
1395
1024
|
).responseData { response in
|
|
1396
1025
|
switch response.result {
|
|
1397
1026
|
case .success:
|
|
1398
|
-
print("\(
|
|
1027
|
+
print("\(CapacitorUpdater.TAG) Stats sent for \(action), version \(versionName)")
|
|
1399
1028
|
case let .failure(error):
|
|
1400
|
-
print("\(
|
|
1029
|
+
print("\(CapacitorUpdater.TAG) Error sending stats: ", response.value ?? "", error.localizedDescription)
|
|
1401
1030
|
}
|
|
1402
1031
|
semaphore.signal()
|
|
1403
1032
|
}
|
|
@@ -1412,7 +1041,7 @@ extension CustomError: LocalizedError {
|
|
|
1412
1041
|
if id != nil {
|
|
1413
1042
|
trueId = id!
|
|
1414
1043
|
}
|
|
1415
|
-
// print("\(
|
|
1044
|
+
// print("\(CapacitorUpdater.TAG) Getting info for bundle [\(trueId)]")
|
|
1416
1045
|
let result: BundleInfo
|
|
1417
1046
|
if BundleInfo.ID_BUILTIN == trueId {
|
|
1418
1047
|
result = BundleInfo(id: trueId, version: "", status: BundleStatus.SUCCESS, checksum: "")
|
|
@@ -1422,11 +1051,11 @@ extension CustomError: LocalizedError {
|
|
|
1422
1051
|
do {
|
|
1423
1052
|
result = try UserDefaults.standard.getObj(forKey: "\(trueId)\(self.INFO_SUFFIX)", castTo: BundleInfo.self)
|
|
1424
1053
|
} catch {
|
|
1425
|
-
print("\(
|
|
1054
|
+
print("\(CapacitorUpdater.TAG) Failed to parse info for bundle [\(trueId)]", error.localizedDescription)
|
|
1426
1055
|
result = BundleInfo(id: trueId, version: "", status: BundleStatus.PENDING, checksum: "")
|
|
1427
1056
|
}
|
|
1428
1057
|
}
|
|
1429
|
-
// print("\(
|
|
1058
|
+
// print("\(CapacitorUpdater.TAG) Returning info bundle [\(result.toString())]")
|
|
1430
1059
|
return result
|
|
1431
1060
|
}
|
|
1432
1061
|
|
|
@@ -1446,26 +1075,26 @@ extension CustomError: LocalizedError {
|
|
|
1446
1075
|
|
|
1447
1076
|
public func saveBundleInfo(id: String, bundle: BundleInfo?) {
|
|
1448
1077
|
if bundle != nil && (bundle!.isBuiltin() || bundle!.isUnknown()) {
|
|
1449
|
-
print("\(
|
|
1078
|
+
print("\(CapacitorUpdater.TAG) Not saving info for bundle [\(id)]", bundle?.toString() ?? "")
|
|
1450
1079
|
return
|
|
1451
1080
|
}
|
|
1452
1081
|
if bundle == nil {
|
|
1453
|
-
print("\(
|
|
1082
|
+
print("\(CapacitorUpdater.TAG) Removing info for bundle [\(id)]")
|
|
1454
1083
|
UserDefaults.standard.removeObject(forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
1455
1084
|
} else {
|
|
1456
1085
|
let update = bundle!.setId(id: id)
|
|
1457
|
-
print("\(
|
|
1086
|
+
print("\(CapacitorUpdater.TAG) Storing info for bundle [\(id)]", update.toString())
|
|
1458
1087
|
do {
|
|
1459
1088
|
try UserDefaults.standard.setObj(update, forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
1460
1089
|
} catch {
|
|
1461
|
-
print("\(
|
|
1090
|
+
print("\(CapacitorUpdater.TAG) Failed to save info for bundle [\(id)]", error.localizedDescription)
|
|
1462
1091
|
}
|
|
1463
1092
|
}
|
|
1464
1093
|
UserDefaults.standard.synchronize()
|
|
1465
1094
|
}
|
|
1466
1095
|
|
|
1467
1096
|
private func setBundleStatus(id: String, status: BundleStatus) {
|
|
1468
|
-
print("\(
|
|
1097
|
+
print("\(CapacitorUpdater.TAG) Setting status for bundle [\(id)] to \(status)")
|
|
1469
1098
|
let info = self.getBundleInfo(id: id)
|
|
1470
1099
|
self.saveBundleInfo(id: id, bundle: info.setStatus(status: status.localizedString))
|
|
1471
1100
|
}
|