@capgo/capacitor-updater 5.0.0-alpha.0 → 7.0.0
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/CapgoCapacitorUpdater.podspec +1 -1
- package/LICENCE +373 -661
- package/README.md +339 -75
- package/android/build.gradle +13 -12
- package/android/src/main/AndroidManifest.xml +4 -2
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +205 -121
- package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +32 -24
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdater.java +1041 -441
- package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +1217 -536
- package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +153 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +62 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DelayUntilNext.java +14 -0
- package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +126 -0
- package/dist/docs.json +727 -171
- package/dist/esm/definitions.d.ts +234 -45
- package/dist/esm/definitions.js +5 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +9 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/web.d.ts +12 -6
- package/dist/esm/web.js +64 -20
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +70 -23
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +70 -23
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/BundleInfo.swift +38 -19
- package/ios/Plugin/BundleStatus.swift +11 -4
- package/ios/Plugin/CapacitorUpdater.swift +520 -192
- package/ios/Plugin/CapacitorUpdaterPlugin.m +8 -1
- package/ios/Plugin/CapacitorUpdaterPlugin.swift +447 -190
- package/ios/Plugin/CryptoCipher.swift +240 -0
- package/ios/Plugin/DelayCondition.swift +74 -0
- package/ios/Plugin/DelayUntilNext.swift +30 -0
- package/ios/Plugin/UserDefaultsExtension.swift +48 -0
- package/package.json +26 -20
- package/ios/Plugin/ObjectPreferences.swift +0 -97
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
package ee.forgr.capacitor_updater;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Created by Awesometic
|
|
11
|
+
* It's encrypt returns Base64 encoded, and also decrypt for Base64 encoded cipher
|
|
12
|
+
* references: http://stackoverflow.com/questions/12471999/rsa-encryption-decryption-in-android
|
|
13
|
+
*/
|
|
14
|
+
import android.util.Base64;
|
|
15
|
+
import java.security.GeneralSecurityException;
|
|
16
|
+
import java.security.InvalidAlgorithmParameterException;
|
|
17
|
+
import java.security.InvalidKeyException;
|
|
18
|
+
import java.security.KeyFactory;
|
|
19
|
+
import java.security.NoSuchAlgorithmException;
|
|
20
|
+
import java.security.PrivateKey;
|
|
21
|
+
import java.security.spec.InvalidKeySpecException;
|
|
22
|
+
import java.security.spec.MGF1ParameterSpec;
|
|
23
|
+
import java.security.spec.PKCS8EncodedKeySpec;
|
|
24
|
+
import javax.crypto.BadPaddingException;
|
|
25
|
+
import javax.crypto.Cipher;
|
|
26
|
+
import javax.crypto.IllegalBlockSizeException;
|
|
27
|
+
import javax.crypto.NoSuchPaddingException;
|
|
28
|
+
import javax.crypto.SecretKey;
|
|
29
|
+
import javax.crypto.spec.IvParameterSpec;
|
|
30
|
+
import javax.crypto.spec.OAEPParameterSpec;
|
|
31
|
+
import javax.crypto.spec.PSource;
|
|
32
|
+
import javax.crypto.spec.SecretKeySpec;
|
|
33
|
+
|
|
34
|
+
public class CryptoCipher {
|
|
35
|
+
|
|
36
|
+
public static byte[] decryptRSA(byte[] source, PrivateKey privateKey)
|
|
37
|
+
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
|
38
|
+
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
|
|
39
|
+
OAEPParameterSpec oaepParams = new OAEPParameterSpec(
|
|
40
|
+
"SHA-256",
|
|
41
|
+
"MGF1",
|
|
42
|
+
new MGF1ParameterSpec("SHA-256"),
|
|
43
|
+
PSource.PSpecified.DEFAULT
|
|
44
|
+
);
|
|
45
|
+
cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
|
|
46
|
+
byte[] decryptedBytes = cipher.doFinal(source);
|
|
47
|
+
return decryptedBytes;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public static byte[] decryptAES(byte[] cipherText, SecretKey key, byte[] iv) {
|
|
51
|
+
try {
|
|
52
|
+
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
|
53
|
+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
|
54
|
+
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
|
|
55
|
+
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
|
|
56
|
+
byte[] decryptedText = cipher.doFinal(cipherText);
|
|
57
|
+
return decryptedText;
|
|
58
|
+
} catch (Exception e) {
|
|
59
|
+
e.printStackTrace();
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public static SecretKey byteToSessionKey(byte[] sessionKey) {
|
|
65
|
+
// rebuild key using SecretKeySpec
|
|
66
|
+
SecretKey originalKey = new SecretKeySpec(
|
|
67
|
+
sessionKey,
|
|
68
|
+
0,
|
|
69
|
+
sessionKey.length,
|
|
70
|
+
"AES"
|
|
71
|
+
);
|
|
72
|
+
return originalKey;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private static PrivateKey readPkcs8PrivateKey(byte[] pkcs8Bytes)
|
|
76
|
+
throws GeneralSecurityException {
|
|
77
|
+
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
|
78
|
+
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8Bytes);
|
|
79
|
+
try {
|
|
80
|
+
return keyFactory.generatePrivate(keySpec);
|
|
81
|
+
} catch (InvalidKeySpecException e) {
|
|
82
|
+
throw new IllegalArgumentException("Unexpected key format!", e);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private static byte[] join(byte[] byteArray1, byte[] byteArray2) {
|
|
87
|
+
byte[] bytes = new byte[byteArray1.length + byteArray2.length];
|
|
88
|
+
System.arraycopy(byteArray1, 0, bytes, 0, byteArray1.length);
|
|
89
|
+
System.arraycopy(
|
|
90
|
+
byteArray2,
|
|
91
|
+
0,
|
|
92
|
+
bytes,
|
|
93
|
+
byteArray1.length,
|
|
94
|
+
byteArray2.length
|
|
95
|
+
);
|
|
96
|
+
return bytes;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private static PrivateKey readPkcs1PrivateKey(byte[] pkcs1Bytes)
|
|
100
|
+
throws GeneralSecurityException {
|
|
101
|
+
// We can't use Java internal APIs to parse ASN.1 structures, so we build a PKCS#8 key Java can understand
|
|
102
|
+
int pkcs1Length = pkcs1Bytes.length;
|
|
103
|
+
int totalLength = pkcs1Length + 22;
|
|
104
|
+
byte[] pkcs8Header = new byte[] {
|
|
105
|
+
0x30,
|
|
106
|
+
(byte) 0x82,
|
|
107
|
+
(byte) ((totalLength >> 8) & 0xff),
|
|
108
|
+
(byte) (totalLength & 0xff), // Sequence + total length
|
|
109
|
+
0x2,
|
|
110
|
+
0x1,
|
|
111
|
+
0x0, // Integer (0)
|
|
112
|
+
0x30,
|
|
113
|
+
0xD,
|
|
114
|
+
0x6,
|
|
115
|
+
0x9,
|
|
116
|
+
0x2A,
|
|
117
|
+
(byte) 0x86,
|
|
118
|
+
0x48,
|
|
119
|
+
(byte) 0x86,
|
|
120
|
+
(byte) 0xF7,
|
|
121
|
+
0xD,
|
|
122
|
+
0x1,
|
|
123
|
+
0x1,
|
|
124
|
+
0x1,
|
|
125
|
+
0x5,
|
|
126
|
+
0x0, // Sequence: 1.2.840.113549.1.1.1, NULL
|
|
127
|
+
0x4,
|
|
128
|
+
(byte) 0x82,
|
|
129
|
+
(byte) ((pkcs1Length >> 8) & 0xff),
|
|
130
|
+
(byte) (pkcs1Length & 0xff), // Octet string + length
|
|
131
|
+
};
|
|
132
|
+
byte[] pkcs8bytes = join(pkcs8Header, pkcs1Bytes);
|
|
133
|
+
return readPkcs8PrivateKey(pkcs8bytes);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public static PrivateKey stringToPrivateKey(String private_key)
|
|
137
|
+
throws GeneralSecurityException {
|
|
138
|
+
// Base64 decode the result
|
|
139
|
+
|
|
140
|
+
String pkcs1Pem = private_key.toString();
|
|
141
|
+
pkcs1Pem = pkcs1Pem.replace("-----BEGIN RSA PRIVATE KEY-----", "");
|
|
142
|
+
pkcs1Pem = pkcs1Pem.replace("-----END RSA PRIVATE KEY-----", "");
|
|
143
|
+
pkcs1Pem = pkcs1Pem.replace("\\n", "");
|
|
144
|
+
pkcs1Pem = pkcs1Pem.replace(" ", "");
|
|
145
|
+
|
|
146
|
+
byte[] pkcs1EncodedBytes = Base64.decode(
|
|
147
|
+
pkcs1Pem.getBytes(),
|
|
148
|
+
Base64.DEFAULT
|
|
149
|
+
);
|
|
150
|
+
// extract the private key
|
|
151
|
+
return readPkcs1PrivateKey(pkcs1EncodedBytes);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
package ee.forgr.capacitor_updater;
|
|
8
|
+
|
|
9
|
+
import com.google.gson.annotations.SerializedName;
|
|
10
|
+
import java.util.Objects;
|
|
11
|
+
|
|
12
|
+
public class DelayCondition {
|
|
13
|
+
|
|
14
|
+
@SerializedName("kind")
|
|
15
|
+
private DelayUntilNext kind;
|
|
16
|
+
|
|
17
|
+
@SerializedName("value")
|
|
18
|
+
private String value;
|
|
19
|
+
|
|
20
|
+
public DelayCondition(DelayUntilNext kind, String value) {
|
|
21
|
+
this.kind = kind;
|
|
22
|
+
this.value = value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public DelayUntilNext getKind() {
|
|
26
|
+
return kind;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public void setKind(DelayUntilNext kind) {
|
|
30
|
+
this.kind = kind;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public String getValue() {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public void setValue(String value) {
|
|
38
|
+
this.value = value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Override
|
|
42
|
+
public boolean equals(Object o) {
|
|
43
|
+
if (this == o) return true;
|
|
44
|
+
if (!(o instanceof DelayCondition)) return false;
|
|
45
|
+
DelayCondition that = (DelayCondition) o;
|
|
46
|
+
return (
|
|
47
|
+
getKind() == that.getKind() && Objects.equals(getValue(), that.getValue())
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Override
|
|
52
|
+
public int hashCode() {
|
|
53
|
+
return Objects.hash(getKind(), getValue());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Override
|
|
57
|
+
public String toString() {
|
|
58
|
+
return (
|
|
59
|
+
"DelayCondition{" + "kind=" + kind + ", value='" + value + '\'' + '}'
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
package ee.forgr.capacitor_updater;
|
|
8
|
+
|
|
9
|
+
public enum DelayUntilNext {
|
|
10
|
+
background,
|
|
11
|
+
kill,
|
|
12
|
+
nativeVersion,
|
|
13
|
+
date,
|
|
14
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
package ee.forgr.capacitor_updater;
|
|
8
|
+
|
|
9
|
+
import android.app.IntentService;
|
|
10
|
+
import android.content.Intent;
|
|
11
|
+
import java.io.DataInputStream;
|
|
12
|
+
import java.io.File;
|
|
13
|
+
import java.io.FileOutputStream;
|
|
14
|
+
import java.io.InputStream;
|
|
15
|
+
import java.net.URL;
|
|
16
|
+
import java.net.URLConnection;
|
|
17
|
+
|
|
18
|
+
public class DownloadService extends IntentService {
|
|
19
|
+
|
|
20
|
+
public static final String URL = "URL";
|
|
21
|
+
public static final String ID = "id";
|
|
22
|
+
public static final String PERCENT = "percent";
|
|
23
|
+
public static final String FILEDEST = "filendest";
|
|
24
|
+
public static final String DOCDIR = "docdir";
|
|
25
|
+
public static final String ERROR = "error";
|
|
26
|
+
public static final String VERSION = "version";
|
|
27
|
+
public static final String SESSIONKEY = "sessionkey";
|
|
28
|
+
public static final String CHECKSUM = "checksum";
|
|
29
|
+
public static final String NOTIFICATION = "service receiver";
|
|
30
|
+
public static final String PERCENTDOWNLOAD = "percent receiver";
|
|
31
|
+
|
|
32
|
+
public DownloadService() {
|
|
33
|
+
super("Background DownloadService");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private int calcTotalPercent(
|
|
37
|
+
final int percent,
|
|
38
|
+
final int min,
|
|
39
|
+
final int max
|
|
40
|
+
) {
|
|
41
|
+
return (percent * (max - min)) / 100 + min;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Will be called asynchronously by OS.
|
|
45
|
+
@Override
|
|
46
|
+
protected void onHandleIntent(Intent intent) {
|
|
47
|
+
String url = intent.getStringExtra(URL);
|
|
48
|
+
String id = intent.getStringExtra(ID);
|
|
49
|
+
String documentsDir = intent.getStringExtra(DOCDIR);
|
|
50
|
+
String dest = intent.getStringExtra(FILEDEST);
|
|
51
|
+
String version = intent.getStringExtra(VERSION);
|
|
52
|
+
String sessionKey = intent.getStringExtra(SESSIONKEY);
|
|
53
|
+
String checksum = intent.getStringExtra(CHECKSUM);
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
final URL u = new URL(url);
|
|
57
|
+
final URLConnection connection = u.openConnection();
|
|
58
|
+
final InputStream is = u.openStream();
|
|
59
|
+
final DataInputStream dis = new DataInputStream(is);
|
|
60
|
+
|
|
61
|
+
final File target = new File(documentsDir, dest);
|
|
62
|
+
target.getParentFile().mkdirs();
|
|
63
|
+
target.createNewFile();
|
|
64
|
+
final FileOutputStream fos = new FileOutputStream(target);
|
|
65
|
+
|
|
66
|
+
final long totalLength = connection.getContentLength();
|
|
67
|
+
final int bufferSize = 1024;
|
|
68
|
+
final byte[] buffer = new byte[bufferSize];
|
|
69
|
+
int length;
|
|
70
|
+
|
|
71
|
+
int bytesRead = bufferSize;
|
|
72
|
+
int percent = 0;
|
|
73
|
+
this.notifyDownload(id, 10);
|
|
74
|
+
while ((length = dis.read(buffer)) > 0) {
|
|
75
|
+
fos.write(buffer, 0, length);
|
|
76
|
+
final int newPercent = (int) ((bytesRead * 100) / totalLength);
|
|
77
|
+
if (totalLength > 1 && newPercent != percent) {
|
|
78
|
+
percent = newPercent;
|
|
79
|
+
this.notifyDownload(id, this.calcTotalPercent(percent, 10, 70));
|
|
80
|
+
}
|
|
81
|
+
bytesRead += length;
|
|
82
|
+
}
|
|
83
|
+
publishResults(dest, id, version, checksum, sessionKey, "");
|
|
84
|
+
} catch (Exception e) {
|
|
85
|
+
e.printStackTrace();
|
|
86
|
+
publishResults(
|
|
87
|
+
"",
|
|
88
|
+
id,
|
|
89
|
+
version,
|
|
90
|
+
checksum,
|
|
91
|
+
sessionKey,
|
|
92
|
+
e.getLocalizedMessage()
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private void notifyDownload(String id, int percent) {
|
|
98
|
+
Intent intent = new Intent(PERCENTDOWNLOAD);
|
|
99
|
+
intent.putExtra(ID, id);
|
|
100
|
+
intent.putExtra(PERCENT, percent);
|
|
101
|
+
sendBroadcast(intent);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private void publishResults(
|
|
105
|
+
String dest,
|
|
106
|
+
String id,
|
|
107
|
+
String version,
|
|
108
|
+
String checksum,
|
|
109
|
+
String sessionKey,
|
|
110
|
+
String error
|
|
111
|
+
) {
|
|
112
|
+
Intent intent = new Intent(NOTIFICATION);
|
|
113
|
+
if (dest != null && !dest.isEmpty()) {
|
|
114
|
+
intent.putExtra(FILEDEST, dest);
|
|
115
|
+
}
|
|
116
|
+
if (error != null && !error.isEmpty()) {
|
|
117
|
+
intent.putExtra(ERROR, error);
|
|
118
|
+
}
|
|
119
|
+
intent.putExtra(ID, id);
|
|
120
|
+
intent.putExtra(VERSION, version);
|
|
121
|
+
intent.putExtra(SESSIONKEY, sessionKey);
|
|
122
|
+
intent.putExtra(CHECKSUM, checksum);
|
|
123
|
+
intent.putExtra(ERROR, error);
|
|
124
|
+
sendBroadcast(intent);
|
|
125
|
+
}
|
|
126
|
+
}
|