@capgo/capacitor-updater 7.34.0 → 7.34.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/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +76 -66
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +7 -7
- package/package.json +1 -1
|
@@ -72,7 +72,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
72
72
|
private static final String[] BREAKING_EVENT_NAMES = { "breakingAvailable", "majorAvailable" };
|
|
73
73
|
private static final String LAST_FAILED_BUNDLE_PREF_KEY = "CapacitorUpdater.lastFailedBundle";
|
|
74
74
|
|
|
75
|
-
private final String pluginVersion = "7.34.
|
|
75
|
+
private final String pluginVersion = "7.34.2";
|
|
76
76
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
77
77
|
|
|
78
78
|
private SharedPreferences.Editor editor;
|
|
@@ -302,8 +302,11 @@ public class DownloadService extends Worker {
|
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
final String finalFileHash = fileHash;
|
|
305
|
+
// Remove .br extension for decompressed files
|
|
306
|
+
boolean isBrotli = fileName.endsWith(".br");
|
|
307
|
+
String finalFileName = isBrotli ? fileName.substring(0, fileName.length() - 3) : fileName;
|
|
305
308
|
File targetFile = new File(destFolder, fileName);
|
|
306
|
-
File cacheFile = new File(cacheFolder, finalFileHash + "_" + new File(
|
|
309
|
+
File cacheFile = new File(cacheFolder, finalFileHash + "_" + new File(finalFileName).getName());
|
|
307
310
|
File builtinFile = new File(builtinFolder, fileName);
|
|
308
311
|
|
|
309
312
|
// Ensure parent directories of the target file exist
|
|
@@ -589,83 +592,90 @@ public class DownloadService extends Worker {
|
|
|
589
592
|
// Create a temporary file for the compressed data
|
|
590
593
|
File compressedFile = new File(getApplicationContext().getCacheDir(), "temp_" + targetFile.getName() + ".tmp");
|
|
591
594
|
|
|
592
|
-
try
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
595
|
+
try {
|
|
596
|
+
try (Response response = sharedClient.newCall(request).execute()) {
|
|
597
|
+
if (!response.isSuccessful()) {
|
|
598
|
+
sendStatsAsync("download_manifest_file_fail", getInputData().getString(VERSION) + ":" + finalTargetFile.getName());
|
|
599
|
+
throw new IOException("Unexpected response code: " + response.code());
|
|
600
|
+
}
|
|
597
601
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
602
|
+
// Download compressed file atomically
|
|
603
|
+
ResponseBody responseBody = response.body();
|
|
604
|
+
if (responseBody == null) {
|
|
605
|
+
throw new IOException("Response body is null");
|
|
606
|
+
}
|
|
603
607
|
|
|
604
|
-
|
|
605
|
-
|
|
608
|
+
// Use OkIO for atomic write
|
|
609
|
+
writeFileAtomic(compressedFile, responseBody.byteStream(), null);
|
|
606
610
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
+
if (!publicKey.isEmpty() && sessionKey != null && !sessionKey.isEmpty()) {
|
|
612
|
+
logger.debug("Decrypting file " + targetFile.getName());
|
|
613
|
+
CryptoCipher.decryptFile(compressedFile, publicKey, sessionKey);
|
|
614
|
+
}
|
|
611
615
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
616
|
+
// Only decompress if file has .br extension
|
|
617
|
+
if (isBrotli) {
|
|
618
|
+
// Use new decompression method with atomic write
|
|
619
|
+
try (FileInputStream fis = new FileInputStream(compressedFile)) {
|
|
620
|
+
byte[] compressedData = new byte[(int) compressedFile.length()];
|
|
621
|
+
fis.read(compressedData);
|
|
622
|
+
byte[] decompressedData;
|
|
623
|
+
try {
|
|
624
|
+
decompressedData = decompressBrotli(compressedData, targetFile.getName());
|
|
625
|
+
} catch (IOException e) {
|
|
626
|
+
sendStatsAsync(
|
|
627
|
+
"download_manifest_brotli_fail",
|
|
628
|
+
getInputData().getString(VERSION) + ":" + finalTargetFile.getName()
|
|
629
|
+
);
|
|
630
|
+
throw e;
|
|
631
|
+
}
|
|
628
632
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
633
|
+
// Write decompressed data atomically
|
|
634
|
+
try (java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(decompressedData)) {
|
|
635
|
+
writeFileAtomic(finalTargetFile, bais, null);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
} else {
|
|
639
|
+
// Just copy the file without decompression using atomic operation
|
|
640
|
+
try (FileInputStream fis = new FileInputStream(compressedFile)) {
|
|
641
|
+
writeFileAtomic(finalTargetFile, fis, null);
|
|
632
642
|
}
|
|
633
643
|
}
|
|
634
|
-
} else {
|
|
635
|
-
// Just copy the file without decompression using atomic operation
|
|
636
|
-
try (FileInputStream fis = new FileInputStream(compressedFile)) {
|
|
637
|
-
writeFileAtomic(finalTargetFile, fis, null);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
644
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
645
|
+
// Delete the compressed file
|
|
646
|
+
compressedFile.delete();
|
|
647
|
+
String calculatedHash = CryptoCipher.calcChecksum(finalTargetFile);
|
|
648
|
+
CryptoCipher.logChecksumInfo("Calculated checksum", calculatedHash);
|
|
649
|
+
CryptoCipher.logChecksumInfo("Expected checksum", expectedHash);
|
|
650
|
+
|
|
651
|
+
// Verify checksum
|
|
652
|
+
if (calculatedHash.equals(expectedHash)) {
|
|
653
|
+
// Only cache if checksum is correct - use atomic copy
|
|
654
|
+
try (FileInputStream fis = new FileInputStream(finalTargetFile)) {
|
|
655
|
+
writeFileAtomic(cacheFile, fis, expectedHash);
|
|
656
|
+
}
|
|
657
|
+
} else {
|
|
658
|
+
finalTargetFile.delete();
|
|
659
|
+
sendStatsAsync("download_manifest_checksum_fail", getInputData().getString(VERSION) + ":" + finalTargetFile.getName());
|
|
660
|
+
throw new IOException(
|
|
661
|
+
"Checksum verification failed for: " +
|
|
662
|
+
downloadUrl +
|
|
663
|
+
" " +
|
|
664
|
+
targetFile.getName() +
|
|
665
|
+
" expected: " +
|
|
666
|
+
expectedHash +
|
|
667
|
+
" calculated: " +
|
|
668
|
+
calculatedHash
|
|
669
|
+
);
|
|
652
670
|
}
|
|
653
|
-
} else {
|
|
654
|
-
finalTargetFile.delete();
|
|
655
|
-
sendStatsAsync("download_manifest_checksum_fail", getInputData().getString(VERSION) + ":" + finalTargetFile.getName());
|
|
656
|
-
throw new IOException(
|
|
657
|
-
"Checksum verification failed for: " +
|
|
658
|
-
downloadUrl +
|
|
659
|
-
" " +
|
|
660
|
-
targetFile.getName() +
|
|
661
|
-
" expected: " +
|
|
662
|
-
expectedHash +
|
|
663
|
-
" calculated: " +
|
|
664
|
-
calculatedHash
|
|
665
|
-
);
|
|
666
671
|
}
|
|
667
672
|
} catch (Exception e) {
|
|
668
673
|
throw new IOException("Error in downloadAndVerify: " + e.getMessage());
|
|
674
|
+
} finally {
|
|
675
|
+
// Always cleanup the compressed temp file if it still exists
|
|
676
|
+
if (compressedFile.exists()) {
|
|
677
|
+
compressedFile.delete();
|
|
678
|
+
}
|
|
669
679
|
}
|
|
670
680
|
}
|
|
671
681
|
|
|
@@ -54,7 +54,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
54
54
|
CAPPluginMethod(name: "isShakeMenuEnabled", returnType: CAPPluginReturnPromise)
|
|
55
55
|
]
|
|
56
56
|
public var implementation = CapgoUpdater()
|
|
57
|
-
private let pluginVersion: String = "7.34.
|
|
57
|
+
private let pluginVersion: String = "7.34.2"
|
|
58
58
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
59
59
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
60
60
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
@@ -445,10 +445,13 @@ import UIKit
|
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
|
|
448
|
-
|
|
448
|
+
// Check if file has .br extension for Brotli decompression
|
|
449
|
+
let isBrotli = fileName.hasSuffix(".br")
|
|
450
|
+
let finalFileName = isBrotli ? String(fileName.dropLast(3)) : fileName
|
|
451
|
+
let fileNameWithoutPath = (finalFileName as NSString).lastPathComponent
|
|
449
452
|
let cacheFileName = "\(fileHash)_\(fileNameWithoutPath)"
|
|
450
453
|
let cacheFilePath = cacheFolder.appendingPathComponent(cacheFileName)
|
|
451
|
-
let destFilePath = destFolder.appendingPathComponent(
|
|
454
|
+
let destFilePath = destFolder.appendingPathComponent(finalFileName)
|
|
452
455
|
let builtinFilePath = builtinFolder.appendingPathComponent(fileName)
|
|
453
456
|
|
|
454
457
|
// Create necessary subdirectories in the destination folder
|
|
@@ -502,11 +505,6 @@ import UIKit
|
|
|
502
505
|
try FileManager.default.removeItem(at: tempFile)
|
|
503
506
|
}
|
|
504
507
|
|
|
505
|
-
// Check if file has .br extension for Brotli decompression
|
|
506
|
-
let isBrotli = fileName.hasSuffix(".br")
|
|
507
|
-
let finalFileName = isBrotli ? String(fileName.dropLast(3)) : fileName
|
|
508
|
-
let destFilePath = destFolder.appendingPathComponent(finalFileName)
|
|
509
|
-
|
|
510
508
|
if isBrotli {
|
|
511
509
|
// Decompress the Brotli data
|
|
512
510
|
guard let decompressedData = self.decompressBrotli(data: finalData, fileName: fileName) else {
|
|
@@ -523,6 +521,8 @@ import UIKit
|
|
|
523
521
|
CryptoCipher.logChecksumInfo(label: "Calculated checksum", hexChecksum: calculatedChecksum)
|
|
524
522
|
CryptoCipher.logChecksumInfo(label: "Expected checksum", hexChecksum: fileHash)
|
|
525
523
|
if calculatedChecksum != fileHash {
|
|
524
|
+
// Delete the corrupt file before throwing error
|
|
525
|
+
try? FileManager.default.removeItem(at: destFilePath)
|
|
526
526
|
self.sendStats(action: "download_manifest_checksum_fail", versionName: "\(version):\(finalFileName)")
|
|
527
527
|
throw NSError(domain: "ChecksumError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Computed checksum is not equal to required checksum (\(calculatedChecksum) != \(fileHash)) for file \(fileName) at url \(downloadUrl)"])
|
|
528
528
|
}
|