@capgo/capacitor-updater 7.0.22 → 7.0.26

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.
@@ -14,7 +14,7 @@ Pod::Spec.new do |s|
14
14
  s.ios.deployment_target = '14.0'
15
15
  s.dependency 'Capacitor'
16
16
  s.dependency 'SSZipArchive', '2.4.3'
17
- s.dependency 'Alamofire'
18
- s.dependency 'Version'
17
+ s.dependency 'Alamofire', '5.10.2'
18
+ s.dependency 'Version', '0.8.0'
19
19
  s.swift_version = '5.1'
20
20
  end
@@ -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.22";
60
+ private final String PLUGIN_VERSION = "7.0.26";
61
61
  private static final String DELAY_CONDITION_PREFERENCES = "";
62
62
 
63
63
  private SharedPreferences.Editor editor;
@@ -16,6 +16,7 @@ import java.io.FileInputStream;
16
16
  import java.net.HttpURLConnection;
17
17
  import java.net.URL;
18
18
  import java.nio.channels.FileChannel;
19
+ import java.nio.file.Files;
19
20
  import java.security.MessageDigest;
20
21
  import java.util.ArrayList;
21
22
  import java.util.Arrays;
@@ -162,25 +163,38 @@ public class DownloadService extends Worker {
162
163
  String fileHash = entry.getString("file_hash");
163
164
  String downloadUrl = entry.getString("download_url");
164
165
 
166
+ if (!publicKey.isEmpty() && sessionKey != null && !sessionKey.isEmpty()) {
167
+ try {
168
+ fileHash = CryptoCipherV2.decryptChecksum(fileHash, publicKey);
169
+ } catch (Exception e) {
170
+ Log.e(TAG, "Error decrypting checksum for " + fileName, e);
171
+ hasError.set(true);
172
+ continue;
173
+ }
174
+ }
175
+
176
+ final String finalFileHash = fileHash;
165
177
  File targetFile = new File(destFolder, fileName);
166
- File cacheFile = new File(cacheFolder, fileHash + "_" + new File(fileName).getName());
178
+ File cacheFile = new File(cacheFolder, finalFileHash + "_" + new File(fileName).getName());
167
179
  File builtinFile = new File(builtinFolder, fileName);
168
180
 
169
181
  // Ensure parent directories of the target file exist
170
182
  if (!Objects.requireNonNull(targetFile.getParentFile()).exists() && !targetFile.getParentFile().mkdirs()) {
171
- throw new IOException("Failed to create parent directory for: " + targetFile.getAbsolutePath());
183
+ Log.e(TAG, "Failed to create parent directory for: " + targetFile.getAbsolutePath());
184
+ hasError.set(true);
185
+ continue;
172
186
  }
173
187
 
174
188
  Future<?> future = executor.submit(() -> {
175
189
  try {
176
- if (builtinFile.exists() && verifyChecksum(builtinFile, fileHash)) {
190
+ if (builtinFile.exists() && verifyChecksum(builtinFile, finalFileHash)) {
177
191
  copyFile(builtinFile, targetFile);
178
192
  Log.d(TAG, "using builtin file " + fileName);
179
- } else if (cacheFile.exists() && verifyChecksum(cacheFile, fileHash)) {
193
+ } else if (cacheFile.exists() && verifyChecksum(cacheFile, finalFileHash)) {
180
194
  copyFile(cacheFile, targetFile);
181
195
  Log.d(TAG, "already cached " + fileName);
182
196
  } else {
183
- downloadAndVerify(downloadUrl, targetFile, cacheFile, fileHash, sessionKey, publicKey);
197
+ downloadAndVerify(downloadUrl, targetFile, cacheFile, finalFileHash, sessionKey, publicKey);
184
198
  }
185
199
 
186
200
  long completed = completedFiles.incrementAndGet();
@@ -215,9 +229,11 @@ public class DownloadService extends Worker {
215
229
  }
216
230
 
217
231
  if (hasError.get()) {
232
+ Log.e(TAG, "One or more files failed to download");
218
233
  throw new IOException("One or more files failed to download");
219
234
  }
220
235
  } catch (Exception e) {
236
+ Log.e(TAG, "Error in handleManifestDownload", e);
221
237
  throw new RuntimeException(e.getLocalizedMessage());
222
238
  }
223
239
  }
@@ -392,18 +408,10 @@ public class DownloadService extends Worker {
392
408
  decryptedExpectedHash = CryptoCipherV2.decryptChecksum(decryptedExpectedHash, publicKey);
393
409
  }
394
410
 
395
- // Decompress the file
396
- try (
397
- FileInputStream fis = new FileInputStream(compressedFile);
398
- BrotliInputStream brotliInputStream = new BrotliInputStream(fis);
399
- FileOutputStream fos = new FileOutputStream(targetFile)
400
- ) {
401
- byte[] buffer = new byte[8192];
402
- int len;
403
- while ((len = brotliInputStream.read(buffer)) != -1) {
404
- fos.write(buffer, 0, len);
405
- }
406
- }
411
+ // Use new decompression method
412
+ byte[] compressedData = Files.readAllBytes(compressedFile.toPath());
413
+ byte[] decompressedData = decompressBrotli(compressedData, targetFile.getName());
414
+ Files.write(targetFile.toPath(), decompressedData);
407
415
 
408
416
  // Delete the compressed file
409
417
  compressedFile.delete();
@@ -459,4 +467,63 @@ public class DownloadService extends Worker {
459
467
  }
460
468
  return sb.toString();
461
469
  }
470
+
471
+ private byte[] decompressBrotli(byte[] data, String fileName) throws IOException {
472
+ // Validate input
473
+ if (data == null) {
474
+ Log.e(TAG, "Error: Null data received for " + fileName);
475
+ throw new IOException("Null data received");
476
+ }
477
+
478
+ // Handle empty files
479
+ if (data.length == 0) {
480
+ return new byte[0];
481
+ }
482
+
483
+ // Handle the special EMPTY_BROTLI_STREAM case
484
+ if (data.length == 3 && data[0] == 0x1B && data[1] == 0x00 && data[2] == 0x06) {
485
+ return new byte[0];
486
+ }
487
+
488
+ // For small files, check if it's a minimal Brotli wrapper
489
+ if (data.length > 3) {
490
+ try {
491
+ // Handle our minimal wrapper pattern
492
+ if (data[0] == 0x1B && data[1] == 0x00 && data[2] == 0x06 && data[data.length - 1] == 0x03) {
493
+ return Arrays.copyOfRange(data, 3, data.length - 1);
494
+ }
495
+
496
+ // Handle brotli.compress minimal wrapper (quality 0)
497
+ if (data[0] == 0x0b && data[1] == 0x02 && data[2] == (byte) 0x80 && data[data.length - 1] == 0x03) {
498
+ return Arrays.copyOfRange(data, 3, data.length - 1);
499
+ }
500
+ } catch (ArrayIndexOutOfBoundsException e) {
501
+ Log.e(TAG, "Error: Malformed data for " + fileName);
502
+ throw new IOException("Malformed data structure");
503
+ }
504
+ }
505
+
506
+ // For all other cases, try standard decompression
507
+ try (
508
+ ByteArrayInputStream bis = new ByteArrayInputStream(data);
509
+ BrotliInputStream brotliInputStream = new BrotliInputStream(bis);
510
+ ByteArrayOutputStream bos = new ByteArrayOutputStream()
511
+ ) {
512
+ byte[] buffer = new byte[8192];
513
+ int len;
514
+ while ((len = brotliInputStream.read(buffer)) != -1) {
515
+ bos.write(buffer, 0, len);
516
+ }
517
+ return bos.toByteArray();
518
+ } catch (IOException e) {
519
+ Log.e(TAG, "Error: Brotli process failed for " + fileName + ". Status: " + e.getMessage());
520
+ // Add hex dump for debugging
521
+ StringBuilder hexDump = new StringBuilder();
522
+ for (int i = 0; i < Math.min(32, data.length); i++) {
523
+ hexDump.append(String.format("%02x ", data[i]));
524
+ }
525
+ Log.e(TAG, "Error: Raw data (" + fileName + "): " + hexDump.toString());
526
+ throw e;
527
+ }
528
+ }
462
529
  }
@@ -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.22"
48
+ private let PLUGIN_VERSION: String = "7.0.26"
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"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-updater",
3
- "version": "7.0.22",
3
+ "version": "7.0.26",
4
4
  "license": "MPL-2.0",
5
5
  "description": "Live update for capacitor apps",
6
6
  "main": "dist/plugin.cjs.js",