@capgo/capacitor-updater 7.0.17 → 7.0.22
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 +2 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1 -1
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +13 -7
- package/ios/Plugin/CapacitorUpdater.swift +49 -12
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +1 -1
- package/package.json +1 -1
|
@@ -235,6 +235,7 @@ public class CapacitorUpdater {
|
|
|
235
235
|
|
|
236
236
|
boolean success = finishDownload(id, dest, version, sessionKey, checksum, true, isManifest);
|
|
237
237
|
if (!success) {
|
|
238
|
+
Log.e(TAG, "Finish download failed: " + version);
|
|
238
239
|
saveBundleInfo(
|
|
239
240
|
id,
|
|
240
241
|
new BundleInfo(id, version, BundleStatus.ERROR, new Date(System.currentTimeMillis()), "")
|
|
@@ -249,6 +250,7 @@ public class CapacitorUpdater {
|
|
|
249
250
|
case FAILED:
|
|
250
251
|
Data failedData = workInfo.getOutputData();
|
|
251
252
|
String error = failedData.getString(DownloadService.ERROR);
|
|
253
|
+
Log.e(TAG, "Download failed: " + error + " " + workInfo.getState());
|
|
252
254
|
String failedVersion = failedData.getString(DownloadService.VERSION);
|
|
253
255
|
saveBundleInfo(
|
|
254
256
|
id,
|
|
@@ -57,7 +57,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
57
57
|
private static final String statsUrlDefault = "https://plugin.capgo.app/stats";
|
|
58
58
|
private static final String channelUrlDefault = "https://plugin.capgo.app/channel_self";
|
|
59
59
|
|
|
60
|
-
private final String PLUGIN_VERSION = "7.0.
|
|
60
|
+
private final String PLUGIN_VERSION = "7.0.22";
|
|
61
61
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
62
62
|
|
|
63
63
|
private SharedPreferences.Editor editor;
|
|
@@ -109,7 +109,6 @@ public class DownloadService extends Worker {
|
|
|
109
109
|
return createSuccessResult(dest, version, sessionKey, checksum, false);
|
|
110
110
|
}
|
|
111
111
|
} catch (Exception e) {
|
|
112
|
-
Log.e(TAG, "Error in doWork", e);
|
|
113
112
|
return createFailureResult(e.getMessage());
|
|
114
113
|
}
|
|
115
114
|
}
|
|
@@ -219,7 +218,7 @@ public class DownloadService extends Worker {
|
|
|
219
218
|
throw new IOException("One or more files failed to download");
|
|
220
219
|
}
|
|
221
220
|
} catch (Exception e) {
|
|
222
|
-
|
|
221
|
+
throw new RuntimeException(e.getLocalizedMessage());
|
|
223
222
|
}
|
|
224
223
|
}
|
|
225
224
|
|
|
@@ -317,10 +316,8 @@ public class DownloadService extends Worker {
|
|
|
317
316
|
}
|
|
318
317
|
}
|
|
319
318
|
} catch (OutOfMemoryError e) {
|
|
320
|
-
e.printStackTrace();
|
|
321
319
|
throw new RuntimeException("low_mem_fail");
|
|
322
320
|
} catch (Exception e) {
|
|
323
|
-
e.printStackTrace();
|
|
324
321
|
throw new RuntimeException(e.getLocalizedMessage());
|
|
325
322
|
}
|
|
326
323
|
}
|
|
@@ -334,7 +331,8 @@ public class DownloadService extends Worker {
|
|
|
334
331
|
infoFile.createNewFile();
|
|
335
332
|
tempFile.createNewFile();
|
|
336
333
|
} catch (IOException e) {
|
|
337
|
-
e
|
|
334
|
+
Log.e(TAG, "Error in clearDownloadData", e);
|
|
335
|
+
// not a fatal error, so we don't throw an exception
|
|
338
336
|
}
|
|
339
337
|
}
|
|
340
338
|
|
|
@@ -417,10 +415,18 @@ public class DownloadService extends Worker {
|
|
|
417
415
|
copyFile(targetFile, cacheFile);
|
|
418
416
|
} else {
|
|
419
417
|
targetFile.delete();
|
|
420
|
-
throw new IOException(
|
|
418
|
+
throw new IOException(
|
|
419
|
+
"Checksum verification failed for: " +
|
|
420
|
+
downloadUrl +
|
|
421
|
+
" " +
|
|
422
|
+
targetFile.getName() +
|
|
423
|
+
" expected: " +
|
|
424
|
+
decryptedExpectedHash +
|
|
425
|
+
" calculated: " +
|
|
426
|
+
calculatedHash
|
|
427
|
+
);
|
|
421
428
|
}
|
|
422
429
|
} catch (Exception e) {
|
|
423
|
-
Log.e(TAG, "Error in downloadAndVerify", e);
|
|
424
430
|
throw new IOException("Error in downloadAndVerify: " + e.getMessage());
|
|
425
431
|
}
|
|
426
432
|
}
|
|
@@ -347,7 +347,7 @@ import UIKit
|
|
|
347
347
|
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("update.dat")
|
|
348
348
|
}
|
|
349
349
|
private var tempData = Data()
|
|
350
|
-
|
|
350
|
+
|
|
351
351
|
private func verifyChecksum(file: URL, expectedHash: String) -> Bool {
|
|
352
352
|
let actualHash = CryptoCipherV2.calcChecksum(filePath: file)
|
|
353
353
|
return actualHash == expectedHash
|
|
@@ -446,9 +446,9 @@ import UIKit
|
|
|
446
446
|
let statusCode = response.response?.statusCode ?? 200
|
|
447
447
|
if statusCode < 200 || statusCode >= 300 {
|
|
448
448
|
if let stringData = String(data: data, encoding: .utf8) {
|
|
449
|
-
throw NSError(domain: "StatusCodeError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch. Status code (\(statusCode)) invalid. Data: \(stringData)"])
|
|
449
|
+
throw NSError(domain: "StatusCodeError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch. Status code (\(statusCode)) invalid. Data: \(stringData) for file \(fileName) at url \(downloadUrl)"])
|
|
450
450
|
} else {
|
|
451
|
-
throw NSError(domain: "StatusCodeError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch. Status code (\(statusCode)) invalid"])
|
|
451
|
+
throw NSError(domain: "StatusCodeError", code: 2, userInfo: [NSLocalizedDescriptionKey: "Failed to fetch. Status code (\(statusCode)) invalid for file \(fileName) at url \(downloadUrl)"])
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
|
|
@@ -470,7 +470,7 @@ import UIKit
|
|
|
470
470
|
|
|
471
471
|
// Decompress the Brotli data
|
|
472
472
|
guard let decompressedData = self.decompressBrotli(data: finalData, fileName: fileName) else {
|
|
473
|
-
throw NSError(domain: "BrotliDecompressionError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to decompress Brotli data"])
|
|
473
|
+
throw NSError(domain: "BrotliDecompressionError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to decompress Brotli data for file \(fileName) at url \(downloadUrl)"])
|
|
474
474
|
}
|
|
475
475
|
finalData = decompressedData
|
|
476
476
|
|
|
@@ -479,7 +479,7 @@ import UIKit
|
|
|
479
479
|
// assume that calcChecksum != null
|
|
480
480
|
let calculatedChecksum = CryptoCipherV2.calcChecksum(filePath: destFilePath)
|
|
481
481
|
if calculatedChecksum != fileHash {
|
|
482
|
-
throw NSError(domain: "ChecksumError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Computed checksum is not equal to required checksum (\(calculatedChecksum) != \(fileHash))"])
|
|
482
|
+
throw NSError(domain: "ChecksumError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Computed checksum is not equal to required checksum (\(calculatedChecksum) != \(fileHash)) for file \(fileName) at url \(downloadUrl)"])
|
|
483
483
|
}
|
|
484
484
|
}
|
|
485
485
|
|
|
@@ -491,10 +491,10 @@ import UIKit
|
|
|
491
491
|
print("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) downloaded, decompressed\(!self.publicKey.isEmpty && !sessionKey.isEmpty ? ", decrypted" : ""), and cached")
|
|
492
492
|
} catch {
|
|
493
493
|
downloadError = error
|
|
494
|
-
|
|
494
|
+
NSLog("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) error: \(error.localizedDescription)")
|
|
495
495
|
}
|
|
496
496
|
case .failure(let error):
|
|
497
|
-
|
|
497
|
+
NSLog("\(CapacitorUpdater.TAG) downloadManifest \(id) \(fileName) download error: \(error.localizedDescription). Debug response: \(response.debugDescription).")
|
|
498
498
|
}
|
|
499
499
|
}
|
|
500
500
|
}
|
|
@@ -518,14 +518,43 @@ import UIKit
|
|
|
518
518
|
}
|
|
519
519
|
|
|
520
520
|
private func decompressBrotli(data: Data, fileName: String) -> Data? {
|
|
521
|
+
// Handle empty files
|
|
522
|
+
if data.count == 0 {
|
|
523
|
+
return data
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Handle the special EMPTY_BROTLI_STREAM case
|
|
527
|
+
if data.count == 3 && data[0] == 0x1B && data[1] == 0x00 && data[2] == 0x06 {
|
|
528
|
+
return Data()
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// For small files, check if it's a minimal Brotli wrapper
|
|
532
|
+
if data.count > 3 {
|
|
533
|
+
let maxBytes = min(32, data.count)
|
|
534
|
+
let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
|
|
535
|
+
// Handle our minimal wrapper pattern
|
|
536
|
+
if data[0] == 0x1B && data[1] == 0x00 && data[2] == 0x06 && data.last == 0x03 {
|
|
537
|
+
let range = data.index(data.startIndex, offsetBy: 3)..<data.index(data.endIndex, offsetBy: -1)
|
|
538
|
+
return data[range]
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Handle brotli.compress minimal wrapper (quality 0)
|
|
542
|
+
if data[0] == 0x0b && data[1] == 0x02 && data[2] == 0x80 && data.last == 0x03 {
|
|
543
|
+
let range = data.index(data.startIndex, offsetBy: 3)..<data.index(data.endIndex, offsetBy: -1)
|
|
544
|
+
return data[range]
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// For all other cases, try standard decompression
|
|
521
549
|
let outputBufferSize = 65536
|
|
522
550
|
var outputBuffer = [UInt8](repeating: 0, count: outputBufferSize)
|
|
523
551
|
var decompressedData = Data()
|
|
524
552
|
|
|
525
553
|
let streamPointer = UnsafeMutablePointer<compression_stream>.allocate(capacity: 1)
|
|
526
554
|
var status = compression_stream_init(streamPointer, COMPRESSION_STREAM_DECODE, COMPRESSION_BROTLI)
|
|
555
|
+
|
|
527
556
|
guard status != COMPRESSION_STATUS_ERROR else {
|
|
528
|
-
print("\(CapacitorUpdater.TAG)
|
|
557
|
+
print("\(CapacitorUpdater.TAG) Error: Failed to initialize Brotli stream for \(fileName). Status: \(status)")
|
|
529
558
|
return nil
|
|
530
559
|
}
|
|
531
560
|
|
|
@@ -547,7 +576,7 @@ import UIKit
|
|
|
547
576
|
if let baseAddress = rawBufferPointer.baseAddress {
|
|
548
577
|
streamPointer.pointee.src_ptr = baseAddress.assumingMemoryBound(to: UInt8.self)
|
|
549
578
|
} else {
|
|
550
|
-
print("\(CapacitorUpdater.TAG) Error:
|
|
579
|
+
print("\(CapacitorUpdater.TAG) Error: Failed to get base address for \(fileName)")
|
|
551
580
|
status = COMPRESSION_STATUS_ERROR
|
|
552
581
|
return
|
|
553
582
|
}
|
|
@@ -555,6 +584,9 @@ import UIKit
|
|
|
555
584
|
}
|
|
556
585
|
|
|
557
586
|
if status == COMPRESSION_STATUS_ERROR {
|
|
587
|
+
let maxBytes = min(32, data.count)
|
|
588
|
+
let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
|
|
589
|
+
print("\(CapacitorUpdater.TAG) Error: Brotli decompression failed for \(fileName). First \(maxBytes) bytes: \(hexDump)")
|
|
558
590
|
break
|
|
559
591
|
}
|
|
560
592
|
|
|
@@ -568,15 +600,19 @@ import UIKit
|
|
|
568
600
|
if status == COMPRESSION_STATUS_END {
|
|
569
601
|
break
|
|
570
602
|
} else if status == COMPRESSION_STATUS_ERROR {
|
|
571
|
-
print("\(CapacitorUpdater.TAG) Error
|
|
572
|
-
// Try to decode as text if mostly ASCII
|
|
603
|
+
print("\(CapacitorUpdater.TAG) Error: Brotli process failed for \(fileName). Status: \(status)")
|
|
573
604
|
if let text = String(data: data, encoding: .utf8) {
|
|
574
605
|
let asciiCount = text.unicodeScalars.filter { $0.isASCII }.count
|
|
575
606
|
let totalCount = text.unicodeScalars.count
|
|
576
607
|
if totalCount > 0 && Double(asciiCount) / Double(totalCount) >= 0.8 {
|
|
577
|
-
print("\(CapacitorUpdater.TAG)
|
|
608
|
+
print("\(CapacitorUpdater.TAG) Error: Input appears to be plain text: \(text)")
|
|
578
609
|
}
|
|
579
610
|
}
|
|
611
|
+
|
|
612
|
+
let maxBytes = min(32, data.count)
|
|
613
|
+
let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
|
|
614
|
+
print("\(CapacitorUpdater.TAG) Error: Raw data (\(fileName)): \(hexDump)")
|
|
615
|
+
|
|
580
616
|
return nil
|
|
581
617
|
}
|
|
582
618
|
|
|
@@ -586,6 +622,7 @@ import UIKit
|
|
|
586
622
|
}
|
|
587
623
|
|
|
588
624
|
if input.count == 0 {
|
|
625
|
+
print("\(CapacitorUpdater.TAG) Error: Zero input size for \(fileName)")
|
|
589
626
|
break
|
|
590
627
|
}
|
|
591
628
|
}
|
|
@@ -45,7 +45,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
45
45
|
CAPPluginMethod(name: "getNextBundle", returnType: CAPPluginReturnPromise)
|
|
46
46
|
]
|
|
47
47
|
public var implementation = CapacitorUpdater()
|
|
48
|
-
private let PLUGIN_VERSION: String = "7.0.
|
|
48
|
+
private let PLUGIN_VERSION: String = "7.0.22"
|
|
49
49
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
50
50
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
51
51
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|