@capgo/capacitor-updater 6.11.2 → 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/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 +114 -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 +1 -1
|
@@ -11,8 +11,6 @@ import ZipArchive
|
|
|
11
11
|
import SSZipArchive
|
|
12
12
|
#endif
|
|
13
13
|
import Alamofire
|
|
14
|
-
import zlib
|
|
15
|
-
import CryptoKit
|
|
16
14
|
import Compression
|
|
17
15
|
|
|
18
16
|
#if canImport(ZipArchive)
|
|
@@ -21,257 +19,6 @@ typealias ZipArchiveHelper = ZipArchive
|
|
|
21
19
|
typealias ZipArchiveHelper = SSZipArchive
|
|
22
20
|
#endif
|
|
23
21
|
|
|
24
|
-
extension Collection {
|
|
25
|
-
subscript(safe index: Index) -> Element? {
|
|
26
|
-
return indices.contains(index) ? self[index] : nil
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
extension URL {
|
|
30
|
-
var isDirectory: Bool {
|
|
31
|
-
(try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true
|
|
32
|
-
}
|
|
33
|
-
var exist: Bool {
|
|
34
|
-
return FileManager().fileExists(atPath: self.path)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
struct SetChannelDec: Decodable {
|
|
38
|
-
let status: String?
|
|
39
|
-
let error: String?
|
|
40
|
-
let message: String?
|
|
41
|
-
}
|
|
42
|
-
public class SetChannel: NSObject {
|
|
43
|
-
var status: String = ""
|
|
44
|
-
var error: String = ""
|
|
45
|
-
var message: String = ""
|
|
46
|
-
}
|
|
47
|
-
extension SetChannel {
|
|
48
|
-
func toDict() -> [String: Any] {
|
|
49
|
-
var dict: [String: Any] = [String: Any]()
|
|
50
|
-
let otherSelf: Mirror = Mirror(reflecting: self)
|
|
51
|
-
for child: Mirror.Child in otherSelf.children {
|
|
52
|
-
if let key: String = child.label {
|
|
53
|
-
dict[key] = child.value
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return dict
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
struct GetChannelDec: Decodable {
|
|
60
|
-
let channel: String?
|
|
61
|
-
let status: String?
|
|
62
|
-
let error: String?
|
|
63
|
-
let message: String?
|
|
64
|
-
let allowSet: Bool?
|
|
65
|
-
}
|
|
66
|
-
public class GetChannel: NSObject {
|
|
67
|
-
var channel: String = ""
|
|
68
|
-
var status: String = ""
|
|
69
|
-
var error: String = ""
|
|
70
|
-
var message: String = ""
|
|
71
|
-
var allowSet: Bool = true
|
|
72
|
-
}
|
|
73
|
-
extension GetChannel {
|
|
74
|
-
func toDict() -> [String: Any] {
|
|
75
|
-
var dict: [String: Any] = [String: Any]()
|
|
76
|
-
let otherSelf: Mirror = Mirror(reflecting: self)
|
|
77
|
-
for child: Mirror.Child in otherSelf.children {
|
|
78
|
-
if let key: String = child.label {
|
|
79
|
-
dict[key] = child.value
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return dict
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
struct InfoObject: Codable {
|
|
86
|
-
let platform: String?
|
|
87
|
-
let device_id: String?
|
|
88
|
-
let app_id: String?
|
|
89
|
-
let custom_id: String?
|
|
90
|
-
let version_build: String?
|
|
91
|
-
let version_code: String?
|
|
92
|
-
let version_os: String?
|
|
93
|
-
var version_name: String?
|
|
94
|
-
var old_version_name: String?
|
|
95
|
-
let plugin_version: String?
|
|
96
|
-
let is_emulator: Bool?
|
|
97
|
-
let is_prod: Bool?
|
|
98
|
-
var action: String?
|
|
99
|
-
var channel: String?
|
|
100
|
-
var defaultChannel: String?
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
public struct ManifestEntry: Codable {
|
|
104
|
-
let file_name: String?
|
|
105
|
-
let file_hash: String?
|
|
106
|
-
let download_url: String?
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
extension ManifestEntry {
|
|
110
|
-
func toDict() -> [String: Any] {
|
|
111
|
-
var dict: [String: Any] = [String: Any]()
|
|
112
|
-
let mirror = Mirror(reflecting: self)
|
|
113
|
-
for child in mirror.children {
|
|
114
|
-
if let key = child.label {
|
|
115
|
-
dict[key] = child.value
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
return dict
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
struct AppVersionDec: Decodable {
|
|
123
|
-
let version: String?
|
|
124
|
-
let checksum: String?
|
|
125
|
-
let url: String?
|
|
126
|
-
let message: String?
|
|
127
|
-
let error: String?
|
|
128
|
-
let session_key: String?
|
|
129
|
-
let major: Bool?
|
|
130
|
-
let data: [String: String]?
|
|
131
|
-
let manifest: [ManifestEntry]?
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public class AppVersion: NSObject {
|
|
135
|
-
var version: String = ""
|
|
136
|
-
var checksum: String = ""
|
|
137
|
-
var url: String = ""
|
|
138
|
-
var message: String?
|
|
139
|
-
var error: String?
|
|
140
|
-
var sessionKey: String?
|
|
141
|
-
var major: Bool?
|
|
142
|
-
var data: [String: String]?
|
|
143
|
-
var manifest: [ManifestEntry]?
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
extension AppVersion {
|
|
147
|
-
func toDict() -> [String: Any] {
|
|
148
|
-
var dict: [String: Any] = [String: Any]()
|
|
149
|
-
let otherSelf: Mirror = Mirror(reflecting: self)
|
|
150
|
-
for child: Mirror.Child in otherSelf.children {
|
|
151
|
-
if let key: String = child.label {
|
|
152
|
-
if key == "manifest", let manifestEntries = child.value as? [ManifestEntry] {
|
|
153
|
-
dict[key] = manifestEntries.map { $0.toDict() }
|
|
154
|
-
} else {
|
|
155
|
-
dict[key] = child.value
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return dict
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
extension OperatingSystemVersion {
|
|
164
|
-
func getFullVersion(separator: String = ".") -> String {
|
|
165
|
-
return "\(majorVersion)\(separator)\(minorVersion)\(separator)\(patchVersion)"
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
extension Bundle {
|
|
169
|
-
var versionName: String? {
|
|
170
|
-
return infoDictionary?["CFBundleShortVersionString"] as? String
|
|
171
|
-
}
|
|
172
|
-
var versionCode: String? {
|
|
173
|
-
return infoDictionary?["CFBundleVersion"] as? String
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
extension ISO8601DateFormatter {
|
|
178
|
-
convenience init(_ formatOptions: Options) {
|
|
179
|
-
self.init()
|
|
180
|
-
self.formatOptions = formatOptions
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
extension Formatter {
|
|
184
|
-
static let iso8601withFractionalSeconds: ISO8601DateFormatter = ISO8601DateFormatter([.withInternetDateTime, .withFractionalSeconds])
|
|
185
|
-
}
|
|
186
|
-
extension Date {
|
|
187
|
-
var iso8601withFractionalSeconds: String { return Formatter.iso8601withFractionalSeconds.string(from: self) }
|
|
188
|
-
}
|
|
189
|
-
extension String {
|
|
190
|
-
|
|
191
|
-
var fileURL: URL {
|
|
192
|
-
return URL(fileURLWithPath: self)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
var lastPathComponent: String {
|
|
196
|
-
get {
|
|
197
|
-
return fileURL.lastPathComponent
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
var iso8601withFractionalSeconds: Date? {
|
|
201
|
-
return Formatter.iso8601withFractionalSeconds.date(from: self)
|
|
202
|
-
}
|
|
203
|
-
func trim(using characterSet: CharacterSet = .whitespacesAndNewlines) -> String {
|
|
204
|
-
return trimmingCharacters(in: characterSet)
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
enum CustomError: Error {
|
|
209
|
-
// Throw when an unzip fail
|
|
210
|
-
case cannotUnzip
|
|
211
|
-
case cannotWrite
|
|
212
|
-
case cannotDecode
|
|
213
|
-
case cannotUnflat
|
|
214
|
-
case cannotCreateDirectory
|
|
215
|
-
case cannotDeleteDirectory
|
|
216
|
-
case cannotDecryptSessionKey
|
|
217
|
-
case invalidBase64
|
|
218
|
-
|
|
219
|
-
// Throw in all other cases
|
|
220
|
-
case unexpected(code: Int)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
extension CustomError: LocalizedError {
|
|
224
|
-
public var errorDescription: String? {
|
|
225
|
-
switch self {
|
|
226
|
-
case .cannotUnzip:
|
|
227
|
-
return NSLocalizedString(
|
|
228
|
-
"The file cannot be unzip",
|
|
229
|
-
comment: "Invalid zip"
|
|
230
|
-
)
|
|
231
|
-
case .cannotCreateDirectory:
|
|
232
|
-
return NSLocalizedString(
|
|
233
|
-
"The folder cannot be created",
|
|
234
|
-
comment: "Invalid folder"
|
|
235
|
-
)
|
|
236
|
-
case .cannotDeleteDirectory:
|
|
237
|
-
return NSLocalizedString(
|
|
238
|
-
"The folder cannot be deleted",
|
|
239
|
-
comment: "Invalid folder"
|
|
240
|
-
)
|
|
241
|
-
case .cannotUnflat:
|
|
242
|
-
return NSLocalizedString(
|
|
243
|
-
"The file cannot be unflat",
|
|
244
|
-
comment: "Invalid folder"
|
|
245
|
-
)
|
|
246
|
-
case .unexpected:
|
|
247
|
-
return NSLocalizedString(
|
|
248
|
-
"An unexpected error occurred.",
|
|
249
|
-
comment: "Unexpected Error"
|
|
250
|
-
)
|
|
251
|
-
case .cannotDecode:
|
|
252
|
-
return NSLocalizedString(
|
|
253
|
-
"Decoding the zip failed with this key",
|
|
254
|
-
comment: "Invalid public key"
|
|
255
|
-
)
|
|
256
|
-
case .cannotWrite:
|
|
257
|
-
return NSLocalizedString(
|
|
258
|
-
"Cannot write to the destination",
|
|
259
|
-
comment: "Invalid destination"
|
|
260
|
-
)
|
|
261
|
-
case .cannotDecryptSessionKey:
|
|
262
|
-
return NSLocalizedString(
|
|
263
|
-
"Decrypting the session key failed",
|
|
264
|
-
comment: "Invalid session key"
|
|
265
|
-
)
|
|
266
|
-
case .invalidBase64:
|
|
267
|
-
return NSLocalizedString(
|
|
268
|
-
"Decrypting the base64 failed",
|
|
269
|
-
comment: "Invalid checksum key"
|
|
270
|
-
)
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
22
|
@objc public class CapacitorUpdater: NSObject {
|
|
276
23
|
|
|
277
24
|
private let versionCode: String = Bundle.main.versionCode ?? ""
|
|
@@ -287,7 +34,7 @@ extension CustomError: LocalizedError {
|
|
|
287
34
|
// Add this line to declare cacheFolder
|
|
288
35
|
private let cacheFolder: URL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("capgo_downloads")
|
|
289
36
|
|
|
290
|
-
public let TAG: String = "✨ Capacitor-updater:"
|
|
37
|
+
static public let TAG: String = "✨ Capacitor-updater:"
|
|
291
38
|
public let CAP_SERVER_PATH: String = "serverBasePath"
|
|
292
39
|
public var versionBuild: String = ""
|
|
293
40
|
public var customId: String = ""
|
|
@@ -368,7 +115,7 @@ extension CustomError: LocalizedError {
|
|
|
368
115
|
do {
|
|
369
116
|
try FileManager.default.createDirectory(atPath: source.path, withIntermediateDirectories: true, attributes: nil)
|
|
370
117
|
} catch {
|
|
371
|
-
print("\(
|
|
118
|
+
print("\(CapacitorUpdater.TAG) Cannot createDirectory \(source.path)")
|
|
372
119
|
throw CustomError.cannotCreateDirectory
|
|
373
120
|
}
|
|
374
121
|
}
|
|
@@ -378,7 +125,7 @@ extension CustomError: LocalizedError {
|
|
|
378
125
|
do {
|
|
379
126
|
try FileManager.default.removeItem(atPath: source.path)
|
|
380
127
|
} catch {
|
|
381
|
-
print("\(
|
|
128
|
+
print("\(CapacitorUpdater.TAG) File not removed. \(source.path)")
|
|
382
129
|
throw CustomError.cannotDeleteDirectory
|
|
383
130
|
}
|
|
384
131
|
}
|
|
@@ -395,105 +142,14 @@ extension CustomError: LocalizedError {
|
|
|
395
142
|
return false
|
|
396
143
|
}
|
|
397
144
|
} catch {
|
|
398
|
-
print("\(
|
|
145
|
+
print("\(CapacitorUpdater.TAG) File not moved. source: \(source.path) dest: \(dest.path)")
|
|
399
146
|
throw CustomError.cannotUnflat
|
|
400
147
|
}
|
|
401
148
|
}
|
|
402
149
|
|
|
403
|
-
private func decryptFileV2(filePath: URL, sessionKey: String, version: String) throws {
|
|
404
|
-
if self.publicKey.isEmpty || sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
|
|
405
|
-
print("\(self.TAG) Cannot find public key or sessionKey")
|
|
406
|
-
return
|
|
407
|
-
}
|
|
408
|
-
do {
|
|
409
|
-
guard let rsaPublicKey: RSAPublicKey = .load(rsaPublicKey: self.publicKey) else {
|
|
410
|
-
print("cannot decode publicKey", self.publicKey)
|
|
411
|
-
throw CustomError.cannotDecode
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
let sessionKeyArray: [String] = sessionKey.components(separatedBy: ":")
|
|
415
|
-
guard let ivData: Data = Data(base64Encoded: sessionKeyArray[0]) else {
|
|
416
|
-
print("cannot decode sessionKey", sessionKey)
|
|
417
|
-
throw CustomError.cannotDecode
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
guard let sessionKeyDataEncrypted = Data(base64Encoded: sessionKeyArray[1]) else {
|
|
421
|
-
throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
guard let sessionKeyDataDecrypted = rsaPublicKey.decrypt(data: sessionKeyDataEncrypted) else {
|
|
425
|
-
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted)
|
|
429
|
-
|
|
430
|
-
guard let encryptedData = try? Data(contentsOf: filePath) else {
|
|
431
|
-
throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
|
|
435
|
-
throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
try decryptedData.write(to: filePath)
|
|
439
|
-
|
|
440
|
-
} catch {
|
|
441
|
-
print("\(self.TAG) Cannot decode: \(filePath.path)", error)
|
|
442
|
-
self.sendStats(action: "decrypt_fail", versionName: version)
|
|
443
|
-
throw CustomError.cannotDecode
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
private func decryptFile(filePath: URL, sessionKey: String, version: String) throws {
|
|
448
|
-
if self.privateKey.isEmpty {
|
|
449
|
-
print("\(self.TAG) Cannot found privateKey")
|
|
450
|
-
return
|
|
451
|
-
} else if sessionKey.isEmpty || sessionKey.components(separatedBy: ":").count != 2 {
|
|
452
|
-
print("\(self.TAG) Cannot found sessionKey")
|
|
453
|
-
return
|
|
454
|
-
}
|
|
455
|
-
do {
|
|
456
|
-
guard let rsaPrivateKey: RSAPrivateKey = .load(rsaPrivateKey: self.privateKey) else {
|
|
457
|
-
print("cannot decode privateKey", self.privateKey)
|
|
458
|
-
throw CustomError.cannotDecode
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
let sessionKeyArray: [String] = sessionKey.components(separatedBy: ":")
|
|
462
|
-
guard let ivData: Data = Data(base64Encoded: sessionKeyArray[0]) else {
|
|
463
|
-
print("cannot decode sessionKey", sessionKey)
|
|
464
|
-
throw CustomError.cannotDecode
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
guard let sessionKeyDataEncrypted = Data(base64Encoded: sessionKeyArray[1]) else {
|
|
468
|
-
throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
guard let sessionKeyDataDecrypted = rsaPrivateKey.decrypt(data: sessionKeyDataEncrypted) else {
|
|
472
|
-
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
let aesPrivateKey = AES128Key(iv: ivData, aes128Key: sessionKeyDataDecrypted)
|
|
476
|
-
|
|
477
|
-
guard let encryptedData = try? Data(contentsOf: filePath) else {
|
|
478
|
-
throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
guard let decryptedData = aesPrivateKey.decrypt(data: encryptedData) else {
|
|
482
|
-
throw NSError(domain: "Failed to decrypt data", code: 4, userInfo: nil)
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
try decryptedData.write(to: filePath)
|
|
486
|
-
|
|
487
|
-
} catch {
|
|
488
|
-
print("\(self.TAG) Cannot decode: \(filePath.path)", error)
|
|
489
|
-
self.sendStats(action: "decrypt_fail", versionName: version)
|
|
490
|
-
throw CustomError.cannotDecode
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
|
|
494
150
|
private func unzipProgressHandler(entry: String, zipInfo: unz_file_info, entryNumber: Int, total: Int, destUnZip: URL, id: String, unzipError: inout NSError?) {
|
|
495
151
|
if entry.contains("\\") {
|
|
496
|
-
print("\(
|
|
152
|
+
print("\(CapacitorUpdater.TAG) unzip: Windows path is not supported, please use unix path as required by zip RFC: \(entry)")
|
|
497
153
|
self.sendStats(action: "windows_path_fail")
|
|
498
154
|
}
|
|
499
155
|
|
|
@@ -596,7 +252,7 @@ extension CustomError: LocalizedError {
|
|
|
596
252
|
if let channel = channel {
|
|
597
253
|
parameters.defaultChannel = channel
|
|
598
254
|
}
|
|
599
|
-
print("\(
|
|
255
|
+
print("\(CapacitorUpdater.TAG) Auto-update parameters: \(parameters)")
|
|
600
256
|
let request = AF.request(url, method: .post, parameters: parameters, encoder: JSONParameterEncoder.default, requestModifier: { $0.timeoutInterval = self.timeout })
|
|
601
257
|
|
|
602
258
|
request.validate().responseDecodable(of: AppVersionDec.self) { response in
|
|
@@ -630,7 +286,7 @@ extension CustomError: LocalizedError {
|
|
|
630
286
|
latest.manifest = manifest
|
|
631
287
|
}
|
|
632
288
|
case let .failure(error):
|
|
633
|
-
print("\(
|
|
289
|
+
print("\(CapacitorUpdater.TAG) Error getting Latest", response.value ?? "", error )
|
|
634
290
|
latest.message = "Error getting Latest \(String(describing: response.value))"
|
|
635
291
|
latest.error = "response_error"
|
|
636
292
|
}
|
|
@@ -643,63 +299,7 @@ extension CustomError: LocalizedError {
|
|
|
643
299
|
private func setCurrentBundle(bundle: String) {
|
|
644
300
|
UserDefaults.standard.set(bundle, forKey: self.CAP_SERVER_PATH)
|
|
645
301
|
UserDefaults.standard.synchronize()
|
|
646
|
-
print("\(
|
|
647
|
-
}
|
|
648
|
-
private func calcChecksum(filePath: URL) -> String {
|
|
649
|
-
let bufferSize = 1024 * 1024 * 5 // 5 MB
|
|
650
|
-
var checksum = uLong(0)
|
|
651
|
-
|
|
652
|
-
do {
|
|
653
|
-
let fileHandle = try FileHandle(forReadingFrom: filePath)
|
|
654
|
-
defer {
|
|
655
|
-
fileHandle.closeFile()
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
while autoreleasepool(invoking: {
|
|
659
|
-
let fileData = fileHandle.readData(ofLength: bufferSize)
|
|
660
|
-
if fileData.count > 0 {
|
|
661
|
-
checksum = fileData.withUnsafeBytes {
|
|
662
|
-
crc32(checksum, $0.bindMemory(to: Bytef.self).baseAddress, uInt(fileData.count))
|
|
663
|
-
}
|
|
664
|
-
return true // Continue
|
|
665
|
-
} else {
|
|
666
|
-
return false // End of file
|
|
667
|
-
}
|
|
668
|
-
}) {}
|
|
669
|
-
|
|
670
|
-
return String(format: "%08X", checksum).lowercased()
|
|
671
|
-
} catch {
|
|
672
|
-
print("\(self.TAG) Cannot get checksum: \(filePath.path)", error)
|
|
673
|
-
return ""
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
private func calcChecksumV2(filePath: URL) -> String {
|
|
678
|
-
let bufferSize = 1024 * 1024 * 5 // 5 MB
|
|
679
|
-
var sha256 = SHA256()
|
|
680
|
-
|
|
681
|
-
do {
|
|
682
|
-
let fileHandle = try FileHandle(forReadingFrom: filePath)
|
|
683
|
-
defer {
|
|
684
|
-
fileHandle.closeFile()
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
while autoreleasepool(invoking: {
|
|
688
|
-
let fileData = fileHandle.readData(ofLength: bufferSize)
|
|
689
|
-
if fileData.count > 0 {
|
|
690
|
-
sha256.update(data: fileData)
|
|
691
|
-
return true // Continue
|
|
692
|
-
} else {
|
|
693
|
-
return false // End of file
|
|
694
|
-
}
|
|
695
|
-
}) {}
|
|
696
|
-
|
|
697
|
-
let digest = sha256.finalize()
|
|
698
|
-
return digest.compactMap { String(format: "%02x", $0) }.joined()
|
|
699
|
-
} catch {
|
|
700
|
-
print("\(self.TAG) Cannot get checksum: \(filePath.path)", error)
|
|
701
|
-
return ""
|
|
702
|
-
}
|
|
302
|
+
print("\(CapacitorUpdater.TAG) Current bundle set to: \((bundle ).isEmpty ? BundleInfo.ID_BUILTIN : bundle)")
|
|
703
303
|
}
|
|
704
304
|
|
|
705
305
|
private var tempDataPath: URL {
|
|
@@ -711,35 +311,14 @@ extension CustomError: LocalizedError {
|
|
|
711
311
|
}
|
|
712
312
|
private var tempData = Data()
|
|
713
313
|
|
|
714
|
-
public func decryptChecksum(checksum: String, version: String) throws -> String {
|
|
715
|
-
if self.publicKey.isEmpty {
|
|
716
|
-
return checksum
|
|
717
|
-
}
|
|
718
|
-
do {
|
|
719
|
-
let checksumBytes: Data = Data(base64Encoded: checksum)!
|
|
720
|
-
guard let rsaPublicKey: RSAPublicKey = .load(rsaPublicKey: self.publicKey) else {
|
|
721
|
-
print("cannot decode publicKey", self.publicKey)
|
|
722
|
-
throw CustomError.cannotDecode
|
|
723
|
-
}
|
|
724
|
-
guard let decryptedChecksum = try? rsaPublicKey.decrypt(data: checksumBytes) else {
|
|
725
|
-
throw NSError(domain: "Failed to decrypt session key data", code: 2, userInfo: nil)
|
|
726
|
-
}
|
|
727
|
-
return decryptedChecksum.base64EncodedString()
|
|
728
|
-
} catch {
|
|
729
|
-
print("\(self.TAG) Cannot decrypt checksum: \(checksum)", error)
|
|
730
|
-
self.sendStats(action: "decrypt_fail", versionName: version)
|
|
731
|
-
throw CustomError.cannotDecode
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
314
|
private func verifyChecksum(file: URL, expectedHash: String) -> Bool {
|
|
736
|
-
let actualHash =
|
|
315
|
+
let actualHash = CryptoCipherV2.calcChecksum(filePath: file)
|
|
737
316
|
return actualHash == expectedHash
|
|
738
317
|
}
|
|
739
318
|
|
|
740
319
|
public func downloadManifest(manifest: [ManifestEntry], version: String, sessionKey: String) throws -> BundleInfo {
|
|
741
320
|
let id = self.randomString(length: 10)
|
|
742
|
-
print("\(
|
|
321
|
+
print("\(CapacitorUpdater.TAG) downloadManifest start \(id)")
|
|
743
322
|
let destFolder = self.getBundleDirectory(id: id)
|
|
744
323
|
let builtinFolder = Bundle.main.bundleURL.appendingPathComponent("public")
|
|
745
324
|
|
|
@@ -761,10 +340,19 @@ extension CustomError: LocalizedError {
|
|
|
761
340
|
|
|
762
341
|
for entry in manifest {
|
|
763
342
|
guard let fileName = entry.file_name,
|
|
764
|
-
|
|
343
|
+
var fileHash = entry.file_hash,
|
|
765
344
|
let downloadUrl = entry.download_url else {
|
|
766
345
|
continue
|
|
767
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
|
+
}
|
|
768
356
|
|
|
769
357
|
let fileNameWithoutPath = (fileName as NSString).lastPathComponent
|
|
770
358
|
let cacheFileName = "\(fileHash)_\(fileNameWithoutPath)"
|
|
@@ -779,13 +367,13 @@ extension CustomError: LocalizedError {
|
|
|
779
367
|
|
|
780
368
|
if FileManager.default.fileExists(atPath: builtinFilePath.path) && verifyChecksum(file: builtinFilePath, expectedHash: fileHash) {
|
|
781
369
|
try FileManager.default.copyItem(at: builtinFilePath, to: destFilePath)
|
|
782
|
-
print("\(
|
|
370
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(fileName) using builtin file \(id)")
|
|
783
371
|
completedFiles += 1
|
|
784
372
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
785
373
|
dispatchGroup.leave()
|
|
786
374
|
} else if FileManager.default.fileExists(atPath: cacheFilePath.path) && verifyChecksum(file: cacheFilePath, expectedHash: fileHash) {
|
|
787
375
|
try FileManager.default.copyItem(at: cacheFilePath, to: destFilePath)
|
|
788
|
-
print("\(
|
|
376
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(fileName) copy from cache \(id)")
|
|
789
377
|
completedFiles += 1
|
|
790
378
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
791
379
|
dispatchGroup.leave()
|
|
@@ -797,25 +385,51 @@ extension CustomError: LocalizedError {
|
|
|
797
385
|
switch response.result {
|
|
798
386
|
case .success(let data):
|
|
799
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
|
+
|
|
800
404
|
// Decompress the Brotli data
|
|
801
|
-
guard let decompressedData = self.decompressBrotli(data:
|
|
405
|
+
guard let decompressedData = self.decompressBrotli(data: finalData) else {
|
|
802
406
|
throw NSError(domain: "BrotliDecompressionError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to decompress Brotli data"])
|
|
803
407
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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
|
+
|
|
808
422
|
|
|
809
423
|
completedFiles += 1
|
|
810
424
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
811
|
-
print("\(
|
|
425
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) downloaded, decompressed, decrypted, and cached")
|
|
812
426
|
} catch {
|
|
813
427
|
downloadError = error
|
|
814
|
-
print("\(
|
|
428
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) error: \(error)")
|
|
815
429
|
}
|
|
816
430
|
case .failure(let error):
|
|
817
431
|
downloadError = error
|
|
818
|
-
print("\(
|
|
432
|
+
print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) download error: \(error)")
|
|
819
433
|
}
|
|
820
434
|
}
|
|
821
435
|
}
|
|
@@ -834,7 +448,7 @@ extension CustomError: LocalizedError {
|
|
|
834
448
|
let updatedBundle = bundleInfo.setStatus(status: BundleStatus.PENDING.localizedString)
|
|
835
449
|
self.saveBundleInfo(id: id, bundle: updatedBundle)
|
|
836
450
|
|
|
837
|
-
print("\(
|
|
451
|
+
print("\(CapacitorUpdater.TAG) downloadManifest done \(id)")
|
|
838
452
|
return updatedBundle
|
|
839
453
|
}
|
|
840
454
|
|
|
@@ -846,7 +460,7 @@ extension CustomError: LocalizedError {
|
|
|
846
460
|
let streamPointer = UnsafeMutablePointer<compression_stream>.allocate(capacity: 1)
|
|
847
461
|
var status = compression_stream_init(streamPointer, COMPRESSION_STREAM_DECODE, COMPRESSION_BROTLI)
|
|
848
462
|
guard status != COMPRESSION_STATUS_ERROR else {
|
|
849
|
-
print("\(
|
|
463
|
+
print("\(CapacitorUpdater.TAG) Unable to initialize the decompression stream.")
|
|
850
464
|
return nil
|
|
851
465
|
}
|
|
852
466
|
|
|
@@ -868,7 +482,7 @@ extension CustomError: LocalizedError {
|
|
|
868
482
|
if let baseAddress = rawBufferPointer.baseAddress {
|
|
869
483
|
streamPointer.pointee.src_ptr = baseAddress.assumingMemoryBound(to: UInt8.self)
|
|
870
484
|
} else {
|
|
871
|
-
print("\(
|
|
485
|
+
print("\(CapacitorUpdater.TAG) Error: Unable to get base address of input data")
|
|
872
486
|
status = COMPRESSION_STATUS_ERROR
|
|
873
487
|
return
|
|
874
488
|
}
|
|
@@ -889,7 +503,7 @@ extension CustomError: LocalizedError {
|
|
|
889
503
|
if status == COMPRESSION_STATUS_END {
|
|
890
504
|
break
|
|
891
505
|
} else if status == COMPRESSION_STATUS_ERROR {
|
|
892
|
-
print("\(
|
|
506
|
+
print("\(CapacitorUpdater.TAG) Error during Brotli decompression")
|
|
893
507
|
return nil
|
|
894
508
|
}
|
|
895
509
|
|
|
@@ -927,7 +541,7 @@ extension CustomError: LocalizedError {
|
|
|
927
541
|
let monitor = ClosureEventMonitor()
|
|
928
542
|
monitor.requestDidCompleteTaskWithError = { (_, _, error) in
|
|
929
543
|
if error != nil {
|
|
930
|
-
print("\(
|
|
544
|
+
print("\(CapacitorUpdater.TAG) Downloading failed - ClosureEventMonitor activated")
|
|
931
545
|
mainError = error as NSError?
|
|
932
546
|
}
|
|
933
547
|
}
|
|
@@ -958,11 +572,11 @@ extension CustomError: LocalizedError {
|
|
|
958
572
|
}
|
|
959
573
|
|
|
960
574
|
} else {
|
|
961
|
-
print("\(
|
|
575
|
+
print("\(CapacitorUpdater.TAG) Download failed")
|
|
962
576
|
}
|
|
963
577
|
|
|
964
578
|
case .complete:
|
|
965
|
-
print("\(
|
|
579
|
+
print("\(CapacitorUpdater.TAG) Download complete, total received bytes: \(totalReceivedBytes)")
|
|
966
580
|
self.notifyDownload(id: id, percent: 70, ignoreMultipleOfTen: true)
|
|
967
581
|
semaphore.signal()
|
|
968
582
|
}
|
|
@@ -984,7 +598,7 @@ extension CustomError: LocalizedError {
|
|
|
984
598
|
reachabilityManager?.stopListening()
|
|
985
599
|
|
|
986
600
|
if mainError != nil {
|
|
987
|
-
print("\(
|
|
601
|
+
print("\(CapacitorUpdater.TAG) Failed to download: \(String(describing: mainError))")
|
|
988
602
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum))
|
|
989
603
|
throw mainError!
|
|
990
604
|
}
|
|
@@ -992,14 +606,19 @@ extension CustomError: LocalizedError {
|
|
|
992
606
|
let finalPath = tempDataPath.deletingLastPathComponent().appendingPathComponent("\(id)")
|
|
993
607
|
do {
|
|
994
608
|
var checksumDecrypted = checksum
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
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
|
|
999
618
|
}
|
|
1000
619
|
try FileManager.default.moveItem(at: tempDataPath, to: finalPath)
|
|
1001
620
|
} catch {
|
|
1002
|
-
print("\(
|
|
621
|
+
print("\(CapacitorUpdater.TAG) Failed decrypt file : \(error)")
|
|
1003
622
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum))
|
|
1004
623
|
cleanDownloadData()
|
|
1005
624
|
throw error
|
|
@@ -1007,15 +626,15 @@ extension CustomError: LocalizedError {
|
|
|
1007
626
|
|
|
1008
627
|
do {
|
|
1009
628
|
if !self.hasOldPrivateKeyPropertyInConfig && !sessionKey.isEmpty {
|
|
1010
|
-
checksum =
|
|
629
|
+
checksum = CryptoCipherV2.calcChecksum(filePath: finalPath)
|
|
1011
630
|
} else {
|
|
1012
|
-
checksum =
|
|
631
|
+
checksum = CryptoCipher.calcChecksum(filePath: finalPath)
|
|
1013
632
|
}
|
|
1014
|
-
print("\(
|
|
633
|
+
print("\(CapacitorUpdater.TAG) Downloading: 80% (unzipping)")
|
|
1015
634
|
try self.saveDownloaded(sourceZip: finalPath, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory), notify: true)
|
|
1016
635
|
|
|
1017
636
|
} catch {
|
|
1018
|
-
print("\(
|
|
637
|
+
print("\(CapacitorUpdater.TAG) Failed to unzip file: \(error)")
|
|
1019
638
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum))
|
|
1020
639
|
cleanDownloadData()
|
|
1021
640
|
// todo: cleanup zip attempts
|
|
@@ -1023,25 +642,25 @@ extension CustomError: LocalizedError {
|
|
|
1023
642
|
}
|
|
1024
643
|
|
|
1025
644
|
self.notifyDownload(id: id, percent: 90)
|
|
1026
|
-
print("\(
|
|
645
|
+
print("\(CapacitorUpdater.TAG) Downloading: 90% (wrapping up)")
|
|
1027
646
|
let info = BundleInfo(id: id, version: version, status: BundleStatus.PENDING, downloaded: Date(), checksum: checksum)
|
|
1028
647
|
self.saveBundleInfo(id: id, bundle: info)
|
|
1029
648
|
self.cleanDownloadData()
|
|
1030
649
|
self.notifyDownload(id: id, percent: 100)
|
|
1031
|
-
print("\(
|
|
650
|
+
print("\(CapacitorUpdater.TAG) Downloading: 100% (complete)")
|
|
1032
651
|
return info
|
|
1033
652
|
}
|
|
1034
653
|
private func ensureResumableFilesExist() {
|
|
1035
654
|
let fileManager = FileManager.default
|
|
1036
655
|
if !fileManager.fileExists(atPath: tempDataPath.path) {
|
|
1037
656
|
if !fileManager.createFile(atPath: tempDataPath.path, contents: Data()) {
|
|
1038
|
-
print("\(
|
|
657
|
+
print("\(CapacitorUpdater.TAG) Cannot ensure that a file at \(tempDataPath.path) exists")
|
|
1039
658
|
}
|
|
1040
659
|
}
|
|
1041
660
|
|
|
1042
661
|
if !fileManager.fileExists(atPath: updateInfo.path) {
|
|
1043
662
|
if !fileManager.createFile(atPath: updateInfo.path, contents: Data()) {
|
|
1044
|
-
print("\(
|
|
663
|
+
print("\(CapacitorUpdater.TAG) Cannot ensure that a file at \(updateInfo.path) exists")
|
|
1045
664
|
}
|
|
1046
665
|
}
|
|
1047
666
|
}
|
|
@@ -1053,7 +672,7 @@ extension CustomError: LocalizedError {
|
|
|
1053
672
|
do {
|
|
1054
673
|
try fileManager.removeItem(at: tempDataPath)
|
|
1055
674
|
} catch {
|
|
1056
|
-
print("\(
|
|
675
|
+
print("\(CapacitorUpdater.TAG) Could not delete file at \(tempDataPath): \(error)")
|
|
1057
676
|
}
|
|
1058
677
|
}
|
|
1059
678
|
// Deleting update.dat
|
|
@@ -1061,7 +680,7 @@ extension CustomError: LocalizedError {
|
|
|
1061
680
|
do {
|
|
1062
681
|
try fileManager.removeItem(at: updateInfo)
|
|
1063
682
|
} catch {
|
|
1064
|
-
print("\(
|
|
683
|
+
print("\(CapacitorUpdater.TAG) Could not delete file at \(updateInfo): \(error)")
|
|
1065
684
|
}
|
|
1066
685
|
}
|
|
1067
686
|
}
|
|
@@ -1089,7 +708,7 @@ extension CustomError: LocalizedError {
|
|
|
1089
708
|
do {
|
|
1090
709
|
try "\(version)".write(to: updateInfo, atomically: true, encoding: .utf8)
|
|
1091
710
|
} catch {
|
|
1092
|
-
print("\(
|
|
711
|
+
print("\(CapacitorUpdater.TAG) Failed to save progress: \(error)")
|
|
1093
712
|
}
|
|
1094
713
|
}
|
|
1095
714
|
private func getLocalUpdateVersion() -> String { // Return the version that was tried to be downloaded on last download attempt
|
|
@@ -1111,7 +730,7 @@ extension CustomError: LocalizedError {
|
|
|
1111
730
|
return fileSize.int64Value
|
|
1112
731
|
}
|
|
1113
732
|
} catch {
|
|
1114
|
-
print("\(
|
|
733
|
+
print("\(CapacitorUpdater.TAG) Could not retrieve already downloaded data size : \(error)")
|
|
1115
734
|
}
|
|
1116
735
|
return 0
|
|
1117
736
|
}
|
|
@@ -1121,7 +740,7 @@ extension CustomError: LocalizedError {
|
|
|
1121
740
|
do {
|
|
1122
741
|
let files: [String] = try FileManager.default.contentsOfDirectory(atPath: dest.path)
|
|
1123
742
|
var res: [BundleInfo] = []
|
|
1124
|
-
print("\(
|
|
743
|
+
print("\(CapacitorUpdater.TAG) list File : \(dest.path)")
|
|
1125
744
|
if dest.exist {
|
|
1126
745
|
for id: String in files {
|
|
1127
746
|
res.append(self.getBundleInfo(id: id))
|
|
@@ -1129,7 +748,7 @@ extension CustomError: LocalizedError {
|
|
|
1129
748
|
}
|
|
1130
749
|
return res
|
|
1131
750
|
} catch {
|
|
1132
|
-
print("\(
|
|
751
|
+
print("\(CapacitorUpdater.TAG) No version available \(dest.path)")
|
|
1133
752
|
return []
|
|
1134
753
|
}
|
|
1135
754
|
}
|
|
@@ -1137,7 +756,7 @@ extension CustomError: LocalizedError {
|
|
|
1137
756
|
public func delete(id: String, removeInfo: Bool) -> Bool {
|
|
1138
757
|
let deleted: BundleInfo = self.getBundleInfo(id: id)
|
|
1139
758
|
if deleted.isBuiltin() || self.getCurrentBundleId() == id {
|
|
1140
|
-
print("\(
|
|
759
|
+
print("\(CapacitorUpdater.TAG) Cannot delete \(id)")
|
|
1141
760
|
return false
|
|
1142
761
|
}
|
|
1143
762
|
|
|
@@ -1146,7 +765,7 @@ extension CustomError: LocalizedError {
|
|
|
1146
765
|
!next.isDeleted() &&
|
|
1147
766
|
!next.isErrorStatus() &&
|
|
1148
767
|
next.getId() == id {
|
|
1149
|
-
print("\(
|
|
768
|
+
print("\(CapacitorUpdater.TAG) Cannot delete the next bundle \(id)")
|
|
1150
769
|
return false
|
|
1151
770
|
}
|
|
1152
771
|
|
|
@@ -1154,7 +773,7 @@ extension CustomError: LocalizedError {
|
|
|
1154
773
|
do {
|
|
1155
774
|
try FileManager.default.removeItem(atPath: destPersist.path)
|
|
1156
775
|
} catch {
|
|
1157
|
-
print("\(
|
|
776
|
+
print("\(CapacitorUpdater.TAG) Folder \(destPersist.path), not removed.")
|
|
1158
777
|
return false
|
|
1159
778
|
}
|
|
1160
779
|
if removeInfo {
|
|
@@ -1162,7 +781,7 @@ extension CustomError: LocalizedError {
|
|
|
1162
781
|
} else {
|
|
1163
782
|
self.saveBundleInfo(id: id, bundle: deleted.setStatus(status: BundleStatus.DELETED.localizedString))
|
|
1164
783
|
}
|
|
1165
|
-
print("\(
|
|
784
|
+
print("\(CapacitorUpdater.TAG) bundle delete \(deleted.getVersionName())")
|
|
1166
785
|
self.sendStats(action: "delete", versionName: deleted.getVersionName())
|
|
1167
786
|
return true
|
|
1168
787
|
}
|
|
@@ -1215,7 +834,7 @@ extension CustomError: LocalizedError {
|
|
|
1215
834
|
public func autoReset() {
|
|
1216
835
|
let currentBundle: BundleInfo = self.getCurrentBundle()
|
|
1217
836
|
if !currentBundle.isBuiltin() && !self.bundleExists(id: currentBundle.getId()) {
|
|
1218
|
-
print("\(
|
|
837
|
+
print("\(CapacitorUpdater.TAG) Folder at bundle path does not exist. Triggering reset.")
|
|
1219
838
|
self.reset()
|
|
1220
839
|
}
|
|
1221
840
|
}
|
|
@@ -1225,7 +844,7 @@ extension CustomError: LocalizedError {
|
|
|
1225
844
|
}
|
|
1226
845
|
|
|
1227
846
|
public func reset(isInternal: Bool) {
|
|
1228
|
-
print("\(
|
|
847
|
+
print("\(CapacitorUpdater.TAG) reset: \(isInternal)")
|
|
1229
848
|
let currentBundleName = self.getCurrentBundle().getVersionName()
|
|
1230
849
|
self.setCurrentBundle(bundle: "")
|
|
1231
850
|
self.setFallbackBundle(fallback: Optional<BundleInfo>.none)
|
|
@@ -1238,14 +857,14 @@ extension CustomError: LocalizedError {
|
|
|
1238
857
|
public func setSuccess(bundle: BundleInfo, autoDeletePrevious: Bool) {
|
|
1239
858
|
self.setBundleStatus(id: bundle.getId(), status: BundleStatus.SUCCESS)
|
|
1240
859
|
let fallback: BundleInfo = self.getFallbackBundle()
|
|
1241
|
-
print("\(
|
|
1242
|
-
print("\(
|
|
860
|
+
print("\(CapacitorUpdater.TAG) Fallback bundle is: \(fallback.toString())")
|
|
861
|
+
print("\(CapacitorUpdater.TAG) Version successfully loaded: \(bundle.toString())")
|
|
1243
862
|
if autoDeletePrevious && !fallback.isBuiltin() && fallback.getId() != bundle.getId() {
|
|
1244
863
|
let res = self.delete(id: fallback.getId())
|
|
1245
864
|
if res {
|
|
1246
|
-
print("\(
|
|
865
|
+
print("\(CapacitorUpdater.TAG) Deleted previous bundle: \(fallback.toString())")
|
|
1247
866
|
} else {
|
|
1248
|
-
print("\(
|
|
867
|
+
print("\(CapacitorUpdater.TAG) Failed to delete previous bundle: \(fallback.toString())")
|
|
1249
868
|
}
|
|
1250
869
|
}
|
|
1251
870
|
self.setFallbackBundle(fallback: bundle)
|
|
@@ -1258,7 +877,7 @@ extension CustomError: LocalizedError {
|
|
|
1258
877
|
func unsetChannel() -> SetChannel {
|
|
1259
878
|
let setChannel: SetChannel = SetChannel()
|
|
1260
879
|
if (self.channelUrl ).isEmpty {
|
|
1261
|
-
print("\(
|
|
880
|
+
print("\(CapacitorUpdater.TAG) Channel URL is not set")
|
|
1262
881
|
setChannel.message = "Channel URL is not set"
|
|
1263
882
|
setChannel.error = "missing_config"
|
|
1264
883
|
return setChannel
|
|
@@ -1281,7 +900,7 @@ extension CustomError: LocalizedError {
|
|
|
1281
900
|
setChannel.message = message
|
|
1282
901
|
}
|
|
1283
902
|
case let .failure(error):
|
|
1284
|
-
print("\(
|
|
903
|
+
print("\(CapacitorUpdater.TAG) Error unset Channel", response.value ?? "", error)
|
|
1285
904
|
setChannel.message = "Error unset Channel \(String(describing: response.value))"
|
|
1286
905
|
setChannel.error = "response_error"
|
|
1287
906
|
}
|
|
@@ -1294,7 +913,7 @@ extension CustomError: LocalizedError {
|
|
|
1294
913
|
func setChannel(channel: String) -> SetChannel {
|
|
1295
914
|
let setChannel: SetChannel = SetChannel()
|
|
1296
915
|
if (self.channelUrl ).isEmpty {
|
|
1297
|
-
print("\(
|
|
916
|
+
print("\(CapacitorUpdater.TAG) Channel URL is not set")
|
|
1298
917
|
setChannel.message = "Channel URL is not set"
|
|
1299
918
|
setChannel.error = "missing_config"
|
|
1300
919
|
return setChannel
|
|
@@ -1318,7 +937,7 @@ extension CustomError: LocalizedError {
|
|
|
1318
937
|
setChannel.message = message
|
|
1319
938
|
}
|
|
1320
939
|
case let .failure(error):
|
|
1321
|
-
print("\(
|
|
940
|
+
print("\(CapacitorUpdater.TAG) Error set Channel", response.value ?? "", error)
|
|
1322
941
|
setChannel.message = "Error set Channel \(String(describing: response.value))"
|
|
1323
942
|
setChannel.error = "response_error"
|
|
1324
943
|
}
|
|
@@ -1331,7 +950,7 @@ extension CustomError: LocalizedError {
|
|
|
1331
950
|
func getChannel() -> GetChannel {
|
|
1332
951
|
let getChannel: GetChannel = GetChannel()
|
|
1333
952
|
if (self.channelUrl ).isEmpty {
|
|
1334
|
-
print("\(
|
|
953
|
+
print("\(CapacitorUpdater.TAG) Channel URL is not set")
|
|
1335
954
|
getChannel.message = "Channel URL is not set"
|
|
1336
955
|
getChannel.error = "missing_config"
|
|
1337
956
|
return getChannel
|
|
@@ -1370,7 +989,7 @@ extension CustomError: LocalizedError {
|
|
|
1370
989
|
}
|
|
1371
990
|
}
|
|
1372
991
|
|
|
1373
|
-
print("\(
|
|
992
|
+
print("\(CapacitorUpdater.TAG) Error get Channel", response.value ?? "", error)
|
|
1374
993
|
getChannel.message = "Error get Channel \(String(describing: response.value)))"
|
|
1375
994
|
getChannel.error = "response_error"
|
|
1376
995
|
}
|
|
@@ -1405,9 +1024,9 @@ extension CustomError: LocalizedError {
|
|
|
1405
1024
|
).responseData { response in
|
|
1406
1025
|
switch response.result {
|
|
1407
1026
|
case .success:
|
|
1408
|
-
print("\(
|
|
1027
|
+
print("\(CapacitorUpdater.TAG) Stats sent for \(action), version \(versionName)")
|
|
1409
1028
|
case let .failure(error):
|
|
1410
|
-
print("\(
|
|
1029
|
+
print("\(CapacitorUpdater.TAG) Error sending stats: ", response.value ?? "", error.localizedDescription)
|
|
1411
1030
|
}
|
|
1412
1031
|
semaphore.signal()
|
|
1413
1032
|
}
|
|
@@ -1422,7 +1041,7 @@ extension CustomError: LocalizedError {
|
|
|
1422
1041
|
if id != nil {
|
|
1423
1042
|
trueId = id!
|
|
1424
1043
|
}
|
|
1425
|
-
// print("\(
|
|
1044
|
+
// print("\(CapacitorUpdater.TAG) Getting info for bundle [\(trueId)]")
|
|
1426
1045
|
let result: BundleInfo
|
|
1427
1046
|
if BundleInfo.ID_BUILTIN == trueId {
|
|
1428
1047
|
result = BundleInfo(id: trueId, version: "", status: BundleStatus.SUCCESS, checksum: "")
|
|
@@ -1432,11 +1051,11 @@ extension CustomError: LocalizedError {
|
|
|
1432
1051
|
do {
|
|
1433
1052
|
result = try UserDefaults.standard.getObj(forKey: "\(trueId)\(self.INFO_SUFFIX)", castTo: BundleInfo.self)
|
|
1434
1053
|
} catch {
|
|
1435
|
-
print("\(
|
|
1054
|
+
print("\(CapacitorUpdater.TAG) Failed to parse info for bundle [\(trueId)]", error.localizedDescription)
|
|
1436
1055
|
result = BundleInfo(id: trueId, version: "", status: BundleStatus.PENDING, checksum: "")
|
|
1437
1056
|
}
|
|
1438
1057
|
}
|
|
1439
|
-
// print("\(
|
|
1058
|
+
// print("\(CapacitorUpdater.TAG) Returning info bundle [\(result.toString())]")
|
|
1440
1059
|
return result
|
|
1441
1060
|
}
|
|
1442
1061
|
|
|
@@ -1456,26 +1075,26 @@ extension CustomError: LocalizedError {
|
|
|
1456
1075
|
|
|
1457
1076
|
public func saveBundleInfo(id: String, bundle: BundleInfo?) {
|
|
1458
1077
|
if bundle != nil && (bundle!.isBuiltin() || bundle!.isUnknown()) {
|
|
1459
|
-
print("\(
|
|
1078
|
+
print("\(CapacitorUpdater.TAG) Not saving info for bundle [\(id)]", bundle?.toString() ?? "")
|
|
1460
1079
|
return
|
|
1461
1080
|
}
|
|
1462
1081
|
if bundle == nil {
|
|
1463
|
-
print("\(
|
|
1082
|
+
print("\(CapacitorUpdater.TAG) Removing info for bundle [\(id)]")
|
|
1464
1083
|
UserDefaults.standard.removeObject(forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
1465
1084
|
} else {
|
|
1466
1085
|
let update = bundle!.setId(id: id)
|
|
1467
|
-
print("\(
|
|
1086
|
+
print("\(CapacitorUpdater.TAG) Storing info for bundle [\(id)]", update.toString())
|
|
1468
1087
|
do {
|
|
1469
1088
|
try UserDefaults.standard.setObj(update, forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
1470
1089
|
} catch {
|
|
1471
|
-
print("\(
|
|
1090
|
+
print("\(CapacitorUpdater.TAG) Failed to save info for bundle [\(id)]", error.localizedDescription)
|
|
1472
1091
|
}
|
|
1473
1092
|
}
|
|
1474
1093
|
UserDefaults.standard.synchronize()
|
|
1475
1094
|
}
|
|
1476
1095
|
|
|
1477
1096
|
private func setBundleStatus(id: String, status: BundleStatus) {
|
|
1478
|
-
print("\(
|
|
1097
|
+
print("\(CapacitorUpdater.TAG) Setting status for bundle [\(id)] to \(status)")
|
|
1479
1098
|
let info = self.getBundleInfo(id: id)
|
|
1480
1099
|
self.saveBundleInfo(id: id, bundle: info.setStatus(status: status.localizedString))
|
|
1481
1100
|
}
|