@capgo/capacitor-updater 5.50.1 → 5.51.15

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.
Files changed (28) hide show
  1. package/CapgoCapacitorUpdater.podspec +1 -1
  2. package/Package.swift +3 -2
  3. package/README.md +53 -48
  4. package/android/build.gradle +1 -0
  5. package/android/src/main/java/ee/forgr/capacitor_updater/AppLifecycleObserver.java +29 -2
  6. package/android/src/main/java/ee/forgr/capacitor_updater/BundleInfo.java +7 -3
  7. package/android/src/main/java/ee/forgr/capacitor_updater/BundleStatus.java +1 -0
  8. package/android/src/main/java/ee/forgr/capacitor_updater/CapacitorUpdaterPlugin.java +476 -151
  9. package/android/src/main/java/ee/forgr/capacitor_updater/CapgoUpdater.java +1354 -412
  10. package/android/src/main/java/ee/forgr/capacitor_updater/CryptoCipher.java +102 -31
  11. package/android/src/main/java/ee/forgr/capacitor_updater/DataManager.java +23 -7
  12. package/android/src/main/java/ee/forgr/capacitor_updater/DelayCondition.java +2 -2
  13. package/android/src/main/java/ee/forgr/capacitor_updater/DownloadService.java +540 -224
  14. package/android/src/main/java/ee/forgr/capacitor_updater/DownloadWorkerManager.java +103 -3
  15. package/android/src/main/java/ee/forgr/capacitor_updater/InternalUtils.java +1 -1
  16. package/android/src/main/java/ee/forgr/capacitor_updater/ShakeMenu.java +131 -145
  17. package/dist/docs.json +32 -8
  18. package/dist/esm/definitions.d.ts +41 -17
  19. package/dist/esm/definitions.js.map +1 -1
  20. package/ios/Sources/CapacitorUpdaterPlugin/AES.swift +124 -0
  21. package/ios/Sources/CapacitorUpdaterPlugin/BundleInfo.swift +9 -1
  22. package/ios/Sources/CapacitorUpdaterPlugin/BundleStatus.swift +3 -0
  23. package/ios/Sources/CapacitorUpdaterPlugin/CapacitorUpdaterPlugin.swift +787 -92
  24. package/ios/Sources/CapacitorUpdaterPlugin/CapgoUpdater.swift +1014 -268
  25. package/ios/Sources/CapacitorUpdaterPlugin/CryptoCipher.swift +49 -31
  26. package/ios/Sources/CapacitorUpdaterPlugin/ShakeMenu.swift +44 -20
  27. package/ios/Sources/CapacitorUpdaterPlugin/WebViewStatsReporter.swift +28 -0
  28. package/package.json +12 -7
@@ -1,6 +1,7 @@
1
1
  import Foundation
2
2
  import CommonCrypto
3
3
  import CryptoKit
4
+ import Darwin
4
5
 
5
6
  ///
6
7
  /// Constants
@@ -83,4 +84,127 @@ public struct AES128Key {
83
84
  return nil
84
85
  }
85
86
  }
