@capgo/capacitor-updater 8.1.0 → 8.2.1
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 +7 -2
- package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +22 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +19 -0
- package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +7 -2
- package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +18 -1
- package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +19 -0
- package/ios/Sources/CapacitorUpdaterPlugin/InternalUtils.swift +1 -0
- 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 = "8.
|
|
75
|
+
private final String pluginVersion = "8.2.1";
|
|
76
76
|
private static final String DELAY_CONDITION_PREFERENCES = "";
|
|
77
77
|
|
|
78
78
|
private SharedPreferences.Editor editor;
|
|
@@ -304,7 +304,12 @@ public class CapacitorUpdaterPlugin extends Plugin {
|
|
|
304
304
|
this.persistCustomId = this.getConfig().getBoolean("persistCustomId", false);
|
|
305
305
|
this.persistModifyUrl = this.getConfig().getBoolean("persistModifyUrl", false);
|
|
306
306
|
this.allowSetDefaultChannel = this.getConfig().getBoolean("allowSetDefaultChannel", true);
|
|
307
|
-
this.implementation.
|
|
307
|
+
this.implementation.setPublicKey(this.getConfig().getString("publicKey", ""));
|
|
308
|
+
// Log public key prefix if encryption is enabled
|
|
309
|
+
String keyId = this.implementation.getKeyId();
|
|
310
|
+
if (keyId != null && !keyId.isEmpty()) {
|
|
311
|
+
logger.info("Public key prefix: " + keyId);
|
|
312
|
+
}
|
|
308
313
|
this.implementation.statsUrl = this.getConfig().getString("statsUrl", statsUrlDefault);
|
|
309
314
|
this.implementation.channelUrl = this.getConfig().getString("channelUrl", channelUrlDefault);
|
|
310
315
|
if (Boolean.TRUE.equals(this.persistModifyUrl)) {
|
|
@@ -81,6 +81,9 @@ public class CapgoUpdater {
|
|
|
81
81
|
public String deviceID = "";
|
|
82
82
|
public int timeout = 20000;
|
|
83
83
|
|
|
84
|
+
// Cached key ID calculated once from publicKey
|
|
85
|
+
private String cachedKeyId = "";
|
|
86
|
+
|
|
84
87
|
// Flag to track if we received a 429 response - stops requests until app restart
|
|
85
88
|
private static volatile boolean rateLimitExceeded = false;
|
|
86
89
|
|
|
@@ -145,6 +148,19 @@ public class CapgoUpdater {
|
|
|
145
148
|
return sb.toString();
|
|
146
149
|
}
|
|
147
150
|
|
|
151
|
+
public void setPublicKey(String publicKey) {
|
|
152
|
+
this.publicKey = publicKey;
|
|
153
|
+
if (!publicKey.isEmpty()) {
|
|
154
|
+
this.cachedKeyId = CryptoCipher.calcKeyId(publicKey);
|
|
155
|
+
} else {
|
|
156
|
+
this.cachedKeyId = "";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public String getKeyId() {
|
|
161
|
+
return this.cachedKeyId;
|
|
162
|
+
}
|
|
163
|
+
|
|
148
164
|
private File unzip(final String id, final File zipFile, final String dest) throws IOException {
|
|
149
165
|
final File targetDirectory = new File(this.documentsDir, dest);
|
|
150
166
|
try (
|
|
@@ -806,6 +822,12 @@ public class CapgoUpdater {
|
|
|
806
822
|
json.put("is_emulator", this.isEmulator());
|
|
807
823
|
json.put("is_prod", this.isProd());
|
|
808
824
|
json.put("defaultChannel", this.defaultChannel);
|
|
825
|
+
|
|
826
|
+
// Add encryption key ID if encryption is enabled (use cached value)
|
|
827
|
+
if (!this.cachedKeyId.isEmpty()) {
|
|
828
|
+
json.put("key_id", this.cachedKeyId);
|
|
829
|
+
}
|
|
830
|
+
|
|
809
831
|
return json;
|
|
810
832
|
}
|
|
811
833
|
|
|
@@ -359,4 +359,23 @@ public class CryptoCipher {
|
|
|
359
359
|
|
|
360
360
|
throw new IllegalArgumentException("size too large, only up to 64KiB length encoding supported: " + size);
|
|
361
361
|
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Get first 4 characters of the public key for identification.
|
|
365
|
+
* Returns 4-character string or empty string if key is invalid/empty.
|
|
366
|
+
*/
|
|
367
|
+
public static String calcKeyId(String publicKey) {
|
|
368
|
+
if (publicKey == null || publicKey.isEmpty()) {
|
|
369
|
+
return "";
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Remove PEM headers and whitespace to get the raw key data
|
|
373
|
+
String cleanedKey = publicKey
|
|
374
|
+
.replaceAll("\\s+", "")
|
|
375
|
+
.replace("-----BEGINRSAPUBLICKEY-----", "")
|
|
376
|
+
.replace("-----ENDRSAPUBLICKEY-----", "");
|
|
377
|
+
|
|
378
|
+
// Return first 4 characters of the base64-encoded key
|
|
379
|
+
return cleanedKey.length() >= 4 ? cleanedKey.substring(0, 4) : cleanedKey;
|
|
380
|
+
}
|
|
362
381
|
}
|
|
@@ -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 = "8.
|
|
57
|
+
private let pluginVersion: String = "8.2.1"
|
|
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"
|
|
@@ -196,7 +196,7 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
196
196
|
periodCheckDelay = periodCheckDelayValue
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
implementation.
|
|
199
|
+
implementation.setPublicKey(getConfig().getString("publicKey") ?? "")
|
|
200
200
|
implementation.notifyDownloadRaw = notifyDownload
|
|
201
201
|
implementation.pluginVersion = self.pluginVersion
|
|
202
202
|
|
|
@@ -204,6 +204,11 @@ public class CapacitorUpdaterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
204
204
|
implementation.setLogger(logger)
|
|
205
205
|
CryptoCipher.setLogger(logger)
|
|
206
206
|
|
|
207
|
+
// Log public key prefix if encryption is enabled
|
|
208
|
+
if let keyId = implementation.getKeyId(), !keyId.isEmpty {
|
|
209
|
+
logger.info("Public key prefix: \(keyId)")
|
|
210
|
+
}
|
|
211
|
+
|
|
207
212
|
// Initialize DelayUpdateUtils
|
|
208
213
|
self.delayUpdateUtils = DelayUpdateUtils(currentVersionNative: currentVersionNative, logger: logger)
|
|
209
214
|
let config = (self.bridge?.viewController as? CAPBridgeViewController)?.instanceDescriptor().legacyConfig
|
|
@@ -42,6 +42,9 @@ import UIKit
|
|
|
42
42
|
public var deviceID = ""
|
|
43
43
|
public var publicKey: String = ""
|
|
44
44
|
|
|
45
|
+
// Cached key ID calculated once from publicKey
|
|
46
|
+
private var cachedKeyId: String?
|
|
47
|
+
|
|
45
48
|
// Flag to track if we received a 429 response - stops requests until app restart
|
|
46
49
|
private static var rateLimitExceeded = false
|
|
47
50
|
|
|
@@ -79,6 +82,19 @@ import UIKit
|
|
|
79
82
|
return String((0..<length).map { _ in letters.randomElement()! })
|
|
80
83
|
}
|
|
81
84
|
|
|
85
|
+
public func setPublicKey(_ publicKey: String) {
|
|
86
|
+
self.publicKey = publicKey
|
|
87
|
+
if !publicKey.isEmpty {
|
|
88
|
+
self.cachedKeyId = CryptoCipher.calcKeyId(publicKey: publicKey)
|
|
89
|
+
} else {
|
|
90
|
+
self.cachedKeyId = nil
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public func getKeyId() -> String? {
|
|
95
|
+
return self.cachedKeyId
|
|
96
|
+
}
|
|
97
|
+
|
|
82
98
|
private var isDevEnvironment: Bool {
|
|
83
99
|
#if DEBUG
|
|
84
100
|
return true
|
|
@@ -324,7 +340,8 @@ import UIKit
|
|
|
324
340
|
is_prod: self.isProd(),
|
|
325
341
|
action: nil,
|
|
326
342
|
channel: nil,
|
|
327
|
-
defaultChannel: self.defaultChannel
|
|
343
|
+
defaultChannel: self.defaultChannel,
|
|
344
|
+
key_id: self.cachedKeyId
|
|
328
345
|
)
|
|
329
346
|
}
|
|
330
347
|
|
|
@@ -264,4 +264,23 @@ public struct CryptoCipher {
|
|
|
264
264
|
throw CustomError.cannotDecode
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
|
+
|
|
268
|
+
/// Get first 4 characters of the public key for identification
|
|
269
|
+
/// Returns 4-character string or empty string if key is invalid/empty
|
|
270
|
+
public static func calcKeyId(publicKey: String) -> String {
|
|
271
|
+
if publicKey.isEmpty {
|
|
272
|
+
return ""
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Remove PEM headers and whitespace to get the raw key data
|
|
276
|
+
let cleanedKey = publicKey
|
|
277
|
+
.replacingOccurrences(of: "-----BEGIN RSA PUBLIC KEY-----", with: "")
|
|
278
|
+
.replacingOccurrences(of: "-----END RSA PUBLIC KEY-----", with: "")
|
|
279
|
+
.replacingOccurrences(of: "\n", with: "")
|
|
280
|
+
.replacingOccurrences(of: "\r", with: "")
|
|
281
|
+
.replacingOccurrences(of: " ", with: "")
|
|
282
|
+
|
|
283
|
+
// Return first 4 characters of the base64-encoded key
|
|
284
|
+
return String(cleanedKey.prefix(4))
|
|
285
|
+
}
|
|
267
286
|
}
|