@capgo/capacitor-updater 6.39.0 → 6.40.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapgoCapacitorUpdater.podspec +1 -1
- package/Package.swift +2 -2
- package/README.md +70 -202
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +54 -56
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +52 -33
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +10 -14
- package/android/src/main/java/ee/forgr/capacitor_updater/DeviceIdHelper.java +0 -6
- package/dist/docs.json +112 -684
- package/dist/esm/definitions.d.ts +23 -352
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/history.js +2 -2
- package/dist/esm/history.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +3 -2
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +4 -4
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +2 -2
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +151 -107
- package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +34 -23
- package/package.json +6 -5
|
@@ -5,11 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import Foundation
|
|
8
|
-
|
|
9
|
-
import ZipArchive
|
|
10
|
-
#else
|
|
11
|
-
import SSZipArchive
|
|
12
|
-
#endif
|
|
8
|
+
import ZIPFoundation
|
|
13
9
|
import Alamofire
|
|
14
10
|
import Compression
|
|
15
11
|
import UIKit
|
|
@@ -161,7 +157,8 @@ import UIKit
|
|
|
161
157
|
case .success:
|
|
162
158
|
self.logger.info("Rate limit statistic sent")
|
|
163
159
|
case let .failure(error):
|
|
164
|
-
self.logger.error("Error sending rate limit statistic
|
|
160
|
+
self.logger.error("Error sending rate limit statistic")
|
|
161
|
+
self.logger.debug("Error: \(error.localizedDescription)")
|
|
165
162
|
}
|
|
166
163
|
semaphore.signal()
|
|
167
164
|
}
|
|
@@ -207,7 +204,8 @@ import UIKit
|
|
|
207
204
|
do {
|
|
208
205
|
try FileManager.default.createDirectory(atPath: source.path, withIntermediateDirectories: true, attributes: nil)
|
|
209
206
|
} catch {
|
|
210
|
-
logger.error("Cannot
|
|
207
|
+
logger.error("Cannot create directory")
|
|
208
|
+
logger.debug("Directory path: \(source.path)")
|
|
211
209
|
throw CustomError.cannotCreateDirectory
|
|
212
210
|
}
|
|
213
211
|
}
|
|
@@ -217,7 +215,8 @@ import UIKit
|
|
|
217
215
|
do {
|
|
218
216
|
try FileManager.default.removeItem(atPath: source.path)
|
|
219
217
|
} catch {
|
|
220
|
-
logger.error("File not removed
|
|
218
|
+
logger.error("File not removed")
|
|
219
|
+
logger.debug("Path: \(source.path)")
|
|
221
220
|
throw CustomError.cannotDeleteDirectory
|
|
222
221
|
}
|
|
223
222
|
}
|
|
@@ -234,43 +233,29 @@ import UIKit
|
|
|
234
233
|
return false
|
|
235
234
|
}
|
|
236
235
|
} catch {
|
|
237
|
-
logger.error("File not moved
|
|
236
|
+
logger.error("File not moved")
|
|
237
|
+
logger.debug("Source: \(source.path), Dest: \(dest.path)")
|
|
238
238
|
throw CustomError.cannotUnflat
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
private func
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
private func validateZipEntry(path: String, destUnZip: URL) throws {
|
|
243
|
+
// Check for Windows paths
|
|
244
|
+
if path.contains("\\") {
|
|
245
|
+
logger.error("Unzip failed: Windows path not supported")
|
|
246
|
+
logger.debug("Invalid path: \(path)")
|
|
245
247
|
self.sendStats(action: "windows_path_fail")
|
|
248
|
+
throw CustomError.cannotUnzip
|
|
246
249
|
}
|
|
247
250
|
|
|
248
|
-
|
|
249
|
-
let
|
|
250
|
-
let
|
|
251
|
+
// Check for path traversal
|
|
252
|
+
let fileURL = destUnZip.appendingPathComponent(path)
|
|
253
|
+
let canonicalPath = fileURL.standardizedFileURL.path
|
|
254
|
+
let canonicalDir = destUnZip.standardizedFileURL.path
|
|
251
255
|
|
|
252
256
|
if !canonicalPath.hasPrefix(canonicalDir) {
|
|
253
257
|
self.sendStats(action: "canonical_path_fail")
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
let isDirectory = entry.hasSuffix("/")
|
|
258
|
-
if !isDirectory {
|
|
259
|
-
let folderURL = fileURL.deletingLastPathComponent()
|
|
260
|
-
if !FileManager.default.fileExists(atPath: folderURL.path) {
|
|
261
|
-
do {
|
|
262
|
-
try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
|
|
263
|
-
} catch {
|
|
264
|
-
self.sendStats(action: "directory_path_fail")
|
|
265
|
-
unzipError = error as NSError
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
let newPercent = self.calcTotalPercent(percent: Int(Double(entryNumber) / Double(total) * 100), min: 75, max: 81)
|
|
271
|
-
if newPercent != self.unzipPercent {
|
|
272
|
-
self.unzipPercent = newPercent
|
|
273
|
-
self.notifyDownload(id: id, percent: newPercent)
|
|
258
|
+
throw CustomError.cannotUnzip
|
|
274
259
|
}
|
|
275
260
|
}
|
|
276
261
|
|
|
@@ -282,36 +267,52 @@ import UIKit
|
|
|
282
267
|
self.unzipPercent = 0
|
|
283
268
|
self.notifyDownload(id: id, percent: 75)
|
|
284
269
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
password: nil,
|
|
294
|
-
error: &unzipError,
|
|
295
|
-
delegate: nil,
|
|
296
|
-
progressHandler: { [weak self] (entry, zipInfo, entryNumber, total) in
|
|
297
|
-
DispatchQueue.global(qos: .background).async {
|
|
298
|
-
guard let self = self else { return }
|
|
299
|
-
if !notify {
|
|
300
|
-
return
|
|
301
|
-
}
|
|
302
|
-
self.unzipProgressHandler(entry: entry, zipInfo: zipInfo, entryNumber: entryNumber, total: total, destUnZip: destUnZip, id: id, unzipError: &unzipError)
|
|
303
|
-
}
|
|
304
|
-
},
|
|
305
|
-
completionHandler: { _, _, _ in
|
|
306
|
-
semaphore.signal()
|
|
307
|
-
})
|
|
270
|
+
// Open the archive
|
|
271
|
+
let archive: Archive
|
|
272
|
+
do {
|
|
273
|
+
archive = try Archive(url: sourceZip, accessMode: .read)
|
|
274
|
+
} catch {
|
|
275
|
+
self.sendStats(action: "unzip_fail")
|
|
276
|
+
throw CustomError.cannotUnzip
|
|
277
|
+
}
|
|
308
278
|
|
|
309
|
-
|
|
279
|
+
// Create destination directory
|
|
280
|
+
try FileManager.default.createDirectory(at: destUnZip, withIntermediateDirectories: true, attributes: nil)
|
|
281
|
+
|
|
282
|
+
// Count total entries for progress
|
|
283
|
+
let totalEntries = archive.reduce(0) { count, _ in count + 1 }
|
|
284
|
+
var processedEntries = 0
|
|
285
|
+
|
|
286
|
+
do {
|
|
287
|
+
for entry in archive {
|
|
288
|
+
// Validate entry path for security
|
|
289
|
+
try validateZipEntry(path: entry.path, destUnZip: destUnZip)
|
|
290
|
+
|
|
291
|
+
let destPath = destUnZip.appendingPathComponent(entry.path)
|
|
292
|
+
|
|
293
|
+
// Create parent directories if needed
|
|
294
|
+
let parentDir = destPath.deletingLastPathComponent()
|
|
295
|
+
if !FileManager.default.fileExists(atPath: parentDir.path) {
|
|
296
|
+
try FileManager.default.createDirectory(at: parentDir, withIntermediateDirectories: true, attributes: nil)
|
|
297
|
+
}
|
|
310
298
|
|
|
311
|
-
|
|
299
|
+
// Extract the entry
|
|
300
|
+
_ = try archive.extract(entry, to: destPath, skipCRC32: true)
|
|
301
|
+
|
|
302
|
+
// Update progress
|
|
303
|
+
processedEntries += 1
|
|
304
|
+
if notify && totalEntries > 0 {
|
|
305
|
+
let newPercent = self.calcTotalPercent(percent: Int(Double(processedEntries) / Double(totalEntries) * 100), min: 75, max: 81)
|
|
306
|
+
if newPercent != self.unzipPercent {
|
|
307
|
+
self.unzipPercent = newPercent
|
|
308
|
+
self.notifyDownload(id: id, percent: newPercent)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
} catch {
|
|
312
313
|
self.sendStats(action: "unzip_fail")
|
|
313
314
|
try? FileManager.default.removeItem(at: destUnZip)
|
|
314
|
-
throw
|
|
315
|
+
throw error
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
if try unflatFolder(source: destUnZip, dest: destPersist) {
|
|
@@ -324,7 +325,8 @@ import UIKit
|
|
|
324
325
|
try FileManager.default.removeItem(at: sourceZip)
|
|
325
326
|
}
|
|
326
327
|
} catch {
|
|
327
|
-
logger.error("Could not delete source zip
|
|
328
|
+
logger.error("Could not delete source zip")
|
|
329
|
+
logger.debug("Path: \(sourceZip.path), Error: \(error)")
|
|
328
330
|
}
|
|
329
331
|
}
|
|
330
332
|
|
|
@@ -399,8 +401,9 @@ import UIKit
|
|
|
399
401
|
latest.comment = comment
|
|
400
402
|
}
|
|
401
403
|
case let .failure(error):
|
|
402
|
-
self.logger.error("Error getting
|
|
403
|
-
|
|
404
|
+
self.logger.error("Error getting latest version")
|
|
405
|
+
self.logger.debug("Response: \(response.value.debugDescription), Error: \(error)")
|
|
406
|
+
latest.message = "Error getting Latest"
|
|
404
407
|
latest.error = "response_error"
|
|
405
408
|
latest.statusCode = response.response?.statusCode ?? 0
|
|
406
409
|
}
|
|
@@ -468,7 +471,8 @@ import UIKit
|
|
|
468
471
|
fileHash = try CryptoCipher.decryptChecksum(checksum: fileHash, publicKey: self.publicKey)
|
|
469
472
|
} catch {
|
|
470
473
|
downloadError = error
|
|
471
|
-
logger.error("
|
|
474
|
+
logger.error("Checksum decryption failed")
|
|
475
|
+
logger.debug("Bundle: \(id), File: \(fileName), Error: \(error)")
|
|
472
476
|
}
|
|
473
477
|
} else if self.hasOldPrivateKeyPropertyInConfig {
|
|
474
478
|
// V1 Encryption (privateKey) - deprecated but supported
|
|
@@ -500,7 +504,8 @@ import UIKit
|
|
|
500
504
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
501
505
|
} catch {
|
|
502
506
|
downloadError = error
|
|
503
|
-
logger.error("Failed to copy builtin file
|
|
507
|
+
logger.error("Failed to copy builtin file")
|
|
508
|
+
logger.debug("File: \(fileName), Error: \(error.localizedDescription)")
|
|
504
509
|
}
|
|
505
510
|
dispatchGroup.leave()
|
|
506
511
|
} else if self.tryCopyFromCache(from: cacheFilePath, to: destFilePath, expectedHash: fileHash) {
|
|
@@ -574,15 +579,18 @@ import UIKit
|
|
|
574
579
|
|
|
575
580
|
completedFiles += 1
|
|
576
581
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
577
|
-
self.logger.info("
|
|
582
|
+
self.logger.info("Manifest file downloaded and cached")
|
|
583
|
+
self.logger.debug("Bundle: \(id), File: \(fileName), Brotli: \(isBrotli), Encrypted: \(!self.publicKey.isEmpty && !sessionKey.isEmpty)")
|
|
578
584
|
} catch {
|
|
579
585
|
downloadError = error
|
|
580
|
-
self.logger.error("
|
|
586
|
+
self.logger.error("Manifest file download failed")
|
|
587
|
+
self.logger.debug("Bundle: \(id), File: \(fileName), Error: \(error.localizedDescription)")
|
|
581
588
|
}
|
|
582
589
|
case .failure(let error):
|
|
583
590
|
downloadError = error
|
|
584
591
|
self.sendStats(action: "download_manifest_file_fail", versionName: "\(version):\(fileName)")
|
|
585
|
-
self.logger.error("
|
|
592
|
+
self.logger.error("Manifest file download network error")
|
|
593
|
+
self.logger.debug("Bundle: \(id), File: \(fileName), Error: \(error.localizedDescription), Response: \(response.debugDescription)")
|
|
586
594
|
}
|
|
587
595
|
}
|
|
588
596
|
}
|
|
@@ -670,7 +678,8 @@ import UIKit
|
|
|
670
678
|
var status = compression_stream_init(streamPointer, COMPRESSION_STREAM_DECODE, COMPRESSION_BROTLI)
|
|
671
679
|
|
|
672
680
|
guard status != COMPRESSION_STATUS_ERROR else {
|
|
673
|
-
logger.error("
|
|
681
|
+
logger.error("Failed to initialize Brotli stream")
|
|
682
|
+
logger.debug("File: \(fileName), Status: \(status)")
|
|
674
683
|
return nil
|
|
675
684
|
}
|
|
676
685
|
|
|
@@ -692,7 +701,8 @@ import UIKit
|
|
|
692
701
|
if let baseAddress = rawBufferPointer.baseAddress {
|
|
693
702
|
streamPointer.pointee.src_ptr = baseAddress.assumingMemoryBound(to: UInt8.self)
|
|
694
703
|
} else {
|
|
695
|
-
logger.error("
|
|
704
|
+
logger.error("Failed to get base address for Brotli decompression")
|
|
705
|
+
logger.debug("File: \(fileName)")
|
|
696
706
|
status = COMPRESSION_STATUS_ERROR
|
|
697
707
|
return
|
|
698
708
|
}
|
|
@@ -702,7 +712,8 @@ import UIKit
|
|
|
702
712
|
if status == COMPRESSION_STATUS_ERROR {
|
|
703
713
|
let maxBytes = min(32, data.count)
|
|
704
714
|
let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
|
|
705
|
-
logger.error("
|
|
715
|
+
logger.error("Brotli decompression failed")
|
|
716
|
+
logger.debug("File: \(fileName), First \(maxBytes) bytes: \(hexDump)")
|
|
706
717
|
break
|
|
707
718
|
}
|
|
708
719
|
|
|
@@ -716,18 +727,19 @@ import UIKit
|
|
|
716
727
|
if status == COMPRESSION_STATUS_END {
|
|
717
728
|
break
|
|
718
729
|
} else if status == COMPRESSION_STATUS_ERROR {
|
|
719
|
-
logger.error("
|
|
730
|
+
logger.error("Brotli process failed")
|
|
731
|
+
logger.debug("File: \(fileName), Status: \(status)")
|
|
720
732
|
if let text = String(data: data, encoding: .utf8) {
|
|
721
733
|
let asciiCount = text.unicodeScalars.filter { $0.isASCII }.count
|
|
722
734
|
let totalCount = text.unicodeScalars.count
|
|
723
735
|
if totalCount > 0 && Double(asciiCount) / Double(totalCount) >= 0.8 {
|
|
724
|
-
logger.
|
|
736
|
+
logger.debug("Input appears to be plain text: \(text)")
|
|
725
737
|
}
|
|
726
738
|
}
|
|
727
739
|
|
|
728
740
|
let maxBytes = min(32, data.count)
|
|
729
741
|
let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
|
|
730
|
-
logger.
|
|
742
|
+
logger.debug("Raw data: \(hexDump)")
|
|
731
743
|
|
|
732
744
|
return nil
|
|
733
745
|
}
|
|
@@ -738,7 +750,8 @@ import UIKit
|
|
|
738
750
|
}
|
|
739
751
|
|
|
740
752
|
if input.count == 0 {
|
|
741
|
-
logger.error("
|
|
753
|
+
logger.error("Zero input size for Brotli decompression")
|
|
754
|
+
logger.debug("File: \(fileName)")
|
|
742
755
|
break
|
|
743
756
|
}
|
|
744
757
|
}
|
|
@@ -830,7 +843,8 @@ import UIKit
|
|
|
830
843
|
reachabilityManager?.stopListening()
|
|
831
844
|
|
|
832
845
|
if mainError != nil {
|
|
833
|
-
logger.error("Failed to download
|
|
846
|
+
logger.error("Failed to download bundle")
|
|
847
|
+
logger.debug("Error: \(String(describing: mainError))")
|
|
834
848
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum, link: link, comment: comment))
|
|
835
849
|
throw mainError!
|
|
836
850
|
}
|
|
@@ -841,7 +855,8 @@ import UIKit
|
|
|
841
855
|
try CryptoCipher.decryptFile(filePath: tempDataPath, publicKey: self.publicKey, sessionKey: sessionKey, version: version)
|
|
842
856
|
try FileManager.default.moveItem(at: tempDataPath, to: finalPath)
|
|
843
857
|
} catch {
|
|
844
|
-
logger.error("Failed decrypt file
|
|
858
|
+
logger.error("Failed to decrypt file")
|
|
859
|
+
logger.debug("Error: \(error)")
|
|
845
860
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum, link: link, comment: comment))
|
|
846
861
|
cleanDownloadData()
|
|
847
862
|
throw error
|
|
@@ -854,7 +869,8 @@ import UIKit
|
|
|
854
869
|
try self.saveDownloaded(sourceZip: finalPath, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory), notify: true)
|
|
855
870
|
|
|
856
871
|
} catch {
|
|
857
|
-
logger.error("Failed to unzip file
|
|
872
|
+
logger.error("Failed to unzip file")
|
|
873
|
+
logger.debug("Error: \(error)")
|
|
858
874
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum, link: link, comment: comment))
|
|
859
875
|
// Best-effort cleanup of the decrypted zip file when unzip fails
|
|
860
876
|
do {
|
|
@@ -862,7 +878,8 @@ import UIKit
|
|
|
862
878
|
try FileManager.default.removeItem(at: finalPath)
|
|
863
879
|
}
|
|
864
880
|
} catch {
|
|
865
|
-
logger.error("Could not delete failed zip
|
|
881
|
+
logger.error("Could not delete failed zip")
|
|
882
|
+
logger.debug("Path: \(finalPath.path), Error: \(error)")
|
|
866
883
|
}
|
|
867
884
|
cleanDownloadData()
|
|
868
885
|
throw error
|
|
@@ -885,13 +902,15 @@ import UIKit
|
|
|
885
902
|
let fileManager = FileManager.default
|
|
886
903
|
if !fileManager.fileExists(atPath: tempDataPath.path) {
|
|
887
904
|
if !fileManager.createFile(atPath: tempDataPath.path, contents: Data()) {
|
|
888
|
-
logger.error("Cannot ensure
|
|
905
|
+
logger.error("Cannot ensure temp data file exists")
|
|
906
|
+
logger.debug("Path: \(tempDataPath.path)")
|
|
889
907
|
}
|
|
890
908
|
}
|
|
891
909
|
|
|
892
910
|
if !fileManager.fileExists(atPath: updateInfo.path) {
|
|
893
911
|
if !fileManager.createFile(atPath: updateInfo.path, contents: Data()) {
|
|
894
|
-
logger.error("Cannot ensure
|
|
912
|
+
logger.error("Cannot ensure update info file exists")
|
|
913
|
+
logger.debug("Path: \(updateInfo.path)")
|
|
895
914
|
}
|
|
896
915
|
}
|
|
897
916
|
}
|
|
@@ -903,7 +922,8 @@ import UIKit
|
|
|
903
922
|
do {
|
|
904
923
|
try fileManager.removeItem(at: tempDataPath)
|
|
905
924
|
} catch {
|
|
906
|
-
logger.error("Could not delete
|
|
925
|
+
logger.error("Could not delete temp data file")
|
|
926
|
+
logger.debug("Path: \(tempDataPath), Error: \(error)")
|
|
907
927
|
}
|
|
908
928
|
}
|
|
909
929
|
// Deleting update.dat
|
|
@@ -911,7 +931,8 @@ import UIKit
|
|
|
911
931
|
do {
|
|
912
932
|
try fileManager.removeItem(at: updateInfo)
|
|
913
933
|
} catch {
|
|
914
|
-
logger.error("Could not delete
|
|
934
|
+
logger.error("Could not delete update info file")
|
|
935
|
+
logger.debug("Path: \(updateInfo), Error: \(error)")
|
|
915
936
|
}
|
|
916
937
|
}
|
|
917
938
|
}
|
|
@@ -930,7 +951,8 @@ import UIKit
|
|
|
930
951
|
fileHandle.closeFile()
|
|
931
952
|
}
|
|
932
953
|
} catch {
|
|
933
|
-
logger.error("Failed to write data
|
|
954
|
+
logger.error("Failed to write partial data")
|
|
955
|
+
logger.debug("Byte offset: \(byteOffset), Error: \(error)")
|
|
934
956
|
}
|
|
935
957
|
self.tempData.removeAll() // Clearing tempData to avoid writing the same data multiple times
|
|
936
958
|
}
|
|
@@ -939,7 +961,8 @@ import UIKit
|
|
|
939
961
|
do {
|
|
940
962
|
try "\(version)".write(to: updateInfo, atomically: true, encoding: .utf8)
|
|
941
963
|
} catch {
|
|
942
|
-
logger.error("Failed to save progress
|
|
964
|
+
logger.error("Failed to save download progress")
|
|
965
|
+
logger.debug("Error: \(error)")
|
|
943
966
|
}
|
|
944
967
|
}
|
|
945
968
|
private func getLocalUpdateVersion() -> String { // Return the version that was tried to be downloaded on last download attempt
|
|
@@ -961,7 +984,8 @@ import UIKit
|
|
|
961
984
|
return fileSize.int64Value
|
|
962
985
|
}
|
|
963
986
|
} catch {
|
|
964
|
-
logger.error("Could not retrieve
|
|
987
|
+
logger.error("Could not retrieve download progress size")
|
|
988
|
+
logger.debug("Error: \(error)")
|
|
965
989
|
}
|
|
966
990
|
return 0
|
|
967
991
|
}
|
|
@@ -1005,7 +1029,8 @@ import UIKit
|
|
|
1005
1029
|
public func delete(id: String, removeInfo: Bool) -> Bool {
|
|
1006
1030
|
let deleted: BundleInfo = self.getBundleInfo(id: id)
|
|
1007
1031
|
if deleted.isBuiltin() || self.getCurrentBundleId() == id {
|
|
1008
|
-
logger.info("Cannot delete
|
|
1032
|
+
logger.info("Cannot delete current or builtin bundle")
|
|
1033
|
+
logger.debug("Bundle ID: \(id)")
|
|
1009
1034
|
return false
|
|
1010
1035
|
}
|
|
1011
1036
|
|
|
@@ -1014,7 +1039,8 @@ import UIKit
|
|
|
1014
1039
|
!next.isDeleted() &&
|
|
1015
1040
|
!next.isErrorStatus() &&
|
|
1016
1041
|
next.getId() == id {
|
|
1017
|
-
logger.info("Cannot delete the next bundle
|
|
1042
|
+
logger.info("Cannot delete the next bundle")
|
|
1043
|
+
logger.debug("Bundle ID: \(id)")
|
|
1018
1044
|
return false
|
|
1019
1045
|
}
|
|
1020
1046
|
|
|
@@ -1022,7 +1048,8 @@ import UIKit
|
|
|
1022
1048
|
do {
|
|
1023
1049
|
try FileManager.default.removeItem(atPath: destPersist.path)
|
|
1024
1050
|
} catch {
|
|
1025
|
-
logger.error("
|
|
1051
|
+
logger.error("Bundle folder not removed")
|
|
1052
|
+
logger.debug("Path: \(destPersist.path)")
|
|
1026
1053
|
// even if, we don;t care. Android doesn't care
|
|
1027
1054
|
if removeInfo {
|
|
1028
1055
|
self.removeBundleInfo(id: id)
|
|
@@ -1035,7 +1062,8 @@ import UIKit
|
|
|
1035
1062
|
} else {
|
|
1036
1063
|
self.saveBundleInfo(id: id, bundle: deleted.setStatus(status: BundleStatus.DELETED.localizedString))
|
|
1037
1064
|
}
|
|
1038
|
-
logger.info("
|
|
1065
|
+
logger.info("Bundle deleted successfully")
|
|
1066
|
+
logger.debug("Version: \(deleted.getVersionName())")
|
|
1039
1067
|
self.sendStats(action: "delete", versionName: deleted.getVersionName())
|
|
1040
1068
|
return true
|
|
1041
1069
|
}
|
|
@@ -1063,7 +1091,8 @@ import UIKit
|
|
|
1063
1091
|
try fileManager.removeItem(at: cacheFolder)
|
|
1064
1092
|
logger.info("Cleaned up delta cache folder")
|
|
1065
1093
|
} catch {
|
|
1066
|
-
logger.error("Failed to cleanup delta cache
|
|
1094
|
+
logger.error("Failed to cleanup delta cache")
|
|
1095
|
+
logger.debug("Error: \(error.localizedDescription)")
|
|
1067
1096
|
}
|
|
1068
1097
|
}
|
|
1069
1098
|
|
|
@@ -1103,13 +1132,16 @@ import UIKit
|
|
|
1103
1132
|
do {
|
|
1104
1133
|
try fileManager.removeItem(at: url)
|
|
1105
1134
|
self.removeBundleInfo(id: id)
|
|
1106
|
-
logger.info("Deleted orphan bundle directory
|
|
1135
|
+
logger.info("Deleted orphan bundle directory")
|
|
1136
|
+
logger.debug("Bundle ID: \(id)")
|
|
1107
1137
|
} catch {
|
|
1108
|
-
logger.error("Failed to delete orphan bundle directory
|
|
1138
|
+
logger.error("Failed to delete orphan bundle directory")
|
|
1139
|
+
logger.debug("Bundle ID: \(id), Error: \(error.localizedDescription)")
|
|
1109
1140
|
}
|
|
1110
1141
|
}
|
|
1111
1142
|
} catch {
|
|
1112
|
-
logger.error("Failed to enumerate bundle directory for cleanup
|
|
1143
|
+
logger.error("Failed to enumerate bundle directory for cleanup")
|
|
1144
|
+
logger.debug("Error: \(error.localizedDescription)")
|
|
1113
1145
|
}
|
|
1114
1146
|
}
|
|
1115
1147
|
|
|
@@ -1140,13 +1172,16 @@ import UIKit
|
|
|
1140
1172
|
|
|
1141
1173
|
do {
|
|
1142
1174
|
try fileManager.removeItem(at: url)
|
|
1143
|
-
logger.info("Deleted orphaned temp unzip folder
|
|
1175
|
+
logger.info("Deleted orphaned temp unzip folder")
|
|
1176
|
+
logger.debug("Folder: \(folderName)")
|
|
1144
1177
|
} catch {
|
|
1145
|
-
logger.error("Failed to delete orphaned temp folder
|
|
1178
|
+
logger.error("Failed to delete orphaned temp folder")
|
|
1179
|
+
logger.debug("Folder: \(folderName), Error: \(error.localizedDescription)")
|
|
1146
1180
|
}
|
|
1147
1181
|
}
|
|
1148
1182
|
} catch {
|
|
1149
|
-
logger.error("Failed to enumerate library directory for temp folder cleanup
|
|
1183
|
+
logger.error("Failed to enumerate library directory for temp folder cleanup")
|
|
1184
|
+
logger.debug("Error: \(error.localizedDescription)")
|
|
1150
1185
|
}
|
|
1151
1186
|
}
|
|
1152
1187
|
|
|
@@ -1222,9 +1257,11 @@ import UIKit
|
|
|
1222
1257
|
if autoDeletePrevious && !fallback.isBuiltin() && fallback.getId() != bundle.getId() {
|
|
1223
1258
|
let res = self.delete(id: fallback.getId())
|
|
1224
1259
|
if res {
|
|
1225
|
-
logger.info("Deleted previous bundle
|
|
1260
|
+
logger.info("Deleted previous bundle")
|
|
1261
|
+
logger.debug("Bundle: \(fallback.toString())")
|
|
1226
1262
|
} else {
|
|
1227
|
-
logger.error("Failed to delete previous bundle
|
|
1263
|
+
logger.error("Failed to delete previous bundle")
|
|
1264
|
+
logger.debug("Bundle: \(fallback.toString())")
|
|
1228
1265
|
}
|
|
1229
1266
|
}
|
|
1230
1267
|
self.setFallbackBundle(fallback: bundle)
|
|
@@ -1305,7 +1342,8 @@ import UIKit
|
|
|
1305
1342
|
}
|
|
1306
1343
|
}
|
|
1307
1344
|
case let .failure(error):
|
|
1308
|
-
self.logger.error("Error
|
|
1345
|
+
self.logger.error("Error setting channel")
|
|
1346
|
+
self.logger.debug("Error: \(error)")
|
|
1309
1347
|
setChannel.error = "Request failed: \(error.localizedDescription)"
|
|
1310
1348
|
}
|
|
1311
1349
|
semaphore.signal()
|
|
@@ -1368,7 +1406,8 @@ import UIKit
|
|
|
1368
1406
|
}
|
|
1369
1407
|
}
|
|
1370
1408
|
|
|
1371
|
-
self.logger.error("Error
|
|
1409
|
+
self.logger.error("Error getting channel")
|
|
1410
|
+
self.logger.debug("Error: \(error)")
|
|
1372
1411
|
getChannel.error = "Request failed: \(error.localizedDescription)"
|
|
1373
1412
|
}
|
|
1374
1413
|
}
|
|
@@ -1458,7 +1497,8 @@ import UIKit
|
|
|
1458
1497
|
}
|
|
1459
1498
|
}
|
|
1460
1499
|
case let .failure(error):
|
|
1461
|
-
self.logger.error("Error
|
|
1500
|
+
self.logger.error("Error listing channels")
|
|
1501
|
+
self.logger.debug("Error: \(error)")
|
|
1462
1502
|
listChannels.error = "Request failed: \(error.localizedDescription)"
|
|
1463
1503
|
}
|
|
1464
1504
|
}
|
|
@@ -1504,9 +1544,11 @@ import UIKit
|
|
|
1504
1544
|
|
|
1505
1545
|
switch response.result {
|
|
1506
1546
|
case .success:
|
|
1507
|
-
self.logger.info("Stats sent
|
|
1547
|
+
self.logger.info("Stats sent successfully")
|
|
1548
|
+
self.logger.debug("Action: \(action), Version: \(versionName)")
|
|
1508
1549
|
case let .failure(error):
|
|
1509
|
-
self.logger.error("Error sending stats
|
|
1550
|
+
self.logger.error("Error sending stats")
|
|
1551
|
+
self.logger.debug("Response: \(response.value?.debugDescription ?? "nil"), Error: \(error.localizedDescription)")
|
|
1510
1552
|
}
|
|
1511
1553
|
semaphore.signal()
|
|
1512
1554
|
}
|
|
@@ -1530,7 +1572,8 @@ import UIKit
|
|
|
1530
1572
|
do {
|
|
1531
1573
|
result = try UserDefaults.standard.getObj(forKey: "\(trueId)\(self.INFO_SUFFIX)", castTo: BundleInfo.self)
|
|
1532
1574
|
} catch {
|
|
1533
|
-
logger.error("Failed to parse
|
|
1575
|
+
logger.error("Failed to parse bundle info")
|
|
1576
|
+
logger.debug("Bundle ID: \(trueId), Error: \(error.localizedDescription)")
|
|
1534
1577
|
result = BundleInfo(id: trueId, version: "", status: BundleStatus.PENDING, checksum: "")
|
|
1535
1578
|
}
|
|
1536
1579
|
}
|
|
@@ -1565,7 +1608,8 @@ import UIKit
|
|
|
1565
1608
|
do {
|
|
1566
1609
|
try UserDefaults.standard.setObj(update, forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
1567
1610
|
} catch {
|
|
1568
|
-
logger.error("Failed to save
|
|
1611
|
+
logger.error("Failed to save bundle info")
|
|
1612
|
+
logger.debug("Bundle ID: \(id), Error: \(error.localizedDescription)")
|
|
1569
1613
|
}
|
|
1570
1614
|
}
|
|
1571
1615
|
UserDefaults.standard.synchronize()
|