@capgo/capacitor-updater 6.40.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/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1 -1
- 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/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +1 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +99 -51
- package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +33 -18
- package/package.json +1 -1
|
@@ -85,7 +85,7 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
85
85
|
private static final String[] BREAKING_EVENT_NAMES = { "breakingAvailable", "majorAvailable" };
|
|
86
86
|
private static final String LAST_FAILED_BUNDLE_PREF_KEY = "CapacitorUpdater.lastFailedBundle";
|
|
87
87
|
|
|
88
|
-
private final String pluginVersion = "6.40.
|
|
88
|
+
private final String pluginVersion = "6.40.2";
|
|
89
89
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
90
90
|
|
|
91
91
|
private SharedPreferences.Editor editor;
|
|
@@ -181,7 +181,8 @@ public class CapgoUpdater {
|
|
|
181
181
|
ZipEntry entry;
|
|
182
182
|
while ((entry = zis.getNextEntry()) != null) {
|
|
183
183
|
if (entry.getName().contains("\\")) {
|
|
184
|
-
logger.error("
|
|
184
|
+
logger.error("Unzip failed: Windows path not supported");
|
|
185
|
+
logger.debug("Invalid path: " + entry.getName());
|
|
185
186
|
this.sendStats("windows_path_fail");
|
|
186
187
|
}
|
|
187
188
|
final File file = new File(targetDirectory, entry.getName());
|
|
@@ -277,7 +278,8 @@ public class CapgoUpdater {
|
|
|
277
278
|
boolean success = finishDownload(id, dest, version, sessionKey, checksum, true, isManifest);
|
|
278
279
|
BundleInfo resultBundle;
|
|
279
280
|
if (!success) {
|
|
280
|
-
logger.error("Finish download failed
|
|
281
|
+
logger.error("Finish download failed");
|
|
282
|
+
logger.debug("Version: " + version);
|
|
281
283
|
resultBundle = new BundleInfo(
|
|
282
284
|
id,
|
|
283
285
|
version,
|
|
@@ -309,7 +311,8 @@ public class CapgoUpdater {
|
|
|
309
311
|
case FAILED:
|
|
310
312
|
Data failedData = workInfo.getOutputData();
|
|
311
313
|
String error = failedData.getString(DownloadService.ERROR);
|
|
312
|
-
logger.error("Download failed
|
|
314
|
+
logger.error("Download failed");
|
|
315
|
+
logger.debug("Error: " + error + ", State: " + workInfo.getState());
|
|
313
316
|
String failedVersion = failedData.getString(DownloadService.VERSION);
|
|
314
317
|
|
|
315
318
|
io.execute(() -> {
|
|
@@ -428,7 +431,8 @@ public class CapgoUpdater {
|
|
|
428
431
|
CryptoCipher.logChecksumInfo("Calculated checksum", checksum);
|
|
429
432
|
CryptoCipher.logChecksumInfo("Expected checksum", checksumDecrypted);
|
|
430
433
|
if ((!checksumDecrypted.isEmpty() || !this.publicKey.isEmpty()) && !checksumDecrypted.equals(checksum)) {
|
|
431
|
-
logger.error("
|
|
434
|
+
logger.error("Checksum mismatch");
|
|
435
|
+
logger.debug("Expected: " + checksumDecrypted + ", Got: " + checksum);
|
|
432
436
|
this.sendStats("checksum_fail");
|
|
433
437
|
throw new IOException("Checksum failed: " + id);
|
|
434
438
|
}
|
|
@@ -440,7 +444,8 @@ public class CapgoUpdater {
|
|
|
440
444
|
}
|
|
441
445
|
final Boolean res = this.delete(id);
|
|
442
446
|
if (!res) {
|
|
443
|
-
logger.info("
|
|
447
|
+
logger.info("Failed to cleanup after error");
|
|
448
|
+
logger.debug("Version: " + version);
|
|
444
449
|
}
|
|
445
450
|
|
|
446
451
|
final Map<String, Object> ret = new HashMap<>();
|
|
@@ -541,7 +546,8 @@ public class CapgoUpdater {
|
|
|
541
546
|
this.deleteDirectory(cacheFolder, threadToCheck);
|
|
542
547
|
logger.info("Cleaned up delta cache folder");
|
|
543
548
|
} catch (IOException e) {
|
|
544
|
-
logger.error("Failed to cleanup delta cache
|
|
549
|
+
logger.error("Failed to cleanup delta cache");
|
|
550
|
+
logger.debug("Error: " + e.getMessage());
|
|
545
551
|
}
|
|
546
552
|
}
|
|
547
553
|
|
|
@@ -582,9 +588,11 @@ public class CapgoUpdater {
|
|
|
582
588
|
try {
|
|
583
589
|
this.deleteDirectory(entry, threadToCheck);
|
|
584
590
|
this.removeBundleInfo(id);
|
|
585
|
-
logger.info("Deleted orphan bundle directory
|
|
591
|
+
logger.info("Deleted orphan bundle directory");
|
|
592
|
+
logger.debug("Bundle ID: " + id);
|
|
586
593
|
} catch (IOException e) {
|
|
587
|
-
logger.error("Failed to delete orphan bundle directory
|
|
594
|
+
logger.error("Failed to delete orphan bundle directory");
|
|
595
|
+
logger.debug("Bundle ID: " + id + ", Error: " + e.getMessage());
|
|
588
596
|
}
|
|
589
597
|
}
|
|
590
598
|
}
|
|
@@ -621,9 +629,11 @@ public class CapgoUpdater {
|
|
|
621
629
|
|
|
622
630
|
try {
|
|
623
631
|
this.deleteDirectory(entry, threadToCheck);
|
|
624
|
-
logger.info("Deleted orphaned temp unzip folder
|
|
632
|
+
logger.info("Deleted orphaned temp unzip folder");
|
|
633
|
+
logger.debug("Folder: " + folderName);
|
|
625
634
|
} catch (IOException e) {
|
|
626
|
-
logger.error("Failed to delete orphaned temp folder
|
|
635
|
+
logger.error("Failed to delete orphaned temp folder");
|
|
636
|
+
logger.debug("Folder: " + folderName + ", Error: " + e.getMessage());
|
|
627
637
|
}
|
|
628
638
|
}
|
|
629
639
|
}
|
|
@@ -710,7 +720,8 @@ public class CapgoUpdater {
|
|
|
710
720
|
} catch (Exception e) {
|
|
711
721
|
// Clean up on failure
|
|
712
722
|
downloadFutures.remove(id);
|
|
713
|
-
logger.error("Error waiting for download
|
|
723
|
+
logger.error("Error waiting for download");
|
|
724
|
+
logger.debug("Error: " + e.getMessage());
|
|
714
725
|
BundleInfo errorBundle = new BundleInfo(id, version, BundleStatus.ERROR, new Date(System.currentTimeMillis()), "");
|
|
715
726
|
saveBundleInfo(id, errorBundle);
|
|
716
727
|
if (e instanceof IOException) {
|
|
@@ -750,12 +761,14 @@ public class CapgoUpdater {
|
|
|
750
761
|
public Boolean delete(final String id, final Boolean removeInfo) throws IOException {
|
|
751
762
|
final BundleInfo deleted = this.getBundleInfo(id);
|
|
752
763
|
if (deleted.isBuiltin() || this.getCurrentBundleId().equals(id)) {
|
|
753
|
-
logger.error("Cannot delete
|
|
764
|
+
logger.error("Cannot delete current or builtin bundle");
|
|
765
|
+
logger.debug("Bundle ID: " + id);
|
|
754
766
|
return false;
|
|
755
767
|
}
|
|
756
768
|
final BundleInfo next = this.getNextBundle();
|
|
757
769
|
if (next != null && !next.isDeleted() && !next.isErrorStatus() && next.getId().equals(id)) {
|
|
758
|
-
logger.error("Cannot delete the next bundle"
|
|
770
|
+
logger.error("Cannot delete the next bundle");
|
|
771
|
+
logger.debug("Bundle ID: " + id);
|
|
759
772
|
return false;
|
|
760
773
|
}
|
|
761
774
|
// Cancel download for this version if active
|
|
@@ -772,7 +785,8 @@ public class CapgoUpdater {
|
|
|
772
785
|
}
|
|
773
786
|
return true;
|
|
774
787
|
}
|
|
775
|
-
logger.
|
|
788
|
+
logger.info("Bundle not found on disk");
|
|
789
|
+
logger.debug("Version: " + deleted.getVersionName());
|
|
776
790
|
// perhaps we did not find the bundle in the files, but if the user requested a delete, we delete
|
|
777
791
|
if (removeInfo) {
|
|
778
792
|
this.removeBundleInfo(id);
|
|
@@ -943,11 +957,13 @@ public class CapgoUpdater {
|
|
|
943
957
|
if (response.isSuccessful()) {
|
|
944
958
|
logger.info("Rate limit statistic sent");
|
|
945
959
|
} else {
|
|
946
|
-
logger.error("Error sending rate limit statistic
|
|
960
|
+
logger.error("Error sending rate limit statistic");
|
|
961
|
+
logger.debug("Response code: " + response.code());
|
|
947
962
|
}
|
|
948
963
|
}
|
|
949
964
|
} catch (final Exception e) {
|
|
950
|
-
logger.error("Failed to send rate limit statistic
|
|
965
|
+
logger.error("Failed to send rate limit statistic");
|
|
966
|
+
logger.debug("Error: " + e.getMessage());
|
|
951
967
|
}
|
|
952
968
|
}
|
|
953
969
|
|
|
@@ -1044,7 +1060,8 @@ public class CapgoUpdater {
|
|
|
1044
1060
|
json.put("defaultChannel", channel);
|
|
1045
1061
|
}
|
|
1046
1062
|
} catch (JSONException e) {
|
|
1047
|
-
logger.error("Error
|
|
1063
|
+
logger.error("Error getting latest version");
|
|
1064
|
+
logger.debug("JSONException: " + e.getMessage());
|
|
1048
1065
|
final Map<String, Object> retError = new HashMap<>();
|
|
1049
1066
|
retError.put("message", "Cannot get info: " + e);
|
|
1050
1067
|
retError.put("error", "json_error");
|
|
@@ -1116,7 +1133,8 @@ public class CapgoUpdater {
|
|
|
1116
1133
|
json = this.createInfoObject();
|
|
1117
1134
|
json.put("channel", channel);
|
|
1118
1135
|
} catch (JSONException e) {
|
|
1119
|
-
logger.error("Error
|
|
1136
|
+
logger.error("Error setting channel");
|
|
1137
|
+
logger.debug("JSONException: " + e.getMessage());
|
|
1120
1138
|
final Map<String, Object> retError = new HashMap<>();
|
|
1121
1139
|
retError.put("message", "Cannot get info: " + e);
|
|
1122
1140
|
retError.put("error", "json_error");
|
|
@@ -1162,7 +1180,8 @@ public class CapgoUpdater {
|
|
|
1162
1180
|
try {
|
|
1163
1181
|
json = this.createInfoObject();
|
|
1164
1182
|
} catch (JSONException e) {
|
|
1165
|
-
logger.error("Error
|
|
1183
|
+
logger.error("Error getting channel");
|
|
1184
|
+
logger.debug("JSONException: " + e.getMessage());
|
|
1166
1185
|
final Map<String, Object> retError = new HashMap<>();
|
|
1167
1186
|
retError.put("message", "Cannot get info: " + e);
|
|
1168
1187
|
retError.put("error", "json_error");
|
|
@@ -1284,7 +1303,8 @@ public class CapgoUpdater {
|
|
|
1284
1303
|
try {
|
|
1285
1304
|
json = this.createInfoObject();
|
|
1286
1305
|
} catch (JSONException e) {
|
|
1287
|
-
logger.error("Error creating info object
|
|
1306
|
+
logger.error("Error creating info object");
|
|
1307
|
+
logger.debug("JSONException: " + e.getMessage());
|
|
1288
1308
|
final Map<String, Object> retError = new HashMap<>();
|
|
1289
1309
|
retError.put("message", "Cannot get info: " + e);
|
|
1290
1310
|
retError.put("error", "json_error");
|
|
@@ -1304,7 +1324,8 @@ public class CapgoUpdater {
|
|
|
1304
1324
|
}
|
|
1305
1325
|
}
|
|
1306
1326
|
} catch (JSONException e) {
|
|
1307
|
-
logger.error("Error adding query parameters
|
|
1327
|
+
logger.error("Error adding query parameters");
|
|
1328
|
+
logger.debug("JSONException: " + e.getMessage());
|
|
1308
1329
|
}
|
|
1309
1330
|
|
|
1310
1331
|
Request request = new Request.Builder().url(urlBuilder.build()).get().build();
|
|
@@ -1425,7 +1446,8 @@ public class CapgoUpdater {
|
|
|
1425
1446
|
json.put("old_version_name", oldVersionName);
|
|
1426
1447
|
json.put("action", action);
|
|
1427
1448
|
} catch (JSONException e) {
|
|
1428
|
-
logger.error("Error
|
|
1449
|
+
logger.error("Error preparing stats");
|
|
1450
|
+
logger.debug("JSONException: " + e.getMessage());
|
|
1429
1451
|
return;
|
|
1430
1452
|
}
|
|
1431
1453
|
|
|
@@ -1440,7 +1462,8 @@ public class CapgoUpdater {
|
|
|
1440
1462
|
new okhttp3.Callback() {
|
|
1441
1463
|
@Override
|
|
1442
1464
|
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
1443
|
-
logger.error("Failed to send stats
|
|
1465
|
+
logger.error("Failed to send stats");
|
|
1466
|
+
logger.debug("Error: " + e.getMessage());
|
|
1444
1467
|
}
|
|
1445
1468
|
|
|
1446
1469
|
@Override
|
|
@@ -1452,9 +1475,11 @@ public class CapgoUpdater {
|
|
|
1452
1475
|
}
|
|
1453
1476
|
|
|
1454
1477
|
if (response.isSuccessful()) {
|
|
1455
|
-
logger.info("Stats
|
|
1478
|
+
logger.info("Stats sent successfully");
|
|
1479
|
+
logger.debug("Action: " + action + ", Version: " + versionName);
|
|
1456
1480
|
} else {
|
|
1457
|
-
logger.error("Error sending stats
|
|
1481
|
+
logger.error("Error sending stats");
|
|
1482
|
+
logger.debug("Response code: " + response.code());
|
|
1458
1483
|
}
|
|
1459
1484
|
}
|
|
1460
1485
|
}
|
|
@@ -1481,14 +1506,8 @@ public class CapgoUpdater {
|
|
|
1481
1506
|
result = BundleInfo.fromJSON(stored);
|
|
1482
1507
|
}
|
|
1483
1508
|
} catch (JSONException e) {
|
|
1484
|
-
logger.error(
|
|
1485
|
-
|
|
1486
|
-
trueId +
|
|
1487
|
-
"] stored value: '" +
|
|
1488
|
-
this.prefs.getString(trueId + INFO_SUFFIX, "") +
|
|
1489
|
-
"' error: " +
|
|
1490
|
-
e.getMessage()
|
|
1491
|
-
);
|
|
1509
|
+
logger.error("Failed to parse bundle info");
|
|
1510
|
+
logger.debug("Bundle ID: " + trueId + ", Error: " + e.getMessage());
|
|
1492
1511
|
// Clear corrupted data
|
|
1493
1512
|
this.editor.remove(trueId + INFO_SUFFIX);
|
|
1494
1513
|
this.editor.commit();
|
|
@@ -235,14 +235,11 @@ public class CryptoCipher {
|
|
|
235
235
|
detectedAlgorithm = "SHA-256";
|
|
236
236
|
} else if (decryptedChecksum.length == 4) {
|
|
237
237
|
detectedAlgorithm = "CRC32 (deprecated)";
|
|
238
|
-
logger.error(
|
|
239
|
-
"CRC32 checksum detected. This algorithm is deprecated and no longer supported. Please update your CLI to use SHA-256 checksums."
|
|
240
|
-
);
|
|
238
|
+
logger.error("CRC32 checksum detected - deprecated algorithm");
|
|
241
239
|
} else {
|
|
242
240
|
detectedAlgorithm = "unknown (" + decryptedChecksum.length + " bytes)";
|
|
243
|
-
logger.error(
|
|
244
|
-
|
|
245
|
-
);
|
|
241
|
+
logger.error("Unknown checksum algorithm detected");
|
|
242
|
+
logger.debug("Byte count: " + decryptedChecksum.length + ", Expected: 32 (SHA-256)");
|
|
246
243
|
}
|
|
247
244
|
logger.debug(
|
|
248
245
|
"Decrypted checksum: " +
|
|
@@ -255,7 +252,8 @@ public class CryptoCipher {
|
|
|
255
252
|
);
|
|
256
253
|
return result;
|
|
257
254
|
} catch (GeneralSecurityException e) {
|
|
258
|
-
logger.error("
|
|
255
|
+
logger.error("Checksum decryption failed");
|
|
256
|
+
logger.debug("Error: " + e.getMessage());
|
|
259
257
|
throw new IOException("Decryption failed: " + e.getMessage());
|
|
260
258
|
}
|
|
261
259
|
}
|
|
@@ -286,13 +284,10 @@ public class CryptoCipher {
|
|
|
286
284
|
String algorithm = detectChecksumAlgorithm(hexChecksum);
|
|
287
285
|
logger.debug(label + ": " + algorithm + " hex format (length: " + hexChecksum.length() + " chars)");
|
|
288
286
|
if (algorithm.contains("CRC32")) {
|
|
289
|
-
logger.error(
|
|
290
|
-
"CRC32 checksum detected. This algorithm is deprecated and no longer supported. Please update your CLI to use SHA-256 checksums."
|
|
291
|
-
);
|
|
287
|
+
logger.error("CRC32 checksum detected - deprecated algorithm");
|
|
292
288
|
} else if (algorithm.contains("unknown")) {
|
|
293
|
-
logger.error(
|
|
294
|
-
|
|
295
|
-
);
|
|
289
|
+
logger.error("Unknown checksum algorithm detected");
|
|
290
|
+
logger.debug("Char count: " + hexChecksum.length() + ", Expected: 64 (SHA-256)");
|
|
296
291
|
}
|
|
297
292
|
}
|
|
298
293
|
|
|
@@ -321,7 +316,8 @@ public class CryptoCipher {
|
|
|
321
316
|
}
|
|
322
317
|
return hexString.toString();
|
|
323
318
|
} catch (IOException e) {
|
|
324
|
-
logger.error("Cannot
|
|
319
|
+
logger.error("Cannot calculate checksum");
|
|
320
|
+
logger.debug("Path: " + file.getPath() + ", Error: " + e.getMessage());
|
|
325
321
|
return "";
|
|
326
322
|
}
|
|
327
323
|
}
|
|
@@ -60,7 +60,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
60
60
|
CAPPluginMethod(name: "completeFlexibleUpdate", returnType: CAPPluginReturnPromise)
|
|
61
61
|
]
|
|
62
62
|
public var implementation = CapgoUpdater()
|
|
63
|
-
private let pluginVersion: String = "6.40.
|
|
63
|
+
private let pluginVersion: String = "6.40.2"
|
|
64
64
|
static let updateUrlDefault = "https://plugin.capgo.app/updates"
|
|
65
65
|
static let statsUrlDefault = "https://plugin.capgo.app/stats"
|
|
66
66
|
static let channelUrlDefault = "https://plugin.capgo.app/channel_self"
|
|
@@ -157,7 +157,8 @@ import UIKit
|
|
|
157
157
|
case .success:
|
|
158
158
|
self.logger.info("Rate limit statistic sent")
|
|
159
159
|
case let .failure(error):
|
|
160
|
-
self.logger.error("Error sending rate limit statistic
|
|
160
|
+
self.logger.error("Error sending rate limit statistic")
|
|
161
|
+
self.logger.debug("Error: \(error.localizedDescription)")
|
|
161
162
|
}
|
|
162
163
|
semaphore.signal()
|
|
163
164
|
}
|
|
@@ -203,7 +204,8 @@ import UIKit
|
|
|
203
204
|
do {
|
|
204
205
|
try FileManager.default.createDirectory(atPath: source.path, withIntermediateDirectories: true, attributes: nil)
|
|
205
206
|
} catch {
|
|
206
|
-
logger.error("Cannot
|
|
207
|
+
logger.error("Cannot create directory")
|
|
208
|
+
logger.debug("Directory path: \(source.path)")
|
|
207
209
|
throw CustomError.cannotCreateDirectory
|
|
208
210
|
}
|
|
209
211
|
}
|
|
@@ -213,7 +215,8 @@ import UIKit
|
|
|
213
215
|
do {
|
|
214
216
|
try FileManager.default.removeItem(atPath: source.path)
|
|
215
217
|
} catch {
|
|
216
|
-
logger.error("File not removed
|
|
218
|
+
logger.error("File not removed")
|
|
219
|
+
logger.debug("Path: \(source.path)")
|
|
217
220
|
throw CustomError.cannotDeleteDirectory
|
|
218
221
|
}
|
|
219
222
|
}
|
|
@@ -230,7 +233,8 @@ import UIKit
|
|
|
230
233
|
return false
|
|
231
234
|
}
|
|
232
235
|
} catch {
|
|
233
|
-
logger.error("File not moved
|
|
236
|
+
logger.error("File not moved")
|
|
237
|
+
logger.debug("Source: \(source.path), Dest: \(dest.path)")
|
|
234
238
|
throw CustomError.cannotUnflat
|
|
235
239
|
}
|
|
236
240
|
}
|
|
@@ -238,7 +242,8 @@ import UIKit
|
|
|
238
242
|
private func validateZipEntry(path: String, destUnZip: URL) throws {
|
|
239
243
|
// Check for Windows paths
|
|
240
244
|
if path.contains("\\") {
|
|
241
|
-
logger.error("
|
|
245
|
+
logger.error("Unzip failed: Windows path not supported")
|
|
246
|
+
logger.debug("Invalid path: \(path)")
|
|
242
247
|
self.sendStats(action: "windows_path_fail")
|
|
243
248
|
throw CustomError.cannotUnzip
|
|
244
249
|
}
|
|
@@ -320,7 +325,8 @@ import UIKit
|
|
|
320
325
|
try FileManager.default.removeItem(at: sourceZip)
|
|
321
326
|
}
|
|
322
327
|
} catch {
|
|
323
|
-
logger.error("Could not delete source zip
|
|
328
|
+
logger.error("Could not delete source zip")
|
|
329
|
+
logger.debug("Path: \(sourceZip.path), Error: \(error)")
|
|
324
330
|
}
|
|
325
331
|
}
|
|
326
332
|
|
|
@@ -395,8 +401,9 @@ import UIKit
|
|
|
395
401
|
latest.comment = comment
|
|
396
402
|
}
|
|
397
403
|
case let .failure(error):
|
|
398
|
-
self.logger.error("Error getting
|
|
399
|
-
|
|
404
|
+
self.logger.error("Error getting latest version")
|
|
405
|
+
self.logger.debug("Response: \(response.value.debugDescription), Error: \(error)")
|
|
406
|
+
latest.message = "Error getting Latest"
|
|
400
407
|
latest.error = "response_error"
|
|
401
408
|
latest.statusCode = response.response?.statusCode ?? 0
|
|
402
409
|
}
|
|
@@ -464,7 +471,8 @@ import UIKit
|
|
|
464
471
|
fileHash = try CryptoCipher.decryptChecksum(checksum: fileHash, publicKey: self.publicKey)
|
|
465
472
|
} catch {
|
|
466
473
|
downloadError = error
|
|
467
|
-
logger.error("
|
|
474
|
+
logger.error("Checksum decryption failed")
|
|
475
|
+
logger.debug("Bundle: \(id), File: \(fileName), Error: \(error)")
|
|
468
476
|
}
|
|
469
477
|
} else if self.hasOldPrivateKeyPropertyInConfig {
|
|
470
478
|
// V1 Encryption (privateKey) - deprecated but supported
|
|
@@ -496,7 +504,8 @@ import UIKit
|
|
|
496
504
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
497
505
|
} catch {
|
|
498
506
|
downloadError = error
|
|
499
|
-
logger.error("Failed to copy builtin file
|
|
507
|
+
logger.error("Failed to copy builtin file")
|
|
508
|
+
logger.debug("File: \(fileName), Error: \(error.localizedDescription)")
|
|
500
509
|
}
|
|
501
510
|
dispatchGroup.leave()
|
|
502
511
|
} else if self.tryCopyFromCache(from: cacheFilePath, to: destFilePath, expectedHash: fileHash) {
|
|
@@ -570,15 +579,18 @@ import UIKit
|
|
|
570
579
|
|
|
571
580
|
completedFiles += 1
|
|
572
581
|
self.notifyDownload(id: id, percent: self.calcTotalPercent(percent: Int((Double(completedFiles) / Double(totalFiles)) * 100), min: 10, max: 70))
|
|
573
|
-
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)")
|
|
574
584
|
} catch {
|
|
575
585
|
downloadError = error
|
|
576
|
-
self.logger.error("
|
|
586
|
+
self.logger.error("Manifest file download failed")
|
|
587
|
+
self.logger.debug("Bundle: \(id), File: \(fileName), Error: \(error.localizedDescription)")
|
|
577
588
|
}
|
|
578
589
|
case .failure(let error):
|
|
579
590
|
downloadError = error
|
|
580
591
|
self.sendStats(action: "download_manifest_file_fail", versionName: "\(version):\(fileName)")
|
|
581
|
-
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)")
|
|
582
594
|
}
|
|
583
595
|
}
|
|
584
596
|
}
|
|
@@ -666,7 +678,8 @@ import UIKit
|
|
|
666
678
|
var status = compression_stream_init(streamPointer, COMPRESSION_STREAM_DECODE, COMPRESSION_BROTLI)
|
|
667
679
|
|
|
668
680
|
guard status != COMPRESSION_STATUS_ERROR else {
|
|
669
|
-
logger.error("
|
|
681
|
+
logger.error("Failed to initialize Brotli stream")
|
|
682
|
+
logger.debug("File: \(fileName), Status: \(status)")
|
|
670
683
|
return nil
|
|
671
684
|
}
|
|
672
685
|
|
|
@@ -688,7 +701,8 @@ import UIKit
|
|
|
688
701
|
if let baseAddress = rawBufferPointer.baseAddress {
|
|
689
702
|
streamPointer.pointee.src_ptr = baseAddress.assumingMemoryBound(to: UInt8.self)
|
|
690
703
|
} else {
|
|
691
|
-
logger.error("
|
|
704
|
+
logger.error("Failed to get base address for Brotli decompression")
|
|
705
|
+
logger.debug("File: \(fileName)")
|
|
692
706
|
status = COMPRESSION_STATUS_ERROR
|
|
693
707
|
return
|
|
694
708
|
}
|
|
@@ -698,7 +712,8 @@ import UIKit
|
|
|
698
712
|
if status == COMPRESSION_STATUS_ERROR {
|
|
699
713
|
let maxBytes = min(32, data.count)
|
|
700
714
|
let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
|
|
701
|
-
logger.error("
|
|
715
|
+
logger.error("Brotli decompression failed")
|
|
716
|
+
logger.debug("File: \(fileName), First \(maxBytes) bytes: \(hexDump)")
|
|
702
717
|
break
|
|
703
718
|
}
|
|
704
719
|
|
|
@@ -712,18 +727,19 @@ import UIKit
|
|
|
712
727
|
if status == COMPRESSION_STATUS_END {
|
|
713
728
|
break
|
|
714
729
|
} else if status == COMPRESSION_STATUS_ERROR {
|
|
715
|
-
logger.error("
|
|
730
|
+
logger.error("Brotli process failed")
|
|
731
|
+
logger.debug("File: \(fileName), Status: \(status)")
|
|
716
732
|
if let text = String(data: data, encoding: .utf8) {
|
|
717
733
|
let asciiCount = text.unicodeScalars.filter { $0.isASCII }.count
|
|
718
734
|
let totalCount = text.unicodeScalars.count
|
|
719
735
|
if totalCount > 0 && Double(asciiCount) / Double(totalCount) >= 0.8 {
|
|
720
|
-
logger.
|
|
736
|
+
logger.debug("Input appears to be plain text: \(text)")
|
|
721
737
|
}
|
|
722
738
|
}
|
|
723
739
|
|
|
724
740
|
let maxBytes = min(32, data.count)
|
|
725
741
|
let hexDump = data.prefix(maxBytes).map { String(format: "%02x", $0) }.joined(separator: " ")
|
|
726
|
-
logger.
|
|
742
|
+
logger.debug("Raw data: \(hexDump)")
|
|
727
743
|
|
|
728
744
|
return nil
|
|
729
745
|
}
|
|
@@ -734,7 +750,8 @@ import UIKit
|
|
|
734
750
|
}
|
|
735
751
|
|
|
736
752
|
if input.count == 0 {
|
|
737
|
-
logger.error("
|
|
753
|
+
logger.error("Zero input size for Brotli decompression")
|
|
754
|
+
logger.debug("File: \(fileName)")
|
|
738
755
|
break
|
|
739
756
|
}
|
|
740
757
|
}
|
|
@@ -826,7 +843,8 @@ import UIKit
|
|
|
826
843
|
reachabilityManager?.stopListening()
|
|
827
844
|
|
|
828
845
|
if mainError != nil {
|
|
829
|
-
logger.error("Failed to download
|
|
846
|
+
logger.error("Failed to download bundle")
|
|
847
|
+
logger.debug("Error: \(String(describing: mainError))")
|
|
830
848
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum, link: link, comment: comment))
|
|
831
849
|
throw mainError!
|
|
832
850
|
}
|
|
@@ -837,7 +855,8 @@ import UIKit
|
|
|
837
855
|
try CryptoCipher.decryptFile(filePath: tempDataPath, publicKey: self.publicKey, sessionKey: sessionKey, version: version)
|
|
838
856
|
try FileManager.default.moveItem(at: tempDataPath, to: finalPath)
|
|
839
857
|
} catch {
|
|
840
|
-
logger.error("Failed decrypt file
|
|
858
|
+
logger.error("Failed to decrypt file")
|
|
859
|
+
logger.debug("Error: \(error)")
|
|
841
860
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum, link: link, comment: comment))
|
|
842
861
|
cleanDownloadData()
|
|
843
862
|
throw error
|
|
@@ -850,7 +869,8 @@ import UIKit
|
|
|
850
869
|
try self.saveDownloaded(sourceZip: finalPath, id: id, base: self.libraryDir.appendingPathComponent(self.bundleDirectory), notify: true)
|
|
851
870
|
|
|
852
871
|
} catch {
|
|
853
|
-
logger.error("Failed to unzip file
|
|
872
|
+
logger.error("Failed to unzip file")
|
|
873
|
+
logger.debug("Error: \(error)")
|
|
854
874
|
self.saveBundleInfo(id: id, bundle: BundleInfo(id: id, version: version, status: BundleStatus.ERROR, downloaded: Date(), checksum: checksum, link: link, comment: comment))
|
|
855
875
|
// Best-effort cleanup of the decrypted zip file when unzip fails
|
|
856
876
|
do {
|
|
@@ -858,7 +878,8 @@ import UIKit
|
|
|
858
878
|
try FileManager.default.removeItem(at: finalPath)
|
|
859
879
|
}
|
|
860
880
|
} catch {
|
|
861
|
-
logger.error("Could not delete failed zip
|
|
881
|
+
logger.error("Could not delete failed zip")
|
|
882
|
+
logger.debug("Path: \(finalPath.path), Error: \(error)")
|
|
862
883
|
}
|
|
863
884
|
cleanDownloadData()
|
|
864
885
|
throw error
|
|
@@ -881,13 +902,15 @@ import UIKit
|
|
|
881
902
|
let fileManager = FileManager.default
|
|
882
903
|
if !fileManager.fileExists(atPath: tempDataPath.path) {
|
|
883
904
|
if !fileManager.createFile(atPath: tempDataPath.path, contents: Data()) {
|
|
884
|
-
logger.error("Cannot ensure
|
|
905
|
+
logger.error("Cannot ensure temp data file exists")
|
|
906
|
+
logger.debug("Path: \(tempDataPath.path)")
|
|
885
907
|
}
|
|
886
908
|
}
|
|
887
909
|
|
|
888
910
|
if !fileManager.fileExists(atPath: updateInfo.path) {
|
|
889
911
|
if !fileManager.createFile(atPath: updateInfo.path, contents: Data()) {
|
|
890
|
-
logger.error("Cannot ensure
|
|
912
|
+
logger.error("Cannot ensure update info file exists")
|
|
913
|
+
logger.debug("Path: \(updateInfo.path)")
|
|
891
914
|
}
|
|
892
915
|
}
|
|
893
916
|
}
|
|
@@ -899,7 +922,8 @@ import UIKit
|
|
|
899
922
|
do {
|
|
900
923
|
try fileManager.removeItem(at: tempDataPath)
|
|
901
924
|
} catch {
|
|
902
|
-
logger.error("Could not delete
|
|
925
|
+
logger.error("Could not delete temp data file")
|
|
926
|
+
logger.debug("Path: \(tempDataPath), Error: \(error)")
|
|
903
927
|
}
|
|
904
928
|
}
|
|
905
929
|
// Deleting update.dat
|
|
@@ -907,7 +931,8 @@ import UIKit
|
|
|
907
931
|
do {
|
|
908
932
|
try fileManager.removeItem(at: updateInfo)
|
|
909
933
|
} catch {
|
|
910
|
-
logger.error("Could not delete
|
|
934
|
+
logger.error("Could not delete update info file")
|
|
935
|
+
logger.debug("Path: \(updateInfo), Error: \(error)")
|
|
911
936
|
}
|
|
912
937
|
}
|
|
913
938
|
}
|
|
@@ -926,7 +951,8 @@ import UIKit
|
|
|
926
951
|
fileHandle.closeFile()
|
|
927
952
|
}
|
|
928
953
|
} catch {
|
|
929
|
-
logger.error("Failed to write data
|
|
954
|
+
logger.error("Failed to write partial data")
|
|
955
|
+
logger.debug("Byte offset: \(byteOffset), Error: \(error)")
|
|
930
956
|
}
|
|
931
957
|
self.tempData.removeAll() // Clearing tempData to avoid writing the same data multiple times
|
|
932
958
|
}
|
|
@@ -935,7 +961,8 @@ import UIKit
|
|
|
935
961
|
do {
|
|
936
962
|
try "\(version)".write(to: updateInfo, atomically: true, encoding: .utf8)
|
|
937
963
|
} catch {
|
|
938
|
-
logger.error("Failed to save progress
|
|
964
|
+
logger.error("Failed to save download progress")
|
|
965
|
+
logger.debug("Error: \(error)")
|
|
939
966
|
}
|
|
940
967
|
}
|
|
941
968
|
private func getLocalUpdateVersion() -> String { // Return the version that was tried to be downloaded on last download attempt
|
|
@@ -957,7 +984,8 @@ import UIKit
|
|
|
957
984
|
return fileSize.int64Value
|
|
958
985
|
}
|
|
959
986
|
} catch {
|
|
960
|
-
logger.error("Could not retrieve
|
|
987
|
+
logger.error("Could not retrieve download progress size")
|
|
988
|
+
logger.debug("Error: \(error)")
|
|
961
989
|
}
|
|
962
990
|
return 0
|
|
963
991
|
}
|
|
@@ -1001,7 +1029,8 @@ import UIKit
|
|
|
1001
1029
|
public func delete(id: String, removeInfo: Bool) -> Bool {
|
|
1002
1030
|
let deleted: BundleInfo = self.getBundleInfo(id: id)
|
|
1003
1031
|
if deleted.isBuiltin() || self.getCurrentBundleId() == id {
|
|
1004
|
-
logger.info("Cannot delete
|
|
1032
|
+
logger.info("Cannot delete current or builtin bundle")
|
|
1033
|
+
logger.debug("Bundle ID: \(id)")
|
|
1005
1034
|
return false
|
|
1006
1035
|
}
|
|
1007
1036
|
|
|
@@ -1010,7 +1039,8 @@ import UIKit
|
|
|
1010
1039
|
!next.isDeleted() &&
|
|
1011
1040
|
!next.isErrorStatus() &&
|
|
1012
1041
|
next.getId() == id {
|
|
1013
|
-
logger.info("Cannot delete the next bundle
|
|
1042
|
+
logger.info("Cannot delete the next bundle")
|
|
1043
|
+
logger.debug("Bundle ID: \(id)")
|
|
1014
1044
|
return false
|
|
1015
1045
|
}
|
|
1016
1046
|
|
|
@@ -1018,7 +1048,8 @@ import UIKit
|
|
|
1018
1048
|
do {
|
|
1019
1049
|
try FileManager.default.removeItem(atPath: destPersist.path)
|
|
1020
1050
|
} catch {
|
|
1021
|
-
logger.error("
|
|
1051
|
+
logger.error("Bundle folder not removed")
|
|
1052
|
+
logger.debug("Path: \(destPersist.path)")
|
|
1022
1053
|
// even if, we don;t care. Android doesn't care
|
|
1023
1054
|
if removeInfo {
|
|
1024
1055
|
self.removeBundleInfo(id: id)
|
|
@@ -1031,7 +1062,8 @@ import UIKit
|
|
|
1031
1062
|
} else {
|
|
1032
1063
|
self.saveBundleInfo(id: id, bundle: deleted.setStatus(status: BundleStatus.DELETED.localizedString))
|
|
1033
1064
|
}
|
|
1034
|
-
logger.info("
|
|
1065
|
+
logger.info("Bundle deleted successfully")
|
|
1066
|
+
logger.debug("Version: \(deleted.getVersionName())")
|
|
1035
1067
|
self.sendStats(action: "delete", versionName: deleted.getVersionName())
|
|
1036
1068
|
return true
|
|
1037
1069
|
}
|
|
@@ -1059,7 +1091,8 @@ import UIKit
|
|
|
1059
1091
|
try fileManager.removeItem(at: cacheFolder)
|
|
1060
1092
|
logger.info("Cleaned up delta cache folder")
|
|
1061
1093
|
} catch {
|
|
1062
|
-
logger.error("Failed to cleanup delta cache
|
|
1094
|
+
logger.error("Failed to cleanup delta cache")
|
|
1095
|
+
logger.debug("Error: \(error.localizedDescription)")
|
|
1063
1096
|
}
|
|
1064
1097
|
}
|
|
1065
1098
|
|
|
@@ -1099,13 +1132,16 @@ import UIKit
|
|
|
1099
1132
|
do {
|
|
1100
1133
|
try fileManager.removeItem(at: url)
|
|
1101
1134
|
self.removeBundleInfo(id: id)
|
|
1102
|
-
logger.info("Deleted orphan bundle directory
|
|
1135
|
+
logger.info("Deleted orphan bundle directory")
|
|
1136
|
+
logger.debug("Bundle ID: \(id)")
|
|
1103
1137
|
} catch {
|
|
1104
|
-
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)")
|
|
1105
1140
|
}
|
|
1106
1141
|
}
|
|
1107
1142
|
} catch {
|
|
1108
|
-
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)")
|
|
1109
1145
|
}
|
|
1110
1146
|
}
|
|
1111
1147
|
|
|
@@ -1136,13 +1172,16 @@ import UIKit
|
|
|
1136
1172
|
|
|
1137
1173
|
do {
|
|
1138
1174
|
try fileManager.removeItem(at: url)
|
|
1139
|
-
logger.info("Deleted orphaned temp unzip folder
|
|
1175
|
+
logger.info("Deleted orphaned temp unzip folder")
|
|
1176
|
+
logger.debug("Folder: \(folderName)")
|
|
1140
1177
|
} catch {
|
|
1141
|
-
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)")
|
|
1142
1180
|
}
|
|
1143
1181
|
}
|
|
1144
1182
|
} catch {
|
|
1145
|
-
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)")
|
|
1146
1185
|
}
|
|
1147
1186
|
}
|
|
1148
1187
|
|
|
@@ -1218,9 +1257,11 @@ import UIKit
|
|
|
1218
1257
|
if autoDeletePrevious && !fallback.isBuiltin() && fallback.getId() != bundle.getId() {
|
|
1219
1258
|
let res = self.delete(id: fallback.getId())
|
|
1220
1259
|
if res {
|
|
1221
|
-
logger.info("Deleted previous bundle
|
|
1260
|
+
logger.info("Deleted previous bundle")
|
|
1261
|
+
logger.debug("Bundle: \(fallback.toString())")
|
|
1222
1262
|
} else {
|
|
1223
|
-
logger.error("Failed to delete previous bundle
|
|
1263
|
+
logger.error("Failed to delete previous bundle")
|
|
1264
|
+
logger.debug("Bundle: \(fallback.toString())")
|
|
1224
1265
|
}
|
|
1225
1266
|
}
|
|
1226
1267
|
self.setFallbackBundle(fallback: bundle)
|
|
@@ -1301,7 +1342,8 @@ import UIKit
|
|
|
1301
1342
|
}
|
|
1302
1343
|
}
|
|
1303
1344
|
case let .failure(error):
|
|
1304
|
-
self.logger.error("Error
|
|
1345
|
+
self.logger.error("Error setting channel")
|
|
1346
|
+
self.logger.debug("Error: \(error)")
|
|
1305
1347
|
setChannel.error = "Request failed: \(error.localizedDescription)"
|
|
1306
1348
|
}
|
|
1307
1349
|
semaphore.signal()
|
|
@@ -1364,7 +1406,8 @@ import UIKit
|
|
|
1364
1406
|
}
|
|
1365
1407
|
}
|
|
1366
1408
|
|
|
1367
|
-
self.logger.error("Error
|
|
1409
|
+
self.logger.error("Error getting channel")
|
|
1410
|
+
self.logger.debug("Error: \(error)")
|
|
1368
1411
|
getChannel.error = "Request failed: \(error.localizedDescription)"
|
|
1369
1412
|
}
|
|
1370
1413
|
}
|
|
@@ -1454,7 +1497,8 @@ import UIKit
|
|
|
1454
1497
|
}
|
|
1455
1498
|
}
|
|
1456
1499
|
case let .failure(error):
|
|
1457
|
-
self.logger.error("Error
|
|
1500
|
+
self.logger.error("Error listing channels")
|
|
1501
|
+
self.logger.debug("Error: \(error)")
|
|
1458
1502
|
listChannels.error = "Request failed: \(error.localizedDescription)"
|
|
1459
1503
|
}
|
|
1460
1504
|
}
|
|
@@ -1500,9 +1544,11 @@ import UIKit
|
|
|
1500
1544
|
|
|
1501
1545
|
switch response.result {
|
|
1502
1546
|
case .success:
|
|
1503
|
-
self.logger.info("Stats sent
|
|
1547
|
+
self.logger.info("Stats sent successfully")
|
|
1548
|
+
self.logger.debug("Action: \(action), Version: \(versionName)")
|
|
1504
1549
|
case let .failure(error):
|
|
1505
|
-
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)")
|
|
1506
1552
|
}
|
|
1507
1553
|
semaphore.signal()
|
|
1508
1554
|
}
|
|
@@ -1526,7 +1572,8 @@ import UIKit
|
|
|
1526
1572
|
do {
|
|
1527
1573
|
result = try UserDefaults.standard.getObj(forKey: "\(trueId)\(self.INFO_SUFFIX)", castTo: BundleInfo.self)
|
|
1528
1574
|
} catch {
|
|
1529
|
-
logger.error("Failed to parse
|
|
1575
|
+
logger.error("Failed to parse bundle info")
|
|
1576
|
+
logger.debug("Bundle ID: \(trueId), Error: \(error.localizedDescription)")
|
|
1530
1577
|
result = BundleInfo(id: trueId, version: "", status: BundleStatus.PENDING, checksum: "")
|
|
1531
1578
|
}
|
|
1532
1579
|
}
|
|
@@ -1561,7 +1608,8 @@ import UIKit
|
|
|
1561
1608
|
do {
|
|
1562
1609
|
try UserDefaults.standard.setObj(update, forKey: "\(id)\(self.INFO_SUFFIX)")
|
|
1563
1610
|
} catch {
|
|
1564
|
-
logger.error("Failed to save
|
|
1611
|
+
logger.error("Failed to save bundle info")
|
|
1612
|
+
logger.debug("Bundle ID: \(id), Error: \(error.localizedDescription)")
|
|
1565
1613
|
}
|
|
1566
1614
|
}
|
|
1567
1615
|
UserDefaults.standard.synchronize()
|
|
@@ -45,7 +45,8 @@ public struct CryptoCipher {
|
|
|
45
45
|
if isHexString(checksum) {
|
|
46
46
|
// Hex encoded (new format from CLI for plugin versions >= 5.30.0, 6.30.0, 7.30.0)
|
|
47
47
|
guard let hexData = hexStringToData(checksum) else {
|
|
48
|
-
logger.error("Cannot decode checksum as hex
|
|
48
|
+
logger.error("Cannot decode checksum as hex")
|
|
49
|
+
logger.debug("Checksum value: \(checksum)")
|
|
49
50
|
throw CustomError.cannotDecode
|
|
50
51
|
}
|
|
51
52
|
checksumBytes = hexData
|
|
@@ -54,7 +55,8 @@ public struct CryptoCipher {
|
|
|
54
55
|
// TODO: remove backwards compatibility
|
|
55
56
|
// Base64 encoded (old format for backwards compatibility)
|
|
56
57
|
guard let base64Data = Data(base64Encoded: checksum) else {
|
|
57
|
-
logger.error("Cannot decode checksum as base64
|
|
58
|
+
logger.error("Cannot decode checksum as base64")
|
|
59
|
+
logger.debug("Checksum value: \(checksum)")
|
|
58
60
|
throw CustomError.cannotDecode
|
|
59
61
|
}
|
|
60
62
|
checksumBytes = base64Data
|
|
@@ -86,15 +88,17 @@ public struct CryptoCipher {
|
|
|
86
88
|
detectedAlgorithm = "SHA-256"
|
|
87
89
|
} else if decryptedChecksum.count == 4 {
|
|
88
90
|
detectedAlgorithm = "CRC32 (deprecated)"
|
|
89
|
-
logger.error("CRC32 checksum detected
|
|
91
|
+
logger.error("CRC32 checksum detected - deprecated algorithm")
|
|
90
92
|
} else {
|
|
91
93
|
detectedAlgorithm = "unknown (\(decryptedChecksum.count) bytes)"
|
|
92
|
-
logger.error("Unknown checksum algorithm detected
|
|
94
|
+
logger.error("Unknown checksum algorithm detected")
|
|
95
|
+
logger.debug("Byte count: \(decryptedChecksum.count), Expected: 32 (SHA-256)")
|
|
93
96
|
}
|
|
94
97
|
logger.debug("Decrypted checksum: \(detectedAlgorithm) hex format (length: \(result.count) chars, \(decryptedChecksum.count) bytes)")
|
|
95
98
|
return result
|
|
96
99
|
} catch {
|
|
97
|
-
logger.error("
|
|
100
|
+
logger.error("Checksum decryption failed")
|
|
101
|
+
logger.debug("Error: \(error.localizedDescription)")
|
|
98
102
|
throw CustomError.cannotDecode
|
|
99
103
|
}
|
|
100
104
|
}
|
|
@@ -121,9 +125,10 @@ public struct CryptoCipher {
|
|
|
121
125
|
let algorithm = detectChecksumAlgorithm(hexChecksum)
|
|
122
126
|
logger.debug("\(label): \(algorithm) hex format (length: \(hexChecksum.count) chars)")
|
|
123
127
|
if algorithm.contains("CRC32") {
|
|
124
|
-
logger.error("CRC32 checksum detected
|
|
128
|
+
logger.error("CRC32 checksum detected - deprecated algorithm")
|
|
125
129
|
} else if algorithm.contains("unknown") {
|
|
126
|
-
logger.error("Unknown checksum algorithm detected
|
|
130
|
+
logger.error("Unknown checksum algorithm detected")
|
|
131
|
+
logger.debug("Char count: \(hexChecksum.count), Expected: 64 (SHA-256)")
|
|
127
132
|
}
|
|
128
133
|
}
|
|
129
134
|
|
|
@@ -136,7 +141,8 @@ public struct CryptoCipher {
|
|
|
136
141
|
do {
|
|
137
142
|
fileHandle = try FileHandle(forReadingFrom: filePath)
|
|
138
143
|
} catch {
|
|
139
|
-
logger.error("Cannot open file for checksum
|
|
144
|
+
logger.error("Cannot open file for checksum calculation")
|
|
145
|
+
logger.debug("Path: \(filePath.path), Error: \(error)")
|
|
140
146
|
return ""
|
|
141
147
|
}
|
|
142
148
|
|
|
@@ -144,7 +150,8 @@ public struct CryptoCipher {
|
|
|
144
150
|
do {
|
|
145
151
|
try fileHandle.close()
|
|
146
152
|
} catch {
|
|
147
|
-
logger.error("Error closing file
|
|
153
|
+
logger.error("Error closing file during checksum")
|
|
154
|
+
logger.debug("Error: \(error)")
|
|
148
155
|
}
|
|
149
156
|
}
|
|
150
157
|
|
|
@@ -153,7 +160,8 @@ public struct CryptoCipher {
|
|
|
153
160
|
do {
|
|
154
161
|
fileData = try fileHandle.read(upToCount: bufferSize) ?? Data()
|
|
155
162
|
} catch {
|
|
156
|
-
logger.error("Error reading file
|
|
163
|
+
logger.error("Error reading file during checksum")
|
|
164
|
+
logger.debug("Error: \(error)")
|
|
157
165
|
return false
|
|
158
166
|
}
|
|
159
167
|
|
|
@@ -168,7 +176,8 @@ public struct CryptoCipher {
|
|
|
168
176
|
let digest = sha256.finalize()
|
|
169
177
|
return digest.compactMap { String(format: "%02x", $0) }.joined()
|
|
170
178
|
} catch {
|
|
171
|
-
logger.error("Cannot
|
|
179
|
+
logger.error("Cannot calculate checksum")
|
|
180
|
+
logger.debug("Path: \(filePath.path), Error: \(error)")
|
|
172
181
|
return ""
|
|
173
182
|
}
|
|
174
183
|
}
|
|
@@ -195,17 +204,20 @@ public struct CryptoCipher {
|
|
|
195
204
|
let encryptedKeyBase64 = sessionKeyComponents[1]
|
|
196
205
|
|
|
197
206
|
guard let ivData = Data(base64Encoded: ivBase64) else {
|
|
198
|
-
logger.error("Cannot decode sessionKey IV
|
|
207
|
+
logger.error("Cannot decode sessionKey IV")
|
|
208
|
+
logger.debug("IV value: \(ivBase64)")
|
|
199
209
|
throw CustomError.cannotDecode
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
if ivData.count != 16 {
|
|
203
|
-
logger.error("IV data has invalid length
|
|
213
|
+
logger.error("IV data has invalid length")
|
|
214
|
+
logger.debug("Length: \(ivData.count), Expected: 16")
|
|
204
215
|
throw CustomError.cannotDecode
|
|
205
216
|
}
|
|
206
217
|
|
|
207
218
|
guard let sessionKeyDataEncrypted = Data(base64Encoded: encryptedKeyBase64) else {
|
|
208
|
-
logger.error("Cannot decode sessionKey data
|
|
219
|
+
logger.error("Cannot decode sessionKey data")
|
|
220
|
+
logger.debug("Key value: \(encryptedKeyBase64)")
|
|
209
221
|
throw NSError(domain: "Invalid session key data", code: 1, userInfo: nil)
|
|
210
222
|
}
|
|
211
223
|
|
|
@@ -215,7 +227,8 @@ public struct CryptoCipher {
|
|
|
215
227
|
}
|
|
216
228
|
|
|
217
229
|
if sessionKeyDataDecrypted.count != 16 {
|
|
218
|
-
logger.error("Decrypted session key has invalid length
|
|
230
|
+
logger.error("Decrypted session key has invalid length")
|
|
231
|
+
logger.debug("Length: \(sessionKeyDataDecrypted.count), Expected: 16")
|
|
219
232
|
throw NSError(domain: "Invalid decrypted session key", code: 5, userInfo: nil)
|
|
220
233
|
}
|
|
221
234
|
|
|
@@ -225,7 +238,8 @@ public struct CryptoCipher {
|
|
|
225
238
|
do {
|
|
226
239
|
encryptedData = try Data(contentsOf: filePath)
|
|
227
240
|
} catch {
|
|
228
|
-
logger.error("Failed to read encrypted data
|
|
241
|
+
logger.error("Failed to read encrypted data")
|
|
242
|
+
logger.debug("Error: \(error)")
|
|
229
243
|
throw NSError(domain: "Failed to read encrypted data", code: 3, userInfo: nil)
|
|
230
244
|
}
|
|
231
245
|
|
|
@@ -251,12 +265,13 @@ public struct CryptoCipher {
|
|
|
251
265
|
throw NSError(domain: "File write failed", code: 8, userInfo: nil)
|
|
252
266
|
}
|
|
253
267
|
} catch {
|
|
254
|
-
logger.error("Error writing decrypted file
|
|
268
|
+
logger.error("Error writing decrypted file")
|
|
269
|
+
logger.debug("Error: \(error)")
|
|
255
270
|
throw error
|
|
256
271
|
}
|
|
257
272
|
|
|
258
273
|
} catch {
|
|
259
|
-
logger.error("
|
|
274
|
+
logger.error("File decryption failed")
|
|
260
275
|
throw CustomError.cannotDecode
|
|
261
276
|
}
|
|
262
277
|
}
|