@swiftpatch/react-native 2.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/README.md +430 -0
- package/android/build.gradle +105 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/swiftpatch/BundleManager.kt +107 -0
- package/android/src/main/java/com/swiftpatch/CrashDetector.kt +79 -0
- package/android/src/main/java/com/swiftpatch/CryptoVerifier.kt +69 -0
- package/android/src/main/java/com/swiftpatch/DownloadManager.kt +120 -0
- package/android/src/main/java/com/swiftpatch/EventQueue.kt +86 -0
- package/android/src/main/java/com/swiftpatch/FileUtils.kt +60 -0
- package/android/src/main/java/com/swiftpatch/PatchApplier.kt +60 -0
- package/android/src/main/java/com/swiftpatch/SignalCrashHandler.kt +84 -0
- package/android/src/main/java/com/swiftpatch/SlotManager.kt +299 -0
- package/android/src/main/java/com/swiftpatch/SwiftPatchModule.kt +630 -0
- package/android/src/main/java/com/swiftpatch/SwiftPatchPackage.kt +21 -0
- package/android/src/main/jni/CMakeLists.txt +12 -0
- package/android/src/main/jni/bspatch.c +188 -0
- package/android/src/main/jni/bspatch.h +57 -0
- package/android/src/main/jni/bspatch_jni.c +28 -0
- package/ios/Libraries/bspatch/bspatch.c +188 -0
- package/ios/Libraries/bspatch/bspatch.h +50 -0
- package/ios/Libraries/bspatch/module.modulemap +4 -0
- package/ios/SwiftPatch/BundleManager.swift +113 -0
- package/ios/SwiftPatch/CrashDetector.swift +71 -0
- package/ios/SwiftPatch/CryptoVerifier.swift +70 -0
- package/ios/SwiftPatch/DownloadManager.swift +125 -0
- package/ios/SwiftPatch/EventQueue.swift +116 -0
- package/ios/SwiftPatch/FileUtils.swift +38 -0
- package/ios/SwiftPatch/PatchApplier.swift +41 -0
- package/ios/SwiftPatch/SignalCrashHandler.swift +129 -0
- package/ios/SwiftPatch/SlotManager.swift +360 -0
- package/ios/SwiftPatch/SwiftPatchModule.m +56 -0
- package/ios/SwiftPatch/SwiftPatchModule.swift +621 -0
- package/lib/commonjs/SwiftPatchCore.js +140 -0
- package/lib/commonjs/SwiftPatchCore.js.map +1 -0
- package/lib/commonjs/SwiftPatchProvider.js +617 -0
- package/lib/commonjs/SwiftPatchProvider.js.map +1 -0
- package/lib/commonjs/constants.js +50 -0
- package/lib/commonjs/constants.js.map +1 -0
- package/lib/commonjs/core/Downloader.js +63 -0
- package/lib/commonjs/core/Downloader.js.map +1 -0
- package/lib/commonjs/core/Installer.js +46 -0
- package/lib/commonjs/core/Installer.js.map +1 -0
- package/lib/commonjs/core/Rollback.js +36 -0
- package/lib/commonjs/core/Rollback.js.map +1 -0
- package/lib/commonjs/core/UpdateChecker.js +57 -0
- package/lib/commonjs/core/UpdateChecker.js.map +1 -0
- package/lib/commonjs/core/Verifier.js +82 -0
- package/lib/commonjs/core/Verifier.js.map +1 -0
- package/lib/commonjs/core/index.js +41 -0
- package/lib/commonjs/core/index.js.map +1 -0
- package/lib/commonjs/index.js +154 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/modal/SwiftPatchModal.js +667 -0
- package/lib/commonjs/modal/SwiftPatchModal.js.map +1 -0
- package/lib/commonjs/modal/useSwiftPatchModal.js +26 -0
- package/lib/commonjs/modal/useSwiftPatchModal.js.map +1 -0
- package/lib/commonjs/native/NativeSwiftPatch.js +85 -0
- package/lib/commonjs/native/NativeSwiftPatch.js.map +1 -0
- package/lib/commonjs/native/NativeSwiftPatchSpec.js +15 -0
- package/lib/commonjs/native/NativeSwiftPatchSpec.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types.js +126 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/useSwiftPatch.js +31 -0
- package/lib/commonjs/useSwiftPatch.js.map +1 -0
- package/lib/commonjs/utils/api.js +206 -0
- package/lib/commonjs/utils/api.js.map +1 -0
- package/lib/commonjs/utils/device.js +23 -0
- package/lib/commonjs/utils/device.js.map +1 -0
- package/lib/commonjs/utils/logger.js +30 -0
- package/lib/commonjs/utils/logger.js.map +1 -0
- package/lib/commonjs/utils/storage.js +31 -0
- package/lib/commonjs/utils/storage.js.map +1 -0
- package/lib/commonjs/withSwiftPatch.js +42 -0
- package/lib/commonjs/withSwiftPatch.js.map +1 -0
- package/lib/module/SwiftPatchCore.js +135 -0
- package/lib/module/SwiftPatchCore.js.map +1 -0
- package/lib/module/SwiftPatchProvider.js +611 -0
- package/lib/module/SwiftPatchProvider.js.map +1 -0
- package/lib/module/constants.js +46 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/core/Downloader.js +57 -0
- package/lib/module/core/Downloader.js.map +1 -0
- package/lib/module/core/Installer.js +41 -0
- package/lib/module/core/Installer.js.map +1 -0
- package/lib/module/core/Rollback.js +31 -0
- package/lib/module/core/Rollback.js.map +1 -0
- package/lib/module/core/UpdateChecker.js +51 -0
- package/lib/module/core/UpdateChecker.js.map +1 -0
- package/lib/module/core/Verifier.js +76 -0
- package/lib/module/core/Verifier.js.map +1 -0
- package/lib/module/core/index.js +8 -0
- package/lib/module/core/index.js.map +1 -0
- package/lib/module/index.js +34 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/modal/SwiftPatchModal.js +661 -0
- package/lib/module/modal/SwiftPatchModal.js.map +1 -0
- package/lib/module/modal/useSwiftPatchModal.js +22 -0
- package/lib/module/modal/useSwiftPatchModal.js.map +1 -0
- package/lib/module/native/NativeSwiftPatch.js +78 -0
- package/lib/module/native/NativeSwiftPatch.js.map +1 -0
- package/lib/module/native/NativeSwiftPatchSpec.js +12 -0
- package/lib/module/native/NativeSwiftPatchSpec.js.map +1 -0
- package/lib/module/types.js +139 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/useSwiftPatch.js +26 -0
- package/lib/module/useSwiftPatch.js.map +1 -0
- package/lib/module/utils/api.js +197 -0
- package/lib/module/utils/api.js.map +1 -0
- package/lib/module/utils/device.js +18 -0
- package/lib/module/utils/device.js.map +1 -0
- package/lib/module/utils/logger.js +26 -0
- package/lib/module/utils/logger.js.map +1 -0
- package/lib/module/utils/storage.js +24 -0
- package/lib/module/utils/storage.js.map +1 -0
- package/lib/module/withSwiftPatch.js +37 -0
- package/lib/module/withSwiftPatch.js.map +1 -0
- package/lib/typescript/SwiftPatchCore.d.ts +64 -0
- package/lib/typescript/SwiftPatchCore.d.ts.map +1 -0
- package/lib/typescript/SwiftPatchProvider.d.ts +22 -0
- package/lib/typescript/SwiftPatchProvider.d.ts.map +1 -0
- package/lib/typescript/constants.d.ts +33 -0
- package/lib/typescript/constants.d.ts.map +1 -0
- package/lib/typescript/core/Downloader.d.ts +34 -0
- package/lib/typescript/core/Downloader.d.ts.map +1 -0
- package/lib/typescript/core/Installer.d.ts +25 -0
- package/lib/typescript/core/Installer.d.ts.map +1 -0
- package/lib/typescript/core/Rollback.d.ts +18 -0
- package/lib/typescript/core/Rollback.d.ts.map +1 -0
- package/lib/typescript/core/UpdateChecker.d.ts +27 -0
- package/lib/typescript/core/UpdateChecker.d.ts.map +1 -0
- package/lib/typescript/core/Verifier.d.ts +31 -0
- package/lib/typescript/core/Verifier.d.ts.map +1 -0
- package/lib/typescript/core/index.d.ts +8 -0
- package/lib/typescript/core/index.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +13 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/modal/SwiftPatchModal.d.ts +11 -0
- package/lib/typescript/modal/SwiftPatchModal.d.ts.map +1 -0
- package/lib/typescript/modal/useSwiftPatchModal.d.ts +7 -0
- package/lib/typescript/modal/useSwiftPatchModal.d.ts.map +1 -0
- package/lib/typescript/native/NativeSwiftPatch.d.ts +61 -0
- package/lib/typescript/native/NativeSwiftPatch.d.ts.map +1 -0
- package/lib/typescript/native/NativeSwiftPatchSpec.d.ts +67 -0
- package/lib/typescript/native/NativeSwiftPatchSpec.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +266 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/useSwiftPatch.d.ts +12 -0
- package/lib/typescript/useSwiftPatch.d.ts.map +1 -0
- package/lib/typescript/utils/api.d.ts +87 -0
- package/lib/typescript/utils/api.d.ts.map +1 -0
- package/lib/typescript/utils/device.d.ts +9 -0
- package/lib/typescript/utils/device.d.ts.map +1 -0
- package/lib/typescript/utils/logger.d.ts +8 -0
- package/lib/typescript/utils/logger.d.ts.map +1 -0
- package/lib/typescript/utils/storage.d.ts +14 -0
- package/lib/typescript/utils/storage.d.ts.map +1 -0
- package/lib/typescript/withSwiftPatch.d.ts +12 -0
- package/lib/typescript/withSwiftPatch.d.ts.map +1 -0
- package/package.json +99 -0
- package/react-native-swiftpatch.podspec +50 -0
- package/src/SwiftPatchCore.ts +148 -0
- package/src/SwiftPatchProvider.tsx +514 -0
- package/src/constants.ts +49 -0
- package/src/core/Downloader.ts +74 -0
- package/src/core/Installer.ts +38 -0
- package/src/core/Rollback.ts +28 -0
- package/src/core/UpdateChecker.ts +70 -0
- package/src/core/Verifier.ts +92 -0
- package/src/core/index.ts +11 -0
- package/src/index.ts +64 -0
- package/src/modal/SwiftPatchModal.tsx +657 -0
- package/src/modal/useSwiftPatchModal.ts +24 -0
- package/src/native/NativeSwiftPatch.ts +205 -0
- package/src/native/NativeSwiftPatchSpec.ts +139 -0
- package/src/types.ts +336 -0
- package/src/useSwiftPatch.ts +29 -0
- package/src/utils/api.ts +244 -0
- package/src/utils/device.ts +15 -0
- package/src/utils/logger.ts +29 -0
- package/src/utils/storage.ts +23 -0
- package/src/withSwiftPatch.tsx +41 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/*-
|
|
2
|
+
* Copyright 2003-2005 Colin Percival
|
|
3
|
+
* Copyright 2012 Matthew Endsley
|
|
4
|
+
* All rights reserved
|
|
5
|
+
*
|
|
6
|
+
* Redistribution and use in source and binary forms, with or without
|
|
7
|
+
* modification, are permitted providing that the following conditions
|
|
8
|
+
* are met:
|
|
9
|
+
* 1. Redistributions of source code must retain the above copyright
|
|
10
|
+
* notice, this list of conditions and the following disclaimer.
|
|
11
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
|
12
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
13
|
+
* documentation and/or other materials provided with the distribution.
|
|
14
|
+
*
|
|
15
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
16
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
17
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
18
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
19
|
+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
21
|
+
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
22
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
23
|
+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
24
|
+
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
25
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
#include "bspatch.h"
|
|
29
|
+
|
|
30
|
+
#include <stdlib.h>
|
|
31
|
+
#include <stdio.h>
|
|
32
|
+
#include <string.h>
|
|
33
|
+
|
|
34
|
+
static int64_t offtin(uint8_t *buf)
|
|
35
|
+
{
|
|
36
|
+
int64_t y;
|
|
37
|
+
|
|
38
|
+
y = buf[7] & 0x7F;
|
|
39
|
+
y = y * 256; y += buf[6];
|
|
40
|
+
y = y * 256; y += buf[5];
|
|
41
|
+
y = y * 256; y += buf[4];
|
|
42
|
+
y = y * 256; y += buf[3];
|
|
43
|
+
y = y * 256; y += buf[2];
|
|
44
|
+
y = y * 256; y += buf[1];
|
|
45
|
+
y = y * 256; y += buf[0];
|
|
46
|
+
|
|
47
|
+
if (buf[7] & 0x80) y = -y;
|
|
48
|
+
|
|
49
|
+
return y;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
int bspatch(const uint8_t* old_data, int64_t old_size,
|
|
53
|
+
uint8_t** new_data, int64_t* new_size,
|
|
54
|
+
const uint8_t* patch_data, int64_t patch_size)
|
|
55
|
+
{
|
|
56
|
+
int64_t newsize;
|
|
57
|
+
int64_t bzctrllen, bzdatalen;
|
|
58
|
+
uint8_t header[32];
|
|
59
|
+
uint8_t buf[8];
|
|
60
|
+
int64_t oldpos, newpos;
|
|
61
|
+
int64_t ctrl[3];
|
|
62
|
+
int64_t i;
|
|
63
|
+
const uint8_t *ctrl_block, *diff_block, *extra_block;
|
|
64
|
+
uint8_t *new_buf;
|
|
65
|
+
|
|
66
|
+
if (patch_size < 32) return -1;
|
|
67
|
+
|
|
68
|
+
/* Read header */
|
|
69
|
+
memcpy(header, patch_data, 32);
|
|
70
|
+
|
|
71
|
+
/* Check for appropriate magic */
|
|
72
|
+
if (memcmp(header, "BSDIFF40", 8) != 0) return -1;
|
|
73
|
+
|
|
74
|
+
/* Read lengths from header */
|
|
75
|
+
bzctrllen = offtin(header + 8);
|
|
76
|
+
bzdatalen = offtin(header + 16);
|
|
77
|
+
newsize = offtin(header + 24);
|
|
78
|
+
|
|
79
|
+
if (bzctrllen < 0 || bzdatalen < 0 || newsize < 0) return -1;
|
|
80
|
+
|
|
81
|
+
/* Set up pointers into patch data */
|
|
82
|
+
ctrl_block = patch_data + 32;
|
|
83
|
+
diff_block = ctrl_block + bzctrllen;
|
|
84
|
+
extra_block = diff_block + bzdatalen;
|
|
85
|
+
|
|
86
|
+
/* Allocate new buffer */
|
|
87
|
+
new_buf = (uint8_t*)malloc(newsize + 1);
|
|
88
|
+
if (new_buf == NULL) return -1;
|
|
89
|
+
|
|
90
|
+
oldpos = 0;
|
|
91
|
+
newpos = 0;
|
|
92
|
+
|
|
93
|
+
while (newpos < newsize) {
|
|
94
|
+
/* Read control data */
|
|
95
|
+
for (i = 0; i < 3; i++) {
|
|
96
|
+
memcpy(buf, ctrl_block, 8);
|
|
97
|
+
ctrl_block += 8;
|
|
98
|
+
ctrl[i] = offtin(buf);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Sanity check */
|
|
102
|
+
if (newpos + ctrl[0] > newsize) {
|
|
103
|
+
free(new_buf);
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Read diff string */
|
|
108
|
+
memcpy(new_buf + newpos, diff_block, ctrl[0]);
|
|
109
|
+
diff_block += ctrl[0];
|
|
110
|
+
|
|
111
|
+
/* Add old data to diff string */
|
|
112
|
+
for (i = 0; i < ctrl[0]; i++) {
|
|
113
|
+
if ((oldpos + i >= 0) && (oldpos + i < old_size)) {
|
|
114
|
+
new_buf[newpos + i] += old_data[oldpos + i];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Adjust pointers */
|
|
119
|
+
newpos += ctrl[0];
|
|
120
|
+
oldpos += ctrl[0];
|
|
121
|
+
|
|
122
|
+
/* Sanity check */
|
|
123
|
+
if (newpos + ctrl[1] > newsize) {
|
|
124
|
+
free(new_buf);
|
|
125
|
+
return -1;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Read extra string */
|
|
129
|
+
memcpy(new_buf + newpos, extra_block, ctrl[1]);
|
|
130
|
+
extra_block += ctrl[1];
|
|
131
|
+
|
|
132
|
+
newpos += ctrl[1];
|
|
133
|
+
oldpos += ctrl[2];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
*new_data = new_buf;
|
|
137
|
+
*new_size = newsize;
|
|
138
|
+
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
int bspatch_files(const char* old_path, const char* patch_path, const char* new_path)
|
|
143
|
+
{
|
|
144
|
+
FILE *f;
|
|
145
|
+
uint8_t *old_data = NULL, *patch_data = NULL, *new_data = NULL;
|
|
146
|
+
int64_t old_size, patch_size, new_size;
|
|
147
|
+
int result = -1;
|
|
148
|
+
|
|
149
|
+
/* Read old file */
|
|
150
|
+
f = fopen(old_path, "rb");
|
|
151
|
+
if (f == NULL) goto cleanup;
|
|
152
|
+
fseek(f, 0, SEEK_END);
|
|
153
|
+
old_size = ftell(f);
|
|
154
|
+
fseek(f, 0, SEEK_SET);
|
|
155
|
+
old_data = (uint8_t*)malloc(old_size);
|
|
156
|
+
if (old_data == NULL) { fclose(f); goto cleanup; }
|
|
157
|
+
if (fread(old_data, 1, old_size, f) != (size_t)old_size) { fclose(f); goto cleanup; }
|
|
158
|
+
fclose(f);
|
|
159
|
+
|
|
160
|
+
/* Read patch file */
|
|
161
|
+
f = fopen(patch_path, "rb");
|
|
162
|
+
if (f == NULL) goto cleanup;
|
|
163
|
+
fseek(f, 0, SEEK_END);
|
|
164
|
+
patch_size = ftell(f);
|
|
165
|
+
fseek(f, 0, SEEK_SET);
|
|
166
|
+
patch_data = (uint8_t*)malloc(patch_size);
|
|
167
|
+
if (patch_data == NULL) { fclose(f); goto cleanup; }
|
|
168
|
+
if (fread(patch_data, 1, patch_size, f) != (size_t)patch_size) { fclose(f); goto cleanup; }
|
|
169
|
+
fclose(f);
|
|
170
|
+
|
|
171
|
+
/* Apply patch */
|
|
172
|
+
if (bspatch(old_data, old_size, &new_data, &new_size, patch_data, patch_size) != 0)
|
|
173
|
+
goto cleanup;
|
|
174
|
+
|
|
175
|
+
/* Write new file */
|
|
176
|
+
f = fopen(new_path, "wb");
|
|
177
|
+
if (f == NULL) goto cleanup;
|
|
178
|
+
if (fwrite(new_data, 1, new_size, f) != (size_t)new_size) { fclose(f); goto cleanup; }
|
|
179
|
+
fclose(f);
|
|
180
|
+
|
|
181
|
+
result = 0;
|
|
182
|
+
|
|
183
|
+
cleanup:
|
|
184
|
+
free(old_data);
|
|
185
|
+
free(patch_data);
|
|
186
|
+
free(new_data);
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*-
|
|
2
|
+
* Copyright 2003-2005 Colin Percival
|
|
3
|
+
* All rights reserved
|
|
4
|
+
*
|
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6
|
+
* modification, are permitted providing that the following conditions
|
|
7
|
+
* are met:
|
|
8
|
+
* 1. Redistributions of source code must retain the above copyright
|
|
9
|
+
* notice, this list of conditions and the following disclaimer.
|
|
10
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
* documentation and/or other materials provided with the distribution.
|
|
13
|
+
*
|
|
14
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
15
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
16
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
18
|
+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
19
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
20
|
+
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
21
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
22
|
+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
23
|
+
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
24
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
#ifndef BSPATCH_H
|
|
28
|
+
#define BSPATCH_H
|
|
29
|
+
|
|
30
|
+
#include <stdint.h>
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Apply a bsdiff patch.
|
|
34
|
+
*
|
|
35
|
+
* @param old_data Pointer to the old file data
|
|
36
|
+
* @param old_size Size of the old file
|
|
37
|
+
* @param new_data Output pointer that will be allocated with the new file data
|
|
38
|
+
* @param new_size Output size of the new file
|
|
39
|
+
* @param patch_data Pointer to the patch file data
|
|
40
|
+
* @param patch_size Size of the patch file
|
|
41
|
+
* @return 0 on success, -1 on failure
|
|
42
|
+
*/
|
|
43
|
+
int bspatch(const uint8_t* old_data, int64_t old_size,
|
|
44
|
+
uint8_t** new_data, int64_t* new_size,
|
|
45
|
+
const uint8_t* patch_data, int64_t patch_size);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Apply a bsdiff patch using file paths.
|
|
49
|
+
*
|
|
50
|
+
* @param old_path Path to the old file
|
|
51
|
+
* @param patch_path Path to the patch file
|
|
52
|
+
* @param new_path Path to write the new file
|
|
53
|
+
* @return 0 on success, non-zero on failure
|
|
54
|
+
*/
|
|
55
|
+
int bspatch_files(const char* old_path, const char* patch_path, const char* new_path);
|
|
56
|
+
|
|
57
|
+
#endif /* BSPATCH_H */
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* JNI wrapper for bspatch
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#include <jni.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
#include "bspatch.h"
|
|
8
|
+
|
|
9
|
+
JNIEXPORT jint JNICALL
|
|
10
|
+
Java_com_swiftpatch_PatchApplier_nativeApplyPatch(
|
|
11
|
+
JNIEnv *env,
|
|
12
|
+
jobject thiz,
|
|
13
|
+
jstring old_path,
|
|
14
|
+
jstring patch_path,
|
|
15
|
+
jstring new_path)
|
|
16
|
+
{
|
|
17
|
+
const char *old_path_str = (*env)->GetStringUTFChars(env, old_path, NULL);
|
|
18
|
+
const char *patch_path_str = (*env)->GetStringUTFChars(env, patch_path, NULL);
|
|
19
|
+
const char *new_path_str = (*env)->GetStringUTFChars(env, new_path, NULL);
|
|
20
|
+
|
|
21
|
+
int result = bspatch_files(old_path_str, patch_path_str, new_path_str);
|
|
22
|
+
|
|
23
|
+
(*env)->ReleaseStringUTFChars(env, old_path, old_path_str);
|
|
24
|
+
(*env)->ReleaseStringUTFChars(env, patch_path, patch_path_str);
|
|
25
|
+
(*env)->ReleaseStringUTFChars(env, new_path, new_path_str);
|
|
26
|
+
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/*-
|
|
2
|
+
* Copyright 2003-2005 Colin Percival
|
|
3
|
+
* Copyright 2012 Matthew Endsley
|
|
4
|
+
* All rights reserved
|
|
5
|
+
*
|
|
6
|
+
* Redistribution and use in source and binary forms, with or without
|
|
7
|
+
* modification, are permitted providing that the following conditions
|
|
8
|
+
* are met:
|
|
9
|
+
* 1. Redistributions of source code must retain the above copyright
|
|
10
|
+
* notice, this list of conditions and the following disclaimer.
|
|
11
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
|
12
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
13
|
+
* documentation and/or other materials provided with the distribution.
|
|
14
|
+
*
|
|
15
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
16
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
17
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
18
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
19
|
+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
21
|
+
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
22
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
23
|
+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
24
|
+
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
25
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
#include "bspatch.h"
|
|
29
|
+
|
|
30
|
+
#include <stdlib.h>
|
|
31
|
+
#include <stdio.h>
|
|
32
|
+
#include <string.h>
|
|
33
|
+
|
|
34
|
+
static int64_t offtin(uint8_t *buf)
|
|
35
|
+
{
|
|
36
|
+
int64_t y;
|
|
37
|
+
|
|
38
|
+
y = buf[7] & 0x7F;
|
|
39
|
+
y = y * 256; y += buf[6];
|
|
40
|
+
y = y * 256; y += buf[5];
|
|
41
|
+
y = y * 256; y += buf[4];
|
|
42
|
+
y = y * 256; y += buf[3];
|
|
43
|
+
y = y * 256; y += buf[2];
|
|
44
|
+
y = y * 256; y += buf[1];
|
|
45
|
+
y = y * 256; y += buf[0];
|
|
46
|
+
|
|
47
|
+
if (buf[7] & 0x80) y = -y;
|
|
48
|
+
|
|
49
|
+
return y;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static int bspatch_internal(const uint8_t* old_data, int64_t old_size,
|
|
53
|
+
uint8_t** new_data, int64_t* new_size,
|
|
54
|
+
const uint8_t* patch_data, int64_t patch_size)
|
|
55
|
+
{
|
|
56
|
+
int64_t newsize;
|
|
57
|
+
int64_t bzctrllen, bzdatalen;
|
|
58
|
+
uint8_t header[32];
|
|
59
|
+
uint8_t buf[8];
|
|
60
|
+
int64_t oldpos, newpos;
|
|
61
|
+
int64_t ctrl[3];
|
|
62
|
+
int64_t i;
|
|
63
|
+
const uint8_t *ctrl_block, *diff_block, *extra_block;
|
|
64
|
+
uint8_t *new_buf;
|
|
65
|
+
|
|
66
|
+
if (patch_size < 32) return -1;
|
|
67
|
+
|
|
68
|
+
/* Read header */
|
|
69
|
+
memcpy(header, patch_data, 32);
|
|
70
|
+
|
|
71
|
+
/* Check for appropriate magic */
|
|
72
|
+
if (memcmp(header, "BSDIFF40", 8) != 0) return -1;
|
|
73
|
+
|
|
74
|
+
/* Read lengths from header */
|
|
75
|
+
bzctrllen = offtin(header + 8);
|
|
76
|
+
bzdatalen = offtin(header + 16);
|
|
77
|
+
newsize = offtin(header + 24);
|
|
78
|
+
|
|
79
|
+
if (bzctrllen < 0 || bzdatalen < 0 || newsize < 0) return -1;
|
|
80
|
+
|
|
81
|
+
/* Set up pointers into patch data */
|
|
82
|
+
ctrl_block = patch_data + 32;
|
|
83
|
+
diff_block = ctrl_block + bzctrllen;
|
|
84
|
+
extra_block = diff_block + bzdatalen;
|
|
85
|
+
|
|
86
|
+
/* Allocate new buffer */
|
|
87
|
+
new_buf = (uint8_t*)malloc((size_t)(newsize + 1));
|
|
88
|
+
if (new_buf == NULL) return -1;
|
|
89
|
+
|
|
90
|
+
oldpos = 0;
|
|
91
|
+
newpos = 0;
|
|
92
|
+
|
|
93
|
+
while (newpos < newsize) {
|
|
94
|
+
/* Read control data */
|
|
95
|
+
for (i = 0; i < 3; i++) {
|
|
96
|
+
memcpy(buf, ctrl_block, 8);
|
|
97
|
+
ctrl_block += 8;
|
|
98
|
+
ctrl[i] = offtin(buf);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* Sanity check */
|
|
102
|
+
if (newpos + ctrl[0] > newsize) {
|
|
103
|
+
free(new_buf);
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Read diff string */
|
|
108
|
+
memcpy(new_buf + newpos, diff_block, (size_t)ctrl[0]);
|
|
109
|
+
diff_block += ctrl[0];
|
|
110
|
+
|
|
111
|
+
/* Add old data to diff string */
|
|
112
|
+
for (i = 0; i < ctrl[0]; i++) {
|
|
113
|
+
if ((oldpos + i >= 0) && (oldpos + i < old_size)) {
|
|
114
|
+
new_buf[newpos + i] += old_data[oldpos + i];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* Adjust pointers */
|
|
119
|
+
newpos += ctrl[0];
|
|
120
|
+
oldpos += ctrl[0];
|
|
121
|
+
|
|
122
|
+
/* Sanity check */
|
|
123
|
+
if (newpos + ctrl[1] > newsize) {
|
|
124
|
+
free(new_buf);
|
|
125
|
+
return -1;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Read extra string */
|
|
129
|
+
memcpy(new_buf + newpos, extra_block, (size_t)ctrl[1]);
|
|
130
|
+
extra_block += ctrl[1];
|
|
131
|
+
|
|
132
|
+
newpos += ctrl[1];
|
|
133
|
+
oldpos += ctrl[2];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
*new_data = new_buf;
|
|
137
|
+
*new_size = newsize;
|
|
138
|
+
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
int bspatch_files(const char* old_path, const char* patch_path, const char* new_path)
|
|
143
|
+
{
|
|
144
|
+
FILE *f;
|
|
145
|
+
uint8_t *old_data = NULL, *patch_data = NULL, *new_data = NULL;
|
|
146
|
+
int64_t old_size, patch_size, new_size;
|
|
147
|
+
int result = -1;
|
|
148
|
+
|
|
149
|
+
/* Read old file */
|
|
150
|
+
f = fopen(old_path, "rb");
|
|
151
|
+
if (f == NULL) goto cleanup;
|
|
152
|
+
fseek(f, 0, SEEK_END);
|
|
153
|
+
old_size = ftell(f);
|
|
154
|
+
fseek(f, 0, SEEK_SET);
|
|
155
|
+
old_data = (uint8_t*)malloc((size_t)old_size);
|
|
156
|
+
if (old_data == NULL) { fclose(f); goto cleanup; }
|
|
157
|
+
if (fread(old_data, 1, (size_t)old_size, f) != (size_t)old_size) { fclose(f); goto cleanup; }
|
|
158
|
+
fclose(f);
|
|
159
|
+
|
|
160
|
+
/* Read patch file */
|
|
161
|
+
f = fopen(patch_path, "rb");
|
|
162
|
+
if (f == NULL) goto cleanup;
|
|
163
|
+
fseek(f, 0, SEEK_END);
|
|
164
|
+
patch_size = ftell(f);
|
|
165
|
+
fseek(f, 0, SEEK_SET);
|
|
166
|
+
patch_data = (uint8_t*)malloc((size_t)patch_size);
|
|
167
|
+
if (patch_data == NULL) { fclose(f); goto cleanup; }
|
|
168
|
+
if (fread(patch_data, 1, (size_t)patch_size, f) != (size_t)patch_size) { fclose(f); goto cleanup; }
|
|
169
|
+
fclose(f);
|
|
170
|
+
|
|
171
|
+
/* Apply patch */
|
|
172
|
+
if (bspatch_internal(old_data, old_size, &new_data, &new_size, patch_data, patch_size) != 0)
|
|
173
|
+
goto cleanup;
|
|
174
|
+
|
|
175
|
+
/* Write new file */
|
|
176
|
+
f = fopen(new_path, "wb");
|
|
177
|
+
if (f == NULL) goto cleanup;
|
|
178
|
+
if (fwrite(new_data, 1, (size_t)new_size, f) != (size_t)new_size) { fclose(f); goto cleanup; }
|
|
179
|
+
fclose(f);
|
|
180
|
+
|
|
181
|
+
result = 0;
|
|
182
|
+
|
|
183
|
+
cleanup:
|
|
184
|
+
free(old_data);
|
|
185
|
+
free(patch_data);
|
|
186
|
+
free(new_data);
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*-
|
|
2
|
+
* Copyright 2003-2005 Colin Percival
|
|
3
|
+
* All rights reserved
|
|
4
|
+
*
|
|
5
|
+
* Redistribution and use in source and binary forms, with or without
|
|
6
|
+
* modification, are permitted providing that the following conditions
|
|
7
|
+
* are met:
|
|
8
|
+
* 1. Redistributions of source code must retain the above copyright
|
|
9
|
+
* notice, this list of conditions and the following disclaimer.
|
|
10
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
* notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
* documentation and/or other materials provided with the distribution.
|
|
13
|
+
*
|
|
14
|
+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
15
|
+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
16
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
17
|
+
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
18
|
+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
19
|
+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
20
|
+
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
21
|
+
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
22
|
+
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
23
|
+
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
24
|
+
* POSSIBILITY OF SUCH DAMAGE.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
#ifndef BSPATCH_H
|
|
28
|
+
#define BSPATCH_H
|
|
29
|
+
|
|
30
|
+
#include <stdint.h>
|
|
31
|
+
|
|
32
|
+
#ifdef __cplusplus
|
|
33
|
+
extern "C" {
|
|
34
|
+
#endif
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Apply a bsdiff patch using file paths.
|
|
38
|
+
*
|
|
39
|
+
* @param old_path Path to the old file
|
|
40
|
+
* @param patch_path Path to the patch file
|
|
41
|
+
* @param new_path Path to write the new file
|
|
42
|
+
* @return 0 on success, non-zero on failure
|
|
43
|
+
*/
|
|
44
|
+
int bspatch_files(const char* old_path, const char* patch_path, const char* new_path);
|
|
45
|
+
|
|
46
|
+
#ifdef __cplusplus
|
|
47
|
+
}
|
|
48
|
+
#endif
|
|
49
|
+
|
|
50
|
+
#endif /* BSPATCH_H */
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
class BundleManager {
|
|
4
|
+
|
|
5
|
+
var baseDir: URL {
|
|
6
|
+
let documentsDir = FileManager.default.urls(
|
|
7
|
+
for: .documentDirectory,
|
|
8
|
+
in: .userDomainMask
|
|
9
|
+
).first!
|
|
10
|
+
return documentsDir.appendingPathComponent("swiftpatch")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private var bundlesDir: URL {
|
|
14
|
+
baseDir.appendingPathComponent("bundles")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
private var patchesDir: URL {
|
|
18
|
+
baseDir.appendingPathComponent("patches")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private var tempDir: URL {
|
|
22
|
+
baseDir.appendingPathComponent("temp")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Ensure all required directories exist
|
|
26
|
+
func ensureDirectoriesExist() {
|
|
27
|
+
let fm = FileManager.default
|
|
28
|
+
try? fm.createDirectory(at: bundlesDir, withIntermediateDirectories: true)
|
|
29
|
+
try? fm.createDirectory(at: patchesDir, withIntermediateDirectories: true)
|
|
30
|
+
try? fm.createDirectory(at: tempDir, withIntermediateDirectories: true)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Get the file URL for a downloaded update (before processing)
|
|
34
|
+
func getDownloadFile(hash: String) -> URL {
|
|
35
|
+
tempDir.appendingPathComponent("\(hash).download")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// Get the file URL for a decompressed file
|
|
39
|
+
func getDecompressedFile(hash: String) -> URL {
|
|
40
|
+
tempDir.appendingPathComponent("\(hash).decompressed")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Get the file URL for a bundle by its hash
|
|
44
|
+
func getBundleFile(hash: String) -> URL {
|
|
45
|
+
bundlesDir.appendingPathComponent("\(hash).bundle")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Extract the original bundled JS bundle from the app bundle
|
|
49
|
+
func extractOriginalBundle() throws -> URL {
|
|
50
|
+
let originalFile = tempDir.appendingPathComponent("original.bundle")
|
|
51
|
+
|
|
52
|
+
if FileManager.default.fileExists(atPath: originalFile.path) {
|
|
53
|
+
return originalFile
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Try main.jsbundle first
|
|
57
|
+
if let bundledURL = Bundle.main.url(forResource: "main", withExtension: "jsbundle") {
|
|
58
|
+
try FileManager.default.copyItem(at: bundledURL, to: originalFile)
|
|
59
|
+
return originalFile
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Try index.ios.bundle
|
|
63
|
+
if let bundledURL = Bundle.main.url(forResource: "index.ios", withExtension: "bundle") {
|
|
64
|
+
try FileManager.default.copyItem(at: bundledURL, to: originalFile)
|
|
65
|
+
return originalFile
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
throw SwiftPatchError.patchFailed
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// Clean up old bundles that are no longer needed
|
|
72
|
+
func cleanupOldBundles(userDefaults: UserDefaults, prefsKey: String) {
|
|
73
|
+
let currentHash = userDefaults.string(forKey: "\(prefsKey)_current_bundle_hash")
|
|
74
|
+
let previousHash = userDefaults.string(forKey: "\(prefsKey)_previous_bundle_hash")
|
|
75
|
+
|
|
76
|
+
let keepHashes = Set([currentHash, previousHash].compactMap { $0 })
|
|
77
|
+
|
|
78
|
+
let fm = FileManager.default
|
|
79
|
+
|
|
80
|
+
// Clean up bundles
|
|
81
|
+
if let files = try? fm.contentsOfDirectory(
|
|
82
|
+
at: bundlesDir,
|
|
83
|
+
includingPropertiesForKeys: nil
|
|
84
|
+
) {
|
|
85
|
+
for file in files {
|
|
86
|
+
let fileHash = file.deletingPathExtension().lastPathComponent
|
|
87
|
+
if !keepHashes.contains(fileHash) {
|
|
88
|
+
try? fm.removeItem(at: file)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Clean up temp directory
|
|
94
|
+
if let files = try? fm.contentsOfDirectory(
|
|
95
|
+
at: tempDir,
|
|
96
|
+
includingPropertiesForKeys: nil
|
|
97
|
+
) {
|
|
98
|
+
for file in files {
|
|
99
|
+
try? fm.removeItem(at: file)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Clean up patches
|
|
104
|
+
if let files = try? fm.contentsOfDirectory(
|
|
105
|
+
at: patchesDir,
|
|
106
|
+
includingPropertiesForKeys: nil
|
|
107
|
+
) {
|
|
108
|
+
for file in files {
|
|
109
|
+
try? fm.removeItem(at: file)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
class CrashDetector {
|
|
4
|
+
|
|
5
|
+
private static let crashDetectionWindowSeconds: TimeInterval = 10.0
|
|
6
|
+
private static let maxCrashesBeforeRollback = 2
|
|
7
|
+
|
|
8
|
+
private let userDefaults: UserDefaults
|
|
9
|
+
private let prefsKey: String
|
|
10
|
+
|
|
11
|
+
init(userDefaults: UserDefaults, prefsKey: String) {
|
|
12
|
+
self.userDefaults = userDefaults
|
|
13
|
+
self.prefsKey = prefsKey
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/// Check if we need to rollback due to a crash after install.
|
|
17
|
+
/// Called during native module initialization.
|
|
18
|
+
func checkForPendingRollback(onRollback: @escaping (String) -> Void) {
|
|
19
|
+
let pendingConfirmation = userDefaults.bool(
|
|
20
|
+
forKey: "\(prefsKey)_pending_install_confirmation"
|
|
21
|
+
)
|
|
22
|
+
guard pendingConfirmation else { return }
|
|
23
|
+
|
|
24
|
+
let installTimestamp = userDefaults.double(
|
|
25
|
+
forKey: "\(prefsKey)_install_timestamp"
|
|
26
|
+
)
|
|
27
|
+
let currentTime = Date().timeIntervalSince1970
|
|
28
|
+
|
|
29
|
+
if currentTime - installTimestamp < Self.crashDetectionWindowSeconds {
|
|
30
|
+
// Within crash detection window - this might be a crash-restart cycle
|
|
31
|
+
let crashCount = userDefaults.integer(forKey: "\(prefsKey)_crash_count") + 1
|
|
32
|
+
|
|
33
|
+
if crashCount >= Self.maxCrashesBeforeRollback {
|
|
34
|
+
let reason = "Automatic rollback after \(crashCount) crashes"
|
|
35
|
+
performRollback(reason: reason)
|
|
36
|
+
onRollback("Crashed \(crashCount) times within detection window")
|
|
37
|
+
} else {
|
|
38
|
+
userDefaults.set(crashCount, forKey: "\(prefsKey)_crash_count")
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
// We survived the crash detection window, mark as stable
|
|
42
|
+
confirmStable()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Mark the current installation as stable
|
|
47
|
+
func confirmStable() {
|
|
48
|
+
userDefaults.set(false, forKey: "\(prefsKey)_pending_install_confirmation")
|
|
49
|
+
userDefaults.set(0, forKey: "\(prefsKey)_crash_count")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private func performRollback(reason: String) {
|
|
53
|
+
let previousHash = userDefaults.string(forKey: "\(prefsKey)_previous_bundle_hash")
|
|
54
|
+
let previousPath = userDefaults.string(forKey: "\(prefsKey)_previous_bundle_path")
|
|
55
|
+
|
|
56
|
+
if let hash = previousHash, let path = previousPath {
|
|
57
|
+
userDefaults.set(hash, forKey: "\(prefsKey)_current_bundle_hash")
|
|
58
|
+
userDefaults.set(path, forKey: "\(prefsKey)_current_bundle_path")
|
|
59
|
+
userDefaults.removeObject(forKey: "\(prefsKey)_previous_bundle_hash")
|
|
60
|
+
userDefaults.removeObject(forKey: "\(prefsKey)_previous_bundle_path")
|
|
61
|
+
} else {
|
|
62
|
+
// Rollback to original bundled version
|
|
63
|
+
userDefaults.removeObject(forKey: "\(prefsKey)_current_bundle_hash")
|
|
64
|
+
userDefaults.removeObject(forKey: "\(prefsKey)_current_bundle_path")
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
userDefaults.set(false, forKey: "\(prefsKey)_pending_install_confirmation")
|
|
68
|
+
userDefaults.set(0, forKey: "\(prefsKey)_crash_count")
|
|
69
|
+
userDefaults.set(reason, forKey: "\(prefsKey)_last_rollback_reason")
|
|
70
|
+
}
|
|
71
|
+
}
|