@capacitor-community/sqlite 5.2.3 → 5.2.5

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.
@@ -451,6 +451,7 @@ public class CapacitorSQLite {
451
451
  throw new Exception(msg);
452
452
  }
453
453
  }
454
+
454
455
  /**
455
456
  * BeginTransaction
456
457
  *
@@ -466,7 +467,7 @@ public class CapacitorSQLite {
466
467
  if (db != null) {
467
468
  if (!db.isNCDB() && db.isOpen()) {
468
469
  try {
469
- Integer res = db.beginTransaction();
470
+ Integer res = db.beginTransaction();
470
471
  retObj.put("changes", res);
471
472
  return retObj;
472
473
  } catch (Exception e) {
@@ -497,7 +498,7 @@ public class CapacitorSQLite {
497
498
  if (db != null) {
498
499
  if (!db.isNCDB() && db.isOpen()) {
499
500
  try {
500
- Integer res = db.commitTransaction();
501
+ Integer res = db.commitTransaction();
501
502
  retObj.put("changes", res);
502
503
  return retObj;
503
504
  } catch (Exception e) {
@@ -528,7 +529,7 @@ public class CapacitorSQLite {
528
529
  if (db != null) {
529
530
  if (!db.isNCDB() && db.isOpen()) {
530
531
  try {
531
- Integer res = db.rollbackTransaction();
532
+ Integer res = db.rollbackTransaction();
532
533
  retObj.put("changes", res);
533
534
  return retObj;
534
535
  } catch (Exception e) {
@@ -563,7 +564,6 @@ public class CapacitorSQLite {
563
564
  } catch (Exception e) {
564
565
  throw new Exception(e.getMessage());
565
566
  }
566
-
567
567
  } else {
568
568
  String msg = "database " + dbName + " not opened";
569
569
  throw new Exception(msg);
@@ -1269,8 +1269,7 @@ public class CapacitorSQLite {
1269
1269
  }
1270
1270
  }
1271
1271
 
1272
- public JSObject exportToJson(String dbName, String expMode,
1273
- Boolean readonly, Boolean encrypted) throws Exception {
1272
+ public JSObject exportToJson(String dbName, String expMode, Boolean readonly, Boolean encrypted) throws Exception {
1274
1273
  dbName = getDatabaseName(dbName);
1275
1274
  String connName = readonly ? "RO_" + dbName : "RW_" + dbName;
1276
1275
  Database db = dbDict.get(connName);
@@ -9,6 +9,8 @@ import com.getcapacitor.PluginCall;
9
9
  import com.getcapacitor.PluginMethod;
10
10
  import com.getcapacitor.annotation.CapacitorPlugin;
11
11
  import com.getcapacitor.community.database.sqlite.SQLite.SqliteConfig;
12
+ import java.util.ArrayList;
13
+ import java.util.Arrays;
12
14
  import java.util.Collections;
13
15
  import java.util.Dictionary;
14
16
  import java.util.Hashtable;
@@ -28,6 +30,9 @@ public class CapacitorSQLitePlugin extends Plugin {
28
30
  private String passphrase = null;
29
31
  private String oldpassphrase = null;
30
32
  private String loadMessage = "";
33
+ private final ArrayList<String> modeList = new ArrayList<>(
34
+ Arrays.asList("no-encryption", "encryption", "secret", "decryption", "wrongsecret")
35
+ );
31
36
 
32
37
  /**
33
38
  * Load Method
@@ -287,12 +292,9 @@ public class CapacitorSQLitePlugin extends Plugin {
287
292
  Boolean encrypted = call.getBoolean("encrypted", false);
288
293
  if (encrypted) {
289
294
  inMode = call.getString("mode", "no-encryption");
290
- if (
291
- !inMode.equals("no-encryption") && !inMode.equals("encryption") && !inMode.equals("secret") && !inMode.equals("wrongsecret")
292
- ) {
295
+ if (!modeList.contains(inMode)) {
293
296
  String msg = "CreateConnection: inMode must ";
294
- msg += "be in ['encryption','secret'] ";
295
- msg += "** 'newsecret' has been deprecated";
297
+ msg += "be in ['encryption','secret', 'decryption'] ";
296
298
  rHandler.retResult(call, null, msg);
297
299
  return;
298
300
  }
@@ -369,6 +371,7 @@ public class CapacitorSQLitePlugin extends Plugin {
369
371
  rHandler.retResult(call, null, loadMessage);
370
372
  }
371
373
  }
374
+
372
375
  /**
373
376
  * BeginTransaction Method
374
377
  * Begin a Database Transaction
@@ -398,6 +401,7 @@ public class CapacitorSQLitePlugin extends Plugin {
398
401
  rHandler.retChanges(call, retRes, loadMessage);
399
402
  }
400
403
  }
404
+
401
405
  /**
402
406
  * CommitTransaction Method
403
407
  * Commit a Database Transaction
@@ -457,6 +461,7 @@ public class CapacitorSQLitePlugin extends Plugin {
457
461
  rHandler.retChanges(call, retRes, loadMessage);
458
462
  }
459
463
  }
464
+
460
465
  /**
461
466
  * IsTransactionActive Method
462
467
  * Check if a Database Transaction is Active
@@ -482,6 +487,7 @@ public class CapacitorSQLitePlugin extends Plugin {
482
487
  rHandler.retResult(call, null, loadMessage);
483
488
  }
484
489
  }
490
+
485
491
  /**
486
492
  * GetUrl Method
487
493
  * Get a database Url
@@ -1452,8 +1458,7 @@ public class CapacitorSQLitePlugin extends Plugin {
1452
1458
 
1453
1459
  if (implementation != null) {
1454
1460
  try {
1455
- JSObject res = implementation.exportToJson(dbName, expMode,
1456
- readOnly, encrypted);
1461
+ JSObject res = implementation.exportToJson(dbName, expMode, readOnly, encrypted);
1457
1462
  rHandler.retJSObject(call, res, null);
1458
1463
  } catch (Exception e) {
1459
1464
  String msg = "ExportToJson: " + e.getMessage();
@@ -144,6 +144,7 @@ public class Database {
144
144
  public boolean isAvailTrans() {
145
145
  return _db.inTransaction();
146
146
  }
147
+
147
148
  /**
148
149
  * BeginTransaction method
149
150
  *
@@ -152,7 +153,7 @@ public class Database {
152
153
  public Integer beginTransaction() throws Exception {
153
154
  if (_db.isOpen()) {
154
155
  try {
155
- if( isAvailTrans()) {
156
+ if (isAvailTrans()) {
156
157
  throw new Exception("Already in transaction");
157
158
  }
158
159
  _db.beginTransaction();
@@ -165,8 +166,8 @@ public class Database {
165
166
  } else {
166
167
  throw new Exception("Database not opened");
167
168
  }
168
-
169
169
  }
170
+
170
171
  /**
171
172
  * CommitTransaction method
172
173
  *
@@ -175,7 +176,7 @@ public class Database {
175
176
  public Integer commitTransaction() throws Exception {
176
177
  if (_db.isOpen()) {
177
178
  try {
178
- if(!isAvailTrans()) {
179
+ if (!isAvailTrans()) {
179
180
  throw new Exception("No transaction active");
180
181
  }
181
182
  _db.setTransactionSuccessful();
@@ -188,7 +189,6 @@ public class Database {
188
189
  } else {
189
190
  throw new Exception("Database not opened");
190
191
  }
191
-
192
192
  }
193
193
 
194
194
  /**
@@ -199,7 +199,7 @@ public class Database {
199
199
  public Integer rollbackTransaction() throws Exception {
200
200
  if (_db.isOpen()) {
201
201
  try {
202
- if( isAvailTrans()) {
202
+ if (isAvailTrans()) {
203
203
  _db.endTransaction();
204
204
  }
205
205
  return 0;
@@ -211,7 +211,6 @@ public class Database {
211
211
  } else {
212
212
  throw new Exception("Database not opened");
213
213
  }
214
-
215
214
  }
216
215
 
217
216
  /**
@@ -233,7 +232,7 @@ public class Database {
233
232
  int curVersion;
234
233
 
235
234
  String password = "";
236
- if (_encrypted && (_mode.equals("secret") || _mode.equals("encryption"))) {
235
+ if (_encrypted && (_mode.equals("secret") || _mode.equals("encryption") || _mode.equals("decryption"))) {
237
236
  if (!_uSecret.isPassphrase()) {
238
237
  throw new Exception("No Passphrase stored");
239
238
  }
@@ -252,6 +251,20 @@ public class Database {
252
251
  throw new Exception("No Encryption set in capacitor.config");
253
252
  }
254
253
  }
254
+ if (_mode.equals("decryption")) {
255
+ if (_isEncryption) {
256
+ try {
257
+ _uCipher.decrypt(_context, _file, SQLiteDatabase.getBytes(password.toCharArray()));
258
+ password = "";
259
+ } catch (Exception e) {
260
+ String msg = "Failed in decryption " + e.getMessage();
261
+ Log.v(TAG, msg);
262
+ throw new Exception(msg);
263
+ }
264
+ } else {
265
+ throw new Exception("No Encryption set in capacitor.config");
266
+ }
267
+ }
255
268
  try {
256
269
  if (!isNCDB() && !this._readOnly) {
257
270
  _db = SQLiteDatabase.openOrCreateDatabase(_file, password, null);
@@ -134,6 +134,68 @@ public class UtilsSQLCipher {
134
134
  }
135
135
  }
136
136
 
137
+ public void decrypt(Context ctxt, File originalFile, byte[] passphrase) throws IOException {
138
+ SQLiteDatabase.loadLibs(ctxt);
139
+
140
+ if (originalFile.exists()) {
141
+ // Create a temporary file for the decrypted database in the cache directory
142
+ File decryptedFile = File.createTempFile("sqlcipherutils", "tmp", ctxt.getCacheDir());
143
+
144
+ // Open the decrypted database
145
+ SQLiteDatabase decryptedDb = SQLiteDatabase.openDatabase(
146
+ decryptedFile.getAbsolutePath(),
147
+ "",
148
+ null,
149
+ SQLiteDatabase.OPEN_READWRITE
150
+ );
151
+
152
+ // Open the encrypted database with the provided passphrase
153
+ SQLiteDatabase encryptedDb = SQLiteDatabase.openDatabase(
154
+ originalFile.getAbsolutePath(),
155
+ new String(passphrase),
156
+ null,
157
+ SQLiteDatabase.OPEN_READWRITE
158
+ );
159
+
160
+ int version = encryptedDb.getVersion();
161
+ decryptedDb.setVersion(version);
162
+
163
+ decryptedDb.close();
164
+
165
+ // Attach the encrypted database to itself using an empty key
166
+ StringBuilder attachSql = new StringBuilder();
167
+ attachSql.append("ATTACH DATABASE ? AS plaintext KEY '';");
168
+ final SQLiteStatement attachStatement = encryptedDb.compileStatement(attachSql.toString());
169
+
170
+ attachStatement.bindString(1, decryptedFile.getAbsolutePath());
171
+ attachStatement.execute();
172
+
173
+ // Export data from the encrypted database to the plaintext database
174
+ StringBuilder exportSql = new StringBuilder();
175
+ exportSql.append("SELECT sqlcipher_export('plaintext');");
176
+ encryptedDb.rawExecSQL(exportSql.toString());
177
+
178
+ // Detach the plaintext database
179
+ StringBuilder detachSql = new StringBuilder();
180
+ detachSql.append("DETACH DATABASE plaintext;");
181
+ encryptedDb.rawExecSQL(detachSql.toString());
182
+
183
+ attachStatement.close();
184
+ encryptedDb.close();
185
+
186
+ boolean delFile = originalFile.delete();
187
+ if (!delFile) {
188
+ throw new FileNotFoundException(originalFile.getAbsolutePath() + " not deleted");
189
+ }
190
+ boolean renFile = decryptedFile.renameTo(originalFile);
191
+ if (!renFile) {
192
+ throw new FileNotFoundException(originalFile.getAbsolutePath() + " not renamed");
193
+ }
194
+ } else {
195
+ throw new FileNotFoundException(originalFile.getAbsolutePath() + " not found");
196
+ }
197
+ }
198
+
137
199
  public void changePassword(Context ctxt, File file, String password, String nwpassword) throws IOException {
138
200
  SQLiteDatabase.loadLibs(ctxt);
139
201
 
@@ -134,25 +134,31 @@ public class UtilsSQLStatement {
134
134
 
135
135
  public static List<String> extractColumnNames(String whereClause) {
136
136
  Set<String> keywords = new HashSet<>(Arrays.asList("AND", "OR", "IN", "VALUES", "LIKE", "BETWEEN", "NOT"));
137
- String[] tokens = whereClause.split("\\s|,|\\(|\\)");
138
137
 
138
+ Pattern pattern = Pattern.compile(
139
+ "\\b[a-zA-Z]\\w*\\b(?=\\s*(?:<=?|>=?|<>?|=|AND|OR|BETWEEN|NOT|IN|LIKE))|" +
140
+ "\\b[a-zA-Z]\\w*\\b\\s+BETWEEN\\s+'[^']+'\\s+AND\\s+'[^']+'|" +
141
+ "\\(([^)]+)\\)\\s+IN\\s+\\(\\s*VALUES\\s*\\("
142
+ );
143
+ Matcher matcher = pattern.matcher(whereClause);
139
144
  List<String> columns = new ArrayList<>();
140
- boolean inClause = false;
141
- boolean inValues = false;
142
-
143
- for (String token : tokens) {
144
- if (token.equals("IN")) {
145
- inClause = true;
146
- } else if (inClause && token.equals("(")) {
147
- inValues = true;
148
- } else if (inValues && token.equals(")")) {
149
- inValues = false;
150
- } else if (token.matches("\\b[a-zA-Z]\\w*\\b") && !inValues && !keywords.contains(token.toUpperCase())) {
151
- columns.add(token);
145
+
146
+ while (matcher.find()) {
147
+ String columnList = matcher.group(1);
148
+ if (columnList != null) {
149
+ String[] columnNamesArray = columnList.split(",");
150
+ for (String columnName : columnNamesArray) {
151
+ columns.add(columnName.trim());
152
+ }
153
+ } else {
154
+ String matchedText = matcher.group();
155
+ if (!keywords.contains(matchedText.trim().toUpperCase())) {
156
+ columns.add(matchedText.trim());
157
+ }
152
158
  }
153
159
  }
154
160
 
155
- return new ArrayList<>(new HashSet<>(columns));
161
+ return columns;
156
162
  }
157
163
 
158
164
  public static List<Integer> indicesOf(String str, String searchStr, int fromIndex) {
@@ -127,11 +127,11 @@ export interface CapacitorSQLitePlugin {
127
127
  */
