@capgo/capacitor-updater 7.51.21 → 7.51.23
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/README.md +1 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +18 -2
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +135 -35
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +51 -18
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +103 -28
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +89 -42
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +177 -32
- package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +9 -1
- package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +4 -1
- package/package.json +1 -1
|
@@ -363,6 +363,38 @@ public class DownloadService extends Worker {
|
|
|
363
363
|
return seenTargets.add(targetFile.getCanonicalPath());
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
private static final class ManifestDownloadTask {
|
|
367
|
+
|
|
368
|
+
final String fileName;
|
|
369
|
+
final String finalFileHash;
|
|
370
|
+
final String downloadUrl;
|
|
371
|
+
final boolean isBrotli;
|
|
372
|
+
final File targetFile;
|
|
373
|
+
final File builtinFile;
|
|
374
|
+
final File cacheFile;
|
|
375
|
+
final File legacyCacheFile;
|
|
376
|
+
|
|
377
|
+
ManifestDownloadTask(
|
|
378
|
+
final String fileName,
|
|
379
|
+
final String finalFileHash,
|
|
380
|
+
final String downloadUrl,
|
|
381
|
+
final boolean isBrotli,
|
|
382
|
+
final File targetFile,
|
|
383
|
+
final File builtinFile,
|
|
384
|
+
final File cacheFile,
|
|
385
|
+
final File legacyCacheFile
|
|
386
|
+
) {
|
|
387
|
+
this.fileName = fileName;
|
|
388
|
+
this.finalFileHash = finalFileHash;
|
|
389
|
+
this.downloadUrl = downloadUrl;
|
|
390
|
+
this.isBrotli = isBrotli;
|
|
391
|
+
this.targetFile = targetFile;
|
|
392
|
+
this.builtinFile = builtinFile;
|
|
393
|
+
this.cacheFile = cacheFile;
|
|
394
|
+
this.legacyCacheFile = legacyCacheFile;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
366
398
|
static boolean tryCopyBuiltinAsset(final AssetManager assets, final String fileName, final File dest, final String expectedHash) {
|
|
367
399
|
if (assets == null || fileName == null || dest == null) {
|
|
368
400
|
return false;
|
|
@@ -424,7 +456,7 @@ public class DownloadService extends Worker {
|
|
|
424
456
|
return createFailureResult("Manifest is null");
|
|
425
457
|
}
|
|
426
458
|
} else {
|
|
427
|
-
handleSingleFileDownload(url, id, documentsDir, dest, version, sessionKey, checksum);
|
|
459
|
+
handleSingleFileDownload(url, id, documentsDir, dest, version, sessionKey, checksum, publicKey);
|
|
428
460
|
return createSuccessResult(dest, version, sessionKey, checksum, false);
|
|
429
461
|
}
|
|
430
462
|
} catch (DownloadRetryException e) {
|
|
@@ -515,6 +547,12 @@ public class DownloadService extends Worker {
|
|
|
515
547
|
try {
|
|
516
548
|
logger.debug("handleManifestDownload");
|
|
517
549
|
|
|
550
|
+
if (publicKey != null && !publicKey.isEmpty() && !CryptoCipher.isValidSessionKey(sessionKey)) {
|
|
551
|
+
logger.error("Public key present but no valid session key provided");
|
|
552
|
+
sendStatsAsync("session_key_required", version);
|
|
553
|
+
throw new IOException("Session key required when public key is present");
|
|
554
|
+
}
|
|
555
|
+
|
|
518
556
|
// Send stats for manifest download start
|
|
519
557
|
sendStatsAsync("download_manifest_start", version);
|
|
520
558
|
|
|
@@ -535,9 +573,7 @@ public class DownloadService extends Worker {
|
|
|
535
573
|
int totalFiles = manifest.length();
|
|
536
574
|
final AtomicLong completedFiles = new AtomicLong(0);
|
|
537
575
|
final AtomicBoolean hasError = new AtomicBoolean(false);
|
|
538
|
-
|
|
539
|
-
ExecutorService executor = Executors.newFixedThreadPool(Math.min(MANIFEST_MAX_CONCURRENT_FILES, Math.max(1, totalFiles)));
|
|
540
|
-
List<Future<?>> futures = new ArrayList<>();
|
|
576
|
+
final List<ManifestDownloadTask> tasks = new ArrayList<>();
|
|
541
577
|
final Set<String> seenTargets = new HashSet<>();
|
|
542
578
|
|
|
543
579
|
for (int i = 0; i < totalFiles; i++) {
|
|
@@ -552,7 +588,13 @@ public class DownloadService extends Worker {
|
|
|
552
588
|
continue;
|
|
553
589
|
}
|
|
554
590
|
|
|
555
|
-
if (publicKey != null && !publicKey.isEmpty()
|
|
591
|
+
if (publicKey != null && !publicKey.isEmpty()) {
|
|
592
|
+
if (!CryptoCipher.isValidSessionKey(sessionKey)) {
|
|
593
|
+
logger.error("Public key present but no valid session key provided");
|
|
594
|
+
sendStatsAsync("session_key_required", version);
|
|
595
|
+
hasError.set(true);
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
556
598
|
try {
|
|
557
599
|
fileHash = CryptoCipher.decryptChecksum(fileHash, publicKey);
|
|
558
600
|
} catch (Exception e) {
|
|
@@ -592,35 +634,56 @@ public class DownloadService extends Worker {
|
|
|
592
634
|
final File legacyCacheFile =
|
|
593
635
|
isBrotli && cacheFile != null ? new File(cacheFolder, finalFileHash + "_" + new File(fileName).getName()) : null;
|
|
594
636
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
637
|
+
tasks.add(
|
|
638
|
+
new ManifestDownloadTask(
|
|
639
|
+
fileName,
|
|
640
|
+
finalFileHash,
|
|
641
|
+
downloadUrl,
|
|
642
|
+
isBrotli,
|
|
643
|
+
targetFile,
|
|
644
|
+
builtinFile,
|
|
645
|
+
cacheFile,
|
|
646
|
+
legacyCacheFile
|
|
647
|
+
)
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (hasError.get()) {
|
|
652
|
+
throw new IOException("Manifest contains invalid or duplicate file paths");
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
for (final ManifestDownloadTask task : tasks) {
|
|
656
|
+
if (!Objects.requireNonNull(task.targetFile.getParentFile()).exists() && !task.targetFile.getParentFile().mkdirs()) {
|
|
657
|
+
logger.error("Failed to create parent directory for: " + task.targetFile.getAbsolutePath());
|
|
658
|
+
throw new IOException("Failed to create parent directory for: " + task.targetFile.getAbsolutePath());
|
|
600
659
|
}
|
|
660
|
+
}
|
|
601
661
|
|
|
602
|
-
|
|
662
|
+
ExecutorService executor = Executors.newFixedThreadPool(Math.min(MANIFEST_MAX_CONCURRENT_FILES, Math.max(1, totalFiles)));
|
|
663
|
+
List<Future<?>> futures = new ArrayList<>();
|
|
664
|
+
|
|
665
|
+
for (final ManifestDownloadTask task : tasks) {
|
|
603
666
|
Future<?> future = executor.submit(() -> {
|
|
604
667
|
try {
|
|
605
|
-
if (tryCopyBuiltinAsset(assets, fileName, targetFile, finalFileHash)) {
|
|
606
|
-
logger.debug("using builtin asset " + fileName);
|
|
607
|
-
} else if (tryCopyBuiltinFile(builtinFile, targetFile, finalFileHash)) {
|
|
608
|
-
logger.debug("using builtin file " + fileName);
|
|
668
|
+
if (tryCopyBuiltinAsset(assets, task.fileName, task.targetFile, task.finalFileHash)) {
|
|
669
|
+
logger.debug("using builtin asset " + task.fileName);
|
|
670
|
+
} else if (tryCopyBuiltinFile(task.builtinFile, task.targetFile, task.finalFileHash)) {
|
|
671
|
+
logger.debug("using builtin file " + task.fileName);
|
|
609
672
|
} else if (
|
|
610
|
-
tryCopyFromCache(cacheFile, targetFile, finalFileHash) ||
|
|
611
|
-
(legacyCacheFile != null && tryCopyFromCache(legacyCacheFile, targetFile, finalFileHash))
|
|
673
|
+
tryCopyFromCache(task.cacheFile, task.targetFile, task.finalFileHash) ||
|
|
674
|
+
(task.legacyCacheFile != null && tryCopyFromCache(task.legacyCacheFile, task.targetFile, task.finalFileHash))
|
|
612
675
|
) {
|
|
613
|
-
logger.debug("already cached " + fileName);
|
|
676
|
+
logger.debug("already cached " + task.fileName);
|
|
614
677
|
} else {
|
|
615
678
|
downloadAndVerify(
|
|
616
|
-
downloadUrl,
|
|
617
|
-
targetFile,
|
|
618
|
-
cacheFile,
|
|
619
|
-
finalFileHash,
|
|
679
|
+
task.downloadUrl,
|
|
680
|
+
task.targetFile,
|
|
681
|
+
task.cacheFile,
|
|
682
|
+
task.finalFileHash,
|
|
620
683
|
sessionKey,
|
|
621
684
|
publicKey,
|
|
622
|
-
|
|
623
|
-
fileName
|
|
685
|
+
task.isBrotli,
|
|
686
|
+
task.fileName
|
|
624
687
|
);
|
|
625
688
|
}
|
|
626
689
|
|
|
@@ -628,8 +691,8 @@ public class DownloadService extends Worker {
|
|
|
628
691
|
int percent = calcTotalPercent(completed, totalFiles);
|
|
629
692
|
setProgress(percent);
|
|
630
693
|
} catch (Exception e) {
|
|
631
|
-
logger.error("Error processing file: " + fileName + " " + e.getMessage());
|
|
632
|
-
sendStatsAsync("download_manifest_file_fail", version + ":" + fileName);
|
|
694
|
+
logger.error("Error processing file: " + task.fileName + " " + e.getMessage());
|
|
695
|
+
sendStatsAsync("download_manifest_file_fail", version + ":" + task.fileName);
|
|
633
696
|
hasError.set(true);
|
|
634
697
|
}
|
|
635
698
|
});
|
|
@@ -676,8 +739,20 @@ public class DownloadService extends Worker {
|
|
|
676
739
|
String dest,
|
|
677
740
|
String version,
|
|
678
741
|
String sessionKey,
|
|
679
|
-
String checksum
|
|
742
|
+
String checksum,
|
|
743
|
+
String publicKey
|
|
680
744
|
) {
|
|
745
|
+
if (publicKey != null && !publicKey.isEmpty() && !CryptoCipher.isValidSessionKey(sessionKey)) {
|
|
746
|
+
logger.error("Public key present but no valid session key provided");
|
|
747
|
+
sendStatsAsync("session_key_required", version);
|
|
748
|
+
throw new RuntimeException("Session key required when public key is present");
|
|
749
|
+
}
|
|
750
|
+
if (checksum == null || checksum.isEmpty()) {
|
|
751
|
+
logger.error("No checksum provided");
|
|
752
|
+
sendStatsAsync("checksum_required", version);
|
|
753
|
+
throw new RuntimeException("Checksum required");
|
|
754
|
+
}
|
|
755
|
+
|
|
681
756
|
// Send stats for zip download start
|
|
682
757
|
sendStatsAsync("download_zip_start", version);
|
|
683
758
|
|
|
@@ -1061,7 +1136,7 @@ public class DownloadService extends Worker {
|
|
|
1061
1136
|
}
|
|
1062
1137
|
}
|
|
1063
1138
|
|
|
1064
|
-
boolean needDecrypt = publicKey != null && !publicKey.isEmpty() &&
|
|
1139
|
+
boolean needDecrypt = publicKey != null && !publicKey.isEmpty() && CryptoCipher.isValidSessionKey(sessionKey);
|
|
1065
1140
|
File source = partial;
|
|
1066
1141
|
if (needDecrypt) {
|
|
1067
1142
|
workFile = new File(cacheFolder, "work_" + UUID.randomUUID() + "_" + targetFile.getName() + ".tmp");
|
|
@@ -96,7 +96,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
96
96
|
deinit {
|
|
97
97
|
implementation.shutdown()
|
|
98
98
|
}
|
|
99
|
-
private let pluginVersion: String = "7.51.
|
|
99
|
+
private let pluginVersion: String = "7.51.23"
|
|
100
100
|
private let launchStartedAtMs = Int64(Date().timeIntervalSince1970 * 1000)
|
|
101
101
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
102
102
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
@@ -582,8 +582,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
582
582
|
var dest: URL
|
|
583
583
|
if BundleInfo.ID_BUILTIN == id {
|
|
584
584
|
dest = Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
585
|
+
} else if let bundleDir = try? self.implementation.getBundleDirectory(id: id) {
|
|
586
|
+
dest = bundleDir
|
|
585
587
|
} else {
|
|
586
|
-
dest =
|
|
588
|
+
dest = Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
587
589
|
}
|
|
588
590
|
|
|
589
591
|
if !FileManager.default.fileExists(atPath: dest.path) {
|
|
@@ -1611,7 +1613,19 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1611
1613
|
throw makePreviewError("Invalid download URL")
|
|
1612
1614
|
}
|
|
1613
1615
|
|
|
1616
|
+
if !self.implementation.publicKey.isEmpty && !CryptoCipher.isValidSessionKey(sessionKey) {
|
|
1617
|
+
self.logger.error("Public key present but no valid session key provided")
|
|
1618
|
+
self.implementation.sendStats(action: "session_key_required", versionName: version)
|
|
1619
|
+
throw makePreviewError("Session key required when public key is present")
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1614
1622
|
var checksum = rawChecksum
|
|
1623
|
+
if manifestEntries == nil && checksum.isEmpty {
|
|
1624
|
+
self.logger.error("No checksum provided")
|
|
1625
|
+
self.implementation.sendStats(action: "checksum_required", versionName: version)
|
|
1626
|
+
throw makePreviewError("Checksum required")
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1615
1629
|
let next: BundleInfo
|
|
1616
1630
|
if let manifestEntries = manifestEntries {
|
|
1617
1631
|
next = try self.implementation.downloadManifest(manifest: manifestEntries, version: version, sessionKey: sessionKey)
|
|
@@ -1619,29 +1633,20 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1619
1633
|
next = try self.implementation.download(url: url, version: version, sessionKey: sessionKey)
|
|
1620
1634
|
}
|
|
1621
1635
|
|
|
1622
|
-
if
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
self.
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
CryptoCipher.logChecksumInfo(label: "Expected checksum", hexChecksum: checksum)
|
|
1636
|
-
if (checksum != "" || self.implementation.publicKey != "") && next.getChecksum() != checksum {
|
|
1637
|
-
self.logger.error("Error checksum \(next.getChecksum()) \(checksum)")
|
|
1638
|
-
self.implementation.sendStats(action: "checksum_fail", versionName: next.getVersionName())
|
|
1639
|
-
let id = next.getId()
|
|
1640
|
-
let resDel = self.implementation.delete(id: id)
|
|
1641
|
-
if !resDel {
|
|
1642
|
-
self.logger.error("Delete failed, id \(id) doesn't exist")
|
|
1636
|
+
if manifestEntries == nil {
|
|
1637
|
+
checksum = try CryptoCipher.decryptChecksum(checksum: checksum, publicKey: self.implementation.publicKey)
|
|
1638
|
+
CryptoCipher.logChecksumInfo(label: "Bundle checksum", hexChecksum: next.getChecksum())
|
|
1639
|
+
CryptoCipher.logChecksumInfo(label: "Expected checksum", hexChecksum: checksum)
|
|
1640
|
+
if next.getChecksum() != checksum {
|
|
1641
|
+
self.logger.error("Error checksum \(next.getChecksum()) \(checksum)")
|
|
1642
|
+
self.implementation.sendStats(action: "checksum_fail", versionName: next.getVersionName())
|
|
1643
|
+
let id = next.getId()
|
|
1644
|
+
let resDel = self.implementation.delete(id: id)
|
|
1645
|
+
if !resDel {
|
|
1646
|
+
self.logger.error("Delete failed, id \(id) doesn't exist")
|
|
1647
|
+
}
|
|
1648
|
+
throw ObjectSavableError.checksum
|
|
1643
1649
|
}
|
|
1644
|
-
throw ObjectSavableError.checksum
|
|
1645
1650
|
}
|
|
1646
1651
|
|
|
1647
1652
|
self.logger.info("Good checksum \(next.getChecksum()) \(checksum)")
|
|
@@ -1693,8 +1698,10 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
1693
1698
|
let id = self.implementation.getCurrentBundleId()
|
|
1694
1699
|
if BundleInfo.ID_BUILTIN == id {
|
|
1695
1700
|
return Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
1701
|
+
} else if let bundleDir = try? self.implementation.getBundleDirectory(id: id) {
|
|
1702
|
+
return bundleDir
|
|
1696
1703
|
} else {
|
|
1697
|
-
return
|
|
1704
|
+
return Bundle.main.resourceURL!.appendingPathComponent("public")
|
|
1698
1705
|
}
|
|
1699
1706
|
}
|
|
1700
1707
|
|
|
@@ -4174,6 +4181,22 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
4174
4181
|
self.updateUrl = updateUrl
|
|
4175
4182
|
}
|
|
4176
4183
|
|
|
4184
|
+
func downloadBundleForTesting(
|
|
4185
|
+
urlString: String,
|
|
4186
|
+
version: String,
|
|
4187
|
+
sessionKey: String,
|
|
4188
|
+
checksum: String,
|
|
4189
|
+
manifestEntries: [ManifestEntry]?
|
|
4190
|
+
) throws -> BundleInfo {
|
|
4191
|
+
try downloadBundle(
|
|
4192
|
+
urlString: urlString,
|
|
4193
|
+
version: version,
|
|
4194
|
+
sessionKey: sessionKey,
|
|
4195
|
+
checksum: checksum,
|
|
4196
|
+
manifestEntries: manifestEntries
|
|
4197
|
+
)
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4177
4200
|
func setAutoUpdateModeForTesting(_ autoUpdateMode: String) {
|
|
4178
4201
|
self.autoUpdateMode = Self.normalizedAutoUpdateMode(autoUpdateMode)
|
|
4179
4202
|
self.autoUpdate = Self.isAutoUpdateModeEnabled(self.autoUpdateMode)
|
|
@@ -4509,6 +4532,17 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
4509
4532
|
if latestVersionName != "" && current.getVersionName() != latestVersionName {
|
|
4510
4533
|
do {
|
|
4511
4534
|
self.logger.info("New bundle: \(latestVersionName) found. Current is: \(current.getVersionName()). \(messageUpdate)")
|
|
4535
|
+
if !self.implementation.publicKey.isEmpty && !CryptoCipher.isValidSessionKey(sessionKey) {
|
|
4536
|
+
self.logger.error("Public key present but no valid session key provided")
|
|
4537
|
+
self.implementation.sendStats(action: "session_key_required", versionName: latestVersionName)
|
|
4538
|
+
self.endBackGroundTaskWithNotif(
|
|
4539
|
+
msg: "Session key required when public key is present",
|
|
4540
|
+
latestVersionName: latestVersionName,
|
|
4541
|
+
current: current,
|
|
4542
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
4543
|
+
)
|
|
4544
|
+
return
|
|
4545
|
+
}
|
|
4512
4546
|
var nextImpl = self.implementation.getBundleInfoByVersionName(version: latestVersionName)
|
|
4513
4547
|
let needsDownload = nextImpl.map(Self.shouldRetryDownloadForExistingBundle) ?? true
|
|
4514
4548
|
if needsDownload {
|
|
@@ -4526,6 +4560,17 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
4526
4560
|
if res.manifest != nil {
|
|
4527
4561
|
nextImpl = try self.implementation.downloadManifest(manifest: res.manifest!, version: latestVersionName, sessionKey: sessionKey, link: res.link, comment: res.comment)
|
|
4528
4562
|
} else {
|
|
4563
|
+
if res.checksum.isEmpty {
|
|
4564
|
+
self.logger.error("No checksum provided")
|
|
4565
|
+
self.implementation.sendStats(action: "checksum_required", versionName: latestVersionName)
|
|
4566
|
+
self.endBackGroundTaskWithNotif(
|
|
4567
|
+
msg: "Checksum required",
|
|
4568
|
+
latestVersionName: latestVersionName,
|
|
4569
|
+
current: current,
|
|
4570
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
4571
|
+
)
|
|
4572
|
+
return
|
|
4573
|
+
}
|
|
4529
4574
|
nextImpl = try self.implementation.download(url: downloadUrl, version: latestVersionName, sessionKey: sessionKey, link: res.link, comment: res.comment)
|
|
4530
4575
|
}
|
|
4531
4576
|
}
|
|
@@ -4554,24 +4599,26 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
4554
4599
|
self.endBackGroundTask()
|
|
4555
4600
|
return
|
|
4556
4601
|
}
|
|
4557
|
-
res.
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4602
|
+
if res.manifest == nil {
|
|
4603
|
+
res.checksum = try CryptoCipher.decryptChecksum(checksum: res.checksum, publicKey: self.implementation.publicKey)
|
|
4604
|
+
CryptoCipher.logChecksumInfo(label: "Bundle checksum", hexChecksum: next.getChecksum())
|
|
4605
|
+
CryptoCipher.logChecksumInfo(label: "Expected checksum", hexChecksum: res.checksum)
|
|
4606
|
+
if next.getChecksum() != res.checksum {
|
|
4607
|
+
self.logger.error("Error checksum \(next.getChecksum()) \(res.checksum)")
|
|
4608
|
+
self.implementation.sendStats(action: "checksum_fail", versionName: next.getVersionName())
|
|
4609
|
+
let id = next.getId()
|
|
4610
|
+
let resDel = self.implementation.delete(id: id)
|
|
4611
|
+
if !resDel {
|
|
4612
|
+
self.logger.error("Delete failed, id \(id) doesn't exist")
|
|
4613
|
+
}
|
|
4614
|
+
self.endBackGroundTaskWithNotif(
|
|
4615
|
+
msg: "Error checksum",
|
|
4616
|
+
latestVersionName: latestVersionName,
|
|
4617
|
+
current: current,
|
|
4618
|
+
plannedDirectUpdate: plannedDirectUpdate
|
|
4619
|
+
)
|
|
4620
|
+
return
|
|
4567
4621
|
}
|
|
4568
|
-
self.endBackGroundTaskWithNotif(
|
|
4569
|
-
msg: "Error checksum",
|
|
4570
|
-
latestVersionName: latestVersionName,
|
|
4571
|
-
current: current,
|
|
4572
|
-
plannedDirectUpdate: plannedDirectUpdate
|
|
4573
|
-
)
|
|
4574
|
-
return
|
|
4575
4622
|
}
|
|
4576
4623
|
if self.shouldBlockAutoUpdateForPreviewSession() {
|
|
4577
4624
|
self.clearDownloadInProgressState()
|