87
+
88
+ /// AES-CBC file-to-file. Never holds the whole ciphertext in RAM.
89
+ func decrypt(from source: URL, to destination: URL) throws {
90
+ var cryptor: CCCryptorRef?
91
+ let createStatus: CCCryptorStatus = aes128Key.withUnsafeBytes { keyBytes in
92
+ initVector.withUnsafeBytes { ivBytes in
93
+ guard let keyPtr = keyBytes.baseAddress, let ivPtr = ivBytes.baseAddress else {
94
+ return CCCryptorStatus(kCCParamError)
95
+ }
96
+ return CCCryptorCreate(
97
+ CCOperation(kCCDecrypt),
98
+ AESConstants.aesAlgorithm,
99
+ AESConstants.aesOptions,
100
+ keyPtr,
101
+ keyBytes.count,
102
+ ivPtr,
103
+ &cryptor
104
+ )
105
+ }
106
+ }
107
+ guard createStatus == kCCSuccess, let cryptor else {
108
+ logger.error("Failed to create AES cryptor")
109
+ throw NSError(domain: "AESDecryptError", code: Int(createStatus), userInfo: nil)
110
+ }
111
+ defer {
112
+ CCCryptorRelease(cryptor)
113
+ }
114
+
115
+ guard let input = InputStream(url: source) else {
116
+ throw NSError(domain: "AESDecryptError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to open AES source"])
117
+ }
118
+ input.open()
119
+ defer {
120
+ input.close()
121
+ }
122
+
123
+ let tempURL = destination.deletingLastPathComponent().appendingPathComponent("capgo-aes-\(UUID().uuidString).tmp")
124
+ let fileManager = FileManager.default
125
+ fileManager.createFile(atPath: tempURL.path, contents: nil)
126
+ let output = try FileHandle(forWritingTo: tempURL)
127
+ defer {
128
+ try? output.close()
129
+ try? fileManager.removeItem(at: tempURL)
130
+ }
131
+
132
+ let bufferSize = CryptoCipher.ioBufferBytes()
133
+ let outBufSize = bufferSize + kCCBlockSizeAES128
134
+ var inBuf = [UInt8](repeating: 0, count: bufferSize)
135
+ var outBuf = [UInt8](repeating: 0, count: outBufSize)
136
+ let outFd = output.fileDescriptor
137
+
138
+ while true {
139
+ let n = inBuf.withUnsafeMutableBufferPointer { ptr in
140
+ input.read(ptr.baseAddress!, maxLength: ptr.count)
141
+ }
142
+ if n == 0 {
143
+ break
144
+ }
145
+ if n < 0 {
146
+ throw input.streamError ?? NSError(domain: "AESDecryptError", code: 2, userInfo: [NSLocalizedDescriptionKey: "AES stream read failed"])
147
+ }
148
+ var moved: size_t = 0
149
+ let status: CCCryptorStatus = inBuf.withUnsafeBufferPointer { inRaw in
150
+ outBuf.withUnsafeMutableBytes { outRaw in
151
+ CCCryptorUpdate(
152
+ cryptor,
153
+ inRaw.baseAddress,
154
+ n,
155
+ outRaw.baseAddress,
156
+ outBufSize,
157
+ &moved
158
+ )
159
+ }
160
+ }
161
+ guard status == kCCSuccess else {
162
+ logger.error("AES stream update failed")
163
+ throw NSError(domain: "AESDecryptError", code: Int(status), userInfo: nil)
164
+ }
165
+ if moved > 0 {
166
+ try Self.writeAll(fd: outFd, buffer: &outBuf, count: moved)
167
+ }
168
+ }
169
+
170
+ var moved: size_t = 0
171
+ let finalStatus: CCCryptorStatus = outBuf.withUnsafeMutableBytes { outRaw in
172
+ CCCryptorFinal(cryptor, outRaw.baseAddress, outBufSize, &moved)
173
+ }
174
+ guard finalStatus == kCCSuccess else {
175
+ logger.error("AES stream finalize failed")
176
+ throw NSError(domain: "AESDecryptError", code: Int(finalStatus), userInfo: nil)
177
+ }
178
+ if moved > 0 {
179
+ try Self.writeAll(fd: outFd, buffer: &outBuf, count: moved)
180
+ }
181
+ try output.close()
182
+
183
+ let decryptedSize = (try fileManager.attributesOfItem(atPath: tempURL.path)[.size] as? NSNumber)?.uint64Value ?? 0
184
+ if decryptedSize == 0 {
185
+ throw NSError(domain: "Empty decrypted data", code: 7, userInfo: nil)
186
+ }
187
+
188
+ do {
189
+ _ = try fileManager.replaceItemAt(destination, withItemAt: tempURL)
190
+ } catch {
191
+ if fileManager.fileExists(atPath: destination.path) {
192
+ throw error
193
+ }
194
+ try fileManager.moveItem(at: tempURL, to: destination)
195
+ }
196
+ }
197
+
198
+ private static func writeAll(fd: Int32, buffer: inout [UInt8], count: Int) throws {
199
+ var offset = 0
200
+ while offset < count {
201
+ let written = buffer.withUnsafeBufferPointer { ptr -> Int in
202
+ Darwin.write(fd, ptr.baseAddress!.advanced(by: offset), count - offset)
203
+ }
204
+ if written <= 0 {
205
+ throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno), userInfo: [NSLocalizedDescriptionKey: "AES stream write failed"])
206
+ }
207
+ offset += written
208
+ }
209
+ }
86
210
  }
@@ -65,9 +65,17 @@ import Foundation
65
65
  return BundleStatus.DELETED == self.status
66
66
  }
67
67
 
68
+ public func isDeleting() -> Bool {
69
+ return BundleStatus.DELETING == self.status
70
+ }
71
+
72
+ public func isDownloading() -> Bool {
73
+ return BundleStatus.DOWNLOADING == self.status
74
+ }
75
+
68
76
  public func isDownloaded() -> Bool {
69
77
  return !self.isBuiltin() && self.downloaded != "" &&
70
- self.downloaded != BundleInfo.DOWNLOADED_BUILTIN && !self.isDeleted()
78
+ self.downloaded != BundleInfo.DOWNLOADED_BUILTIN && !self.isDeleted() && !self.isDeleting()
71
79
  }
72
80
 
73
81
  public func getDownloaded() -> String {
@@ -36,6 +36,7 @@ enum BundleStatus: LocalizedString, CaseIterable, Decodable, Encodable {
36
36
  case ERROR = "error"
37
37
  case PENDING = "pending"
38
38
  case DELETED = "deleted"
39
+ case DELETING = "deleting"
39
40
  case DOWNLOADING = "downloading"
40
41
 
41
42
  var storedValue: String {
@@ -48,6 +49,8 @@ enum BundleStatus: LocalizedString, CaseIterable, Decodable, Encodable {
48
49
  return "pending"
49
50
  case .DELETED:
50
51
  return "deleted"
52
+ case .DELETING:
53
+ return "deleting"
51
54
  case .DOWNLOADING:
52
55
  return "downloading"
53
56
  }