128
128
  commitTransaction(options: capSQLiteOptions): Promise<capSQLiteChanges>;
129
129
  /**
130
- * Rollback Database Transaction
131
- * @param options
132
- * @returns capSQLiteChanges
133
- * @since 5.0.7
134
- */
130
+ * Rollback Database Transaction
131
+ * @param options
132
+ * @returns capSQLiteChanges
133
+ * @since 5.0.7
134
+ */
135
135
  rollbackTransaction(options: capSQLiteOptions): Promise<capSQLiteChanges>;
136
136
  /**
137
137
  * Is Database Transaction Active
@@ -1087,7 +1087,7 @@ export interface ISQLiteConnection {
1087
1087
  clearEncryptionSecret(): Promise<void>;
1088
1088
  /**
1089
1089
  * Check the passphrase stored in a secure store
1090
- * @param passphrase
1090
+ * @param oldPassphrase
1091
1091
  * @returns Promise<capSQLiteResult>
1092
1092
  * @since 4.6.1
1093
1093
  */
@@ -135,6 +135,12 @@ export class SQLiteConnection {
135
135
  const conn = new SQLiteDBConnection(database, readonly, this.sqlite);
136
136
  const connName = readonly ? `RO_${database}` : `RW_${database}`;
137
137
  this._connectionDict.set(connName, conn);
138
+ /*
139
+ console.log(`*** in createConnection connectionDict: ***`)
140
+ this._connectionDict.forEach((connection, key) => {
141
+ console.log(`Key: ${key}, Value: ${connection}`);
142
+ });
143
+ */
138
144
  return Promise.resolve(conn);
139
145
  }
