@capacitor-community/sqlite 5.2.3 → 5.2.4

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.
@@ -6,7 +6,8 @@ import Capacitor
6
6
  // swiftlint:disable type_body_length
7
7
  public class CapacitorSQLitePlugin: CAPPlugin {
8
8
  private var implementation: CapacitorSQLite?
9
- private let modeList: [String] = ["no-encryption", "encryption", "secret", "newsecret", "wrongsecret"]
9
+ private let modeList: [String] = ["no-encryption", "encryption", "secret",
10
+ "decryption", "newsecret", "wrongsecret"]
10
11
  private let retHandler: ReturnHandler = ReturnHandler()
11
12
  private var versionUpgrades: [String: [Int: [String: Any]]] = [:]
12
13
  var importObserver: Any?
@@ -239,7 +240,7 @@ public class CapacitorSQLitePlugin: CAPPlugin {
239
240
  if encrypted && !modeList.contains(inMode) {
240
241
  var msg: String = "CreateConnection: inMode "
241
242
  msg.append("must be in['encryption',")
242
- msg.append("'secret','newsecret']")
243
+ msg.append("'secret','decryption']")
243
244
  retHandler.rResult(call: call, message: msg)
244
245
  return
245
246
  }
@@ -107,7 +107,9 @@ class Database {
107
107
  // swiftlint:disable function_body_length
108
108
  func open () throws {
109
109
  var password: String = ""
110
- if isEncryption && encrypted && (mode == "secret" || mode == "encryption") {
110
+ if isEncryption && encrypted && (mode == "secret"
111
+ || mode == "encryption"
112
+ || mode == "decryption") {
111
113
  let isPassphrase = try UtilsSecret.isPassphrase(account: account)
112
114
  if !isPassphrase {
113
115
  let msg: String = "No Passphrase stored"
@@ -135,6 +137,28 @@ class Database {
135
137
  throw DatabaseError.open(message: msg)
136
138
  }
137
139
 
140
+ }
141
+ if mode == "decryption" {
142
+ if isEncryption {
143
+ do {
144
+ let ret: Bool = try UtilsEncryption
145
+ .decryptDatabase(databaseLocation: databaseLocation,
146
+ filePath: path, password: password,
147
+ version: dbVersion)
148
+ if !ret {
149
+ let msg: String = "Failed in decryption"
150
+ throw DatabaseError.open(message: msg)
151
+ }
152
+ password = ""
153
+ } catch UtilsEncryptionError.decryptionFailed(let message) {
154
+ let msg: String = "Failed in decryption \(message)"
155
+ throw DatabaseError.open(message: msg)
156
+ }
157
+ } else {
158
+ let msg: String = "No Encryption set in capacitor.config"
159
+ throw DatabaseError.open(message: msg)
160
+ }
161
+
138
162
  }
139
163
 
140
164
  do {
@@ -236,7 +260,7 @@ class Database {
236
260
  throw DatabaseError.isAvailTrans(message: msg)
237
261
  }
238
262
  }
239
-
263
+
240
264
  // MARK: - SetIsTransActive
241
265
 
242
266
  func setIsTransActive(newValue: Bool ) {
@@ -653,7 +677,7 @@ class Database {
653
677
  // MARK: - ExportToJson
654
678
 
655
679
  func exportToJson(expMode: String, isEncrypted: Bool)
656
- throws -> [String: Any] {
680
+ throws -> [String: Any] {
657
681
  var retObj: [String: Any] = [:]
658
682
 
659
683
  do {
@@ -105,7 +105,7 @@ class ImportFromJson {
105
105
  try UtilsSQLCipher
106
106
  .rollbackTransaction(mDB: mDB)
107
107
  mDB.setIsTransActive(newValue: false)
108
- } catch UtilsSQLCipherError
108
+ } catch UtilsSQLCipherError
109
109
  .rollbackTransaction(let message) {
110
110
  throw ImportFromJsonError
111
111
  .createSchema(message: message)
@@ -615,7 +615,7 @@ class ImportFromJson {
615
615
  try UtilsSQLCipher
616
616
  .rollbackTransaction(mDB: mDB)
617
617
  mDB.setIsTransActive(newValue: false)
618
- throw ImportFromJsonError
618
+ throw ImportFromJsonError
619
619
  .createViews(message: msg)
620
620
  } catch UtilsSQLCipherError
621
621
  .rollbackTransaction(let message) {
@@ -290,7 +290,7 @@ class UtilsDelete {
290
290
  var relatedItems: [[String: Any]] = []
291
291
  var key: String = ""
292
292
  let t1Names = withRefsNames.map { "t1.\($0)" }
293
- let t2Names = colNames.map { "t2.\($0)" }
293
+ let t2Names = colNames.map{ "t2.\($0)" }
294
294
 
295
295
  do {
296
296
  var whereClause = try UtilsSQLStatement
@@ -301,9 +301,8 @@ class UtilsDelete {
301
301
  whereClause = String(whereClause.dropLast())
302
302
  }
303
303
  let resultString = zip(t1Names, t2Names)
304
- .map {"\($0) = \($1)" }
304
+ .map { "\($0) = \($1)" }
305
305
  .joined(separator: " AND ")
306
-
307
306
  let sql = "SELECT t1.rowid FROM \(updTableName) t1 " +
308
307
  "JOIN \(tableName) t2 ON \(resultString) " +
309
308
  "WHERE \(whereClause) AND t1.sql_deleted = 0;"
@@ -11,6 +11,7 @@ import SQLCipher
11
11
 
12
12
  enum UtilsEncryptionError: Error {
13
13
  case encryptionFailed(message: String)
14
+ case decryptionFailed(message: String)
14
15
  }
15
16
  class UtilsEncryption {
16
17
 
@@ -87,5 +88,79 @@ class UtilsEncryption {
87
88
  return ret
88
89
  }
89
90
 
91
+ // MARK: - DecryptDatabase
92
+
93
+ // swiftlint:disable function_body_length
94
+ class func decryptDatabase(databaseLocation: String, filePath: String,
95
+ password: String, version: Int) throws -> Bool {
96
+ var ret: Bool = false
97
+ var oDB: OpaquePointer?
98
+ var eDB: OpaquePointer?
99
+ do {
100
+ if UtilsFile.isFileExist(filePath: filePath) {
101
+ do {
102
+ let tempPath: String = try UtilsFile
103
+ .getFilePath(databaseLocation: databaseLocation,
104
+ fileName: "temp.db")
105
+ try UtilsFile.renameFile(filePath: filePath,
106
+ toFilePath: tempPath,
107
+ databaseLocation: databaseLocation)
108
+ oDB = try UtilsSQLCipher
109
+ .openOrCreateDatabase(filename: tempPath,
110
+ password: password,
111
+ readonly: false)
112
+ eDB = try UtilsSQLCipher
113
+ .openOrCreateDatabase(filename: filePath,
114
+ password: "",
115
+ readonly: false)
116
+
117
+ var stmt: String = "PRAGMA key = '\(password)';"
118
+ stmt.append("ATTACH DATABASE '\(filePath)' ")
119
+ stmt.append("AS plaintext KEY '';")
120
+ stmt.append("SELECT sqlcipher_export('plaintext');")
121
+ stmt.append("DETACH DATABASE plaintext;")
122
+ if sqlite3_exec(oDB, stmt, nil, nil, nil) ==
123
+ SQLITE_OK {
124
+ try _ = UtilsFile
125
+ .deleteFile(fileName: "temp.db",
126
+ databaseLocation: databaseLocation)
127
+ // set the version
128
+ let sqltr: String = "PRAGMA user_version = \(version);"
129
+ if sqlite3_exec(eDB, sqltr, nil, nil, nil) != SQLITE_OK {
130
+ throw UtilsEncryptionError
131
+ .decryptionFailed(message: "set version to \(version) failed")
132
+ }
133
+
134
+ ret = true
135
+ }
136
+ // close the db
137
+ try UtilsSQLCipher.close(oDB: oDB)
138
+
139
+ } catch UtilsFileError.getFilePathFailed {
140
+ throw UtilsEncryptionError
141
+ .decryptionFailed(message: "file path failed")
142
+ } catch UtilsFileError.renameFileFailed {
143
+ throw UtilsEncryptionError
144
+ .decryptionFailed(message: "file rename failed")
145
+ } catch UtilsSQLCipherError.openOrCreateDatabase(_) {
146
+ throw UtilsEncryptionError
147
+ .decryptionFailed(message: "open failed")
148
+ } catch UtilsSQLCipherError.close(_) {
149
+ throw UtilsEncryptionError
150
+ .decryptionFailed(message: "close failed")
151
+ } catch let error {
152
+ print("Error: \(error)")
153
+ throw UtilsEncryptionError
154
+ .decryptionFailed(message: "Error: \(error)")
155
+ }
156
+ }
157
+ } catch let error {
158
+ print("Error: \(error)")
159
+ throw UtilsEncryptionError
160
+ .decryptionFailed(message: "Error: \(error)")
161
+ }
162
+ return ret
163
+ }
164
+
90
165
  }
91
166
  // swiftlint:enable function_body_length
@@ -665,9 +665,9 @@ class UtilsSQLCipher {
665
665
  "WHERE Statement"
666
666
  throw UtilsSQLCipherError.deleteSQL(message: msg)
667
667
  }
668
- /*
669
- let curTime = UtilsDelete.getCurrentTimeAsInteger()
670
- */
668
+ /*
669
+ let curTime = UtilsDelete.getCurrentTimeAsInteger()
670
+ */
671
671
  let setStmt = "sql_deleted = 1"
672
672
  // Find REFERENCIES if any and update the sql_deleted
673
673
  // column
@@ -7,7 +7,6 @@
7
7
 
8
8
  import Foundation
9
9
  enum UtilsSQLStatementError: Error {
10
- case extractColumnNames(message: String)
11
10
  case extractForeignKeyInfo(message: String)
12
11
  case addPrefixToWhereClause(message: String)
13
12
  }
@@ -69,8 +68,8 @@ class UtilsSQLStatement {
69
68
  // MARK: - addPrefixToWhereClause
70
69
 
71
70
  class func addPrefixToWhereClause(_ whereClause: String,
72
- from: [String],
73
- to: [String], prefix: String)
71
+ from: [String],
72
+ to: [String], prefix: String)
74
73
  throws -> String {
75
74
  var columnValuePairs: [String]
76
75
  if whereClause.contains("AND") {
@@ -141,6 +140,7 @@ class UtilsSQLStatement {
141
140
  }
142
141
  // MARK: - extractForeignKeyInfo
143
142
 
143
+ // swiftlint:enable function_body_length
144
144
  // swiftlint:enable type_body_length
145
145
  class func extractForeignKeyInfo(from sqlStatement: String)
146
146
  throws ->
@@ -225,32 +225,95 @@ class UtilsSQLStatement {
225
225
  return foreignKeyInfo
226
226
  }
227
227
  // swiftlint:enable type_body_length
228
+ // swiftlint:disable function_body_length
228
229
 
229
230
  // MARK: - extractColumnNames
230
231
 
232
+ // swiftlint:disable function_body_length
231
233
  class func extractColumnNames(from whereClause: String) -> [String] {
232
234
  let keywords: Set<String> = ["AND", "OR", "IN", "VALUES", "LIKE", "BETWEEN", "NOT"]
233
- let tokens = whereClause.components(separatedBy: CharacterSet(charactersIn: " ,()"))
235
+ let operators: Set<String> = ["=", "<", "<=", ">=", ">", "<>"]
234
236
 
235
237
  var columns = [String]()
236
238
  var inClause = false
237
239
  var inValues = false
240
+ var betweenClause = false
241
+ var andClause = false
242
+ var inPar = false
243
+ var inOper = false
244
+ var inLike = false
245
+ func extractString(from input: String) -> String {
246
+ // Check if the input string starts with "(" or ends with ")"
247
+ if input.hasPrefix("(") {
248
+ let startIndex = input.index(input.startIndex, offsetBy: 1)
249
+ let result = input[startIndex..<input.endIndex]
250
+ return String(result)
251
+ } else if input.hasSuffix(")") {
252
+ let endIndex = input.index(input.endIndex, offsetBy: -1)
253
+ let result = input[input.startIndex..<endIndex]
254
+ return String(result)
255
+ } else {
256
+ return input
257
+ }
258
+ }
259
+ func removeOperatorsAndFollowing(from input: String) -> String {
260
+ let operatorsPattern = "(=|<=|<|>|>=|<>)"
261
+ if let range = input.range(of: operatorsPattern, options: .regularExpression) {
262
+ let result = input.prefix(upTo: range.lowerBound)
263
+ return String(result)
264
+ } else {
265
+ return input // Return the input string unchanged if no operator is found
266
+ }
267
+ }
238
268
 
239
- for token in tokens {
240
- if token == "IN" {
269
+ func processToken(_ token: String) {
270
+ if token.uppercased() == "IN" {
241
271
  inClause = true
242
- } else if inClause && token == "(" {
272
+ } else if inClause && (token.prefix(7).uppercased() == "(VALUES" ||
273
+ token.prefix(8).uppercased() == "( VALUES") {
243
274
  inValues = true
244
- } else if inValues && token == ")" {
275
+ } else if inValues && (token.suffix(2).uppercased() == "))" ||
276
+ token.suffix(3).uppercased() == ") )") {
245
277
  inValues = false
278
+ } else if inClause && !inValues && token.prefix(1) == "(" {
279
+ inPar = true
280
+ } else if inClause && !inValues && token.suffix(1) == ")" {
281
+ inPar = false
282
+ inClause = false
283
+ } else if token.uppercased() == "BETWEEN" {
284
+ betweenClause = true
285
+ } else if betweenClause && token.uppercased() == "AND" {
286
+ andClause = true
287
+ } else if operators.contains(token) {
288
+ inOper = true
289
+ } else if token.uppercased() == "LIKE" {
290
+ inLike = true
246
291
  } else if token.range(of: "\\b[a-zA-Z]\\w*\\b", options: .regularExpression) != nil
247
- && !inValues && !keywords.contains(token.uppercased()) {
248
- columns.append(token)
292
+ && !inClause && (!inValues || !inPar)
293
+ && !betweenClause && !andClause && !inOper && !inLike
294
+ && !keywords.contains(token.uppercased()) {
295
+ var mToken = extractString(from: token)
296
+ mToken = removeOperatorsAndFollowing(from: mToken)
297
+ columns.append(mToken)
298
+ } else if token.range(of: "\\b[a-zA-Z]\\w*\\b", options: .regularExpression) != nil
299
+ && betweenClause && andClause {
300
+ betweenClause = false
301
+ andClause = false
302
+ } else if token.range(of: "\\b[a-zA-Z]\\w*\\b", options: .regularExpression) != nil
303
+ && inOper {
304
+ inOper = false
305
+ } else if token.range(of: "\\b[a-zA-Z]\\w*\\b", options: .regularExpression) != nil
306
+ && inLike {
307
+ inLike = false
249
308
  }
250
309
  }
251
-
252
- return Array(Set(columns))
310
+ let tokens = whereClause.components(separatedBy: CharacterSet(charactersIn: " ,"))
311
+ for token in tokens {
312
+ processToken(token)
313
+ }
314
+ return Array(columns)
253
315
  }
316
+ // swiftlint:enable function_body_length
254
317
 
255
318
  // MARK: - flattenMultilineString
256
319
 
@@ -308,11 +371,12 @@ class UtilsSQLStatement {
308
371
 
309
372
  for match in matches {
310
373
 
311
- let keysRange = Range(match.range(at: 1), in: whereClause)!
312
- let keysString = String(whereClause[keysRange])
313
- let keys = keysString.split(separator: ",").map {
314
- String($0.trimmingCharacters(in: .whitespaces)) }
315
- primaryKeySets.append(keys)
374
+ if let keysRange = Range(match.range(at: 1), in: whereClause) {
375
+ let keysString = String(whereClause[keysRange])
376
+ let keys = keysString.split(separator: ",").map {
377
+ String($0.trimmingCharacters(in: .whitespaces)) }
378
+ primaryKeySets.append(keys)
379
+ }
316
380
  }
317
381
  return primaryKeySets.isEmpty ? nil : primaryKeySets
318
382
  }
@@ -255,6 +255,7 @@ class UtilsSecret {
255
255
  if !getPassphrase(account: account).isEmpty {
256
256
  try setPassphrase(account: account, passphrase: "")
257
257
  }
258
+
258
259
  } catch UtilsSecretError.setPassphrase(let message) {
259
260
  throw UtilsSecretError.clearEncryptionSecret(message: message)
260
261
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor-community/sqlite",
3
- "version": "5.2.3",
3
+ "version": "5.2.4",
4
4
  "description": "Community plugin for native & electron SQLite databases",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -69,7 +69,7 @@
69
69
  "@ionic/swiftlint-config": "^1.1.2",
70
70
  "@rollup/plugin-commonjs": "^20.0.0",
71
71
  "@rollup/plugin-node-resolve": "^13.0.4",
72
- "electron": "^22.0.0",
72
+ "electron": "^25.2.0",
73
73
  "eslint": "^7.11.0",
74
74
  "prettier": "~2.3.0",
75
75
  "prettier-plugin-java": "~1.0.2",
@@ -98,6 +98,6 @@
98
98
  }
99
99
  },
100
100
  "dependencies": {
101
- "jeep-sqlite": "^2.5.1"
101
+ "jeep-sqlite": "^2.5.3"
102
102
  }
103
103
  }