140
146
  catch (err) {
@@ -148,6 +154,11 @@ export class SQLiteConnection {
148
154
  await this.sqlite.closeConnection({ database, readonly });
149
155
  const connName = readonly ? `RO_${database}` : `RW_${database}`;
150
156
  this._connectionDict.delete(connName);
157
+ /* console.log(`*** in closeConnection connectionDict: ***`)
158
+ this._connectionDict.forEach((connection, key) => {
159
+ console.log(`Key: ${key}, Value: ${connection}`);
160
+ });
161
+ */
151
162
  return Promise.resolve();
152
163
  }
153
164
  catch (err) {
@@ -251,6 +262,11 @@ export class SQLiteConnection {
251
262
  async closeAllConnections() {
252
263
  const delDict = new Map();
253
264
  try {
265
+ /* console.log(`*** in closeAllConnections connectionDict: ***`)
266
+ this._connectionDict.forEach((connection, key) => {
267
+ console.log(`Key: ${key}, Value: ${connection}`);
268
+ });
269
+ */
254
270
  for (const key of this._connectionDict.keys()) {
255
271
  const database = key.substring(3);
256
272
  const readonly = key.substring(0, 3) === 'RO_' ? true : false;
@@ -263,6 +279,7 @@ export class SQLiteConnection {
263
279
  return Promise.resolve();
264
280
  }
265
281
  catch (err) {
282
+ console.log(`in definition closeAllConnections err: `, err);
266
283
  return Promise.reject(err);
267
284
  }
268
285
  }
@@ -470,8 +487,9 @@ export class SQLiteDBConnection {
470
487
  }
471
488
  async beginTransaction() {
472
489
  try {
473
- const changes = await this.sqlite
474
- .beginTransaction({ database: this.dbName });
490
+ const changes = await this.sqlite.beginTransaction({
491
+ database: this.dbName,
492
+ });
475
493
  return Promise.resolve(changes);
476
494
  }
477
495
  catch (err) {
@@ -480,8 +498,9 @@ export class SQLiteDBConnection {
480
498
  }
481
499
  async commitTransaction() {
482
500
  try {
483
- const changes = await this.sqlite
484
- .commitTransaction({ database: this.dbName });
501
+ const changes = await this.sqlite.commitTransaction({
502
+ database: this.dbName,
503
+ });
485
504
  return Promise.resolve(changes);
486
505
  }
487
506
  catch (err) {
@@ -490,8 +509,9 @@ export class SQLiteDBConnection {
490
509
  }
491
510
  async rollbackTransaction() {
492
511
  try {
493
- const changes = await this.sqlite
494
- .rollbackTransaction({ database: this.dbName });
512
+ const changes = await this.sqlite.rollbackTransaction({
513
+ database: this.dbName,
514
+ });
495
515
  return Promise.resolve(changes);
496
516
  }
497
517
  catch (err) {
@@ -500,8 +520,9 @@ export class SQLiteDBConnection {
500
520
  }
501
521
  async isTransactionActive() {
502
522
  try {
503
- const result = await this.sqlite
504
- .isTransactionActive({ database: this.dbName });
523
+ const result = await this.sqlite.isTransactionActive({
524
+ database: this.dbName,
525
+ });
505
526
  return Promise.resolve(result);
506
527
  }
507
528
  catch (err) {
@@ -578,7 +599,7 @@ export class SQLiteDBConnection {
578
599
  statements: statements,
579
600
  transaction: transaction,
580
601
  readonly: false,
581
- isSQL92: isSQL92
602
+ isSQL92: isSQL92,
582
603
  });
583
604
  return Promise.resolve(res);
584
605
  }
@@ -599,7 +620,7 @@ export class SQLiteDBConnection {
599
620
  statement: statement,
600
621
  values: values,
601
622
  readonly: this.readonly,
602
- isSql92: true
623
+ isSql92: true,
603
624
  });
604
625
  }
605
626
  else {
@@ -608,7 +629,7 @@ export class SQLiteDBConnection {
608
629
  statement: statement,
609
630
  values: [],
610
631
  readonly: this.readonly,
611
- isSQL92: isSQL92
632
+ isSQL92: isSQL92,
612
633
  });
613
634
  }
614
635
  // reorder rows for ios
@@ -634,7 +655,7 @@ export class SQLiteDBConnection {
634
655
  transaction: transaction,
635
656
  readonly: false,
636
657
  returnMode: mRetMode,
637
- isSQL92: true
658
+ isSQL92: true,
638
659
  });
639
660
  // }
640
661
  }
@@ -649,7 +670,7 @@ export class SQLiteDBConnection {
649
670
  transaction: transaction,
650
671
  readonly: false,
651
672
  returnMode: mRetMode,
652
- isSQL92: isSQL92
673
+ isSQL92: isSQL92,
653
674
  });
654
675
  }
655
676
  // reorder rows for ios
@@ -674,7 +695,7 @@ export class SQLiteDBConnection {
674
695
  transaction: transaction,
675
696
  readonly: false,
676
697
  returnMode: returnMode,
677
- isSQL92: isSQL92
698
+ isSQL92: isSQL92,
678
699
  });
679
700
  // }
680
701
  // reorder rows for ios
@@ -830,10 +851,10 @@ export class SQLiteDBConnection {
830
851
  if (!this.readonly) {
831
852
  try {
832
853
  await this.sqlite.beginTransaction({
833
- database: this.dbName
854
+ database: this.dbName,
834
855
  });
835
856
  isActive = await this.sqlite.isTransactionActive({
836
- database: this.dbName
857
+ database: this.dbName,
837
858
  });
838
859
  if (!isActive) {
839
860
  return Promise.reject('After Begin Transaction, no transaction active');
@@ -855,7 +876,7 @@ export class SQLiteDBConnection {
855
876
  transaction: false,
856
877
  readonly: false,
857
878
  returnMode: retMode,
858
- isSQL92: isSQL92
879
+ isSQL92: isSQL92,
859
880
  });
860
881
  if (ret.changes.changes <= 0) {
861
882
  throw new Error('Error in transaction method run ');
@@ -870,7 +891,7 @@ export class SQLiteDBConnection {
870
891
  readonly: false,
871
892
  });
872
893
  isActive = await this.sqlite.isTransactionActive({
873
- database: this.dbName
894
+ database: this.dbName,
874
895
  });
875
896
  if (ret.changes.changes < 0) {
876
897
  throw new Error('Error in transaction method execute ');
@@ -879,11 +900,11 @@ export class SQLiteDBConnection {
879
900
  }
880
901
  }
881
902
  isActive = await this.sqlite.isTransactionActive({
882
- database: this.dbName
903
+ database: this.dbName,
883
904
  });
884
905
  if (isActive) {
885
906
  const retC = await this.sqlite.commitTransaction({
886
- database: this.dbName
907
+ database: this.dbName,
887
908
  });
888
909
  changes += retC.changes.changes;
889
910
  }
@@ -893,7 +914,7 @@ export class SQLiteDBConnection {
893
914
  catch (err) {
894
915
  const msg = err.message ? err.message : err;
895
916
  isActive = await this.sqlite.isTransactionActive({
896
- database: this.dbName
917
+ database: this.dbName,
897
918
  });
898
919
  if (isActive) {
899
920
  await this.sqlite.rollbackTransaction({