@@ -4,146 +4,114 @@ import android.database.sqlite.SQLiteException
44import android.os.Build
55import androidx.appcompat.app.AppCompatDelegate
66import androidx.multidex.MultiDexApplication
7- import androidx.sqlite. db.SupportSQLiteDatabase
7+ import app.cash.sqldelight. db.QueryResult
88import app.cash.sqldelight.driver.android.AndroidSqliteDriver
99import com.facebook.flipper.android.AndroidFlipperClient
1010import com.facebook.flipper.core.FlipperClient
1111import com.facebook.soloader.SoLoader
12- import eu.pretix.libpretixsync.Models
1312import eu.pretix.libpretixsync.check.AsyncCheckProvider
1413import eu.pretix.libpretixsync.check.OnlineCheckProvider
1514import eu.pretix.libpretixsync.check.ProxyCheckProvider
1615import eu.pretix.libpretixsync.check.TicketCheckProvider
17- import eu.pretix.libpretixsync.db.Migrations
1816import eu.pretix.libpretixsync.sqldelight.AndroidUtilDateAdapter
1917import eu.pretix.libpretixsync.sqldelight.BigDecimalAdapter
2018import eu.pretix.pretixscan.droid.connectivity.ConnectivityHelper
2119import eu.pretix.pretixscan.sqldelight.SyncDatabase
2220import eu.pretix.pretixscan.utils.KeystoreHelper
21+ import eu.pretix.pretixscan.utils.createDriver
2322import eu.pretix.pretixscan.utils.createSyncDatabase
2423import eu.pretix.pretixscan.utils.readVersionPragma
25- import io.requery.BlockingEntityStore
26- import io.requery.Persistable
27- import io.requery.sql.EntityDataStore
2824import net.zetetic.database.sqlcipher.SQLiteConnection
2925import net.zetetic.database.sqlcipher.SQLiteDatabase
3026import net.zetetic.database.sqlcipher.SQLiteDatabaseHook
31- import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
3227import java.util.concurrent.locks.ReentrantLock
3328
3429
3530class PretixScan : MultiDexApplication () {
36- private var dataStore: BlockingEntityStore <Persistable >? = null
3731 val fileStorage = AndroidFileStorage (this )
3832 val syncLock = ReentrantLock ()
3933 var flipperInit: FlipperInitializer .IntializationResult ? = null
4034 lateinit var connectivityHelper: ConnectivityHelper
4135
42- private fun migrateSqlCipher (name : String , dbPass : String ) {
36+ private fun migrateSqlCipher (name : String , dbPass : String , driver : AndroidSqliteDriver ): Boolean {
4337 System .loadLibrary(" sqlcipher" )
4438
45- val databaseFile = getDatabasePath(name)
46- SQLiteDatabase .openOrCreateDatabase(databaseFile, dbPass, null , null , object : SQLiteDatabaseHook {
47- override fun preKey (connection : SQLiteConnection ) {
48- }
49-
50- override fun postKey (connection : SQLiteConnection ) {
51- val result = connection.executeForLong(" PRAGMA cipher_migrate;" , emptyArray(), null )
52- if (result != 0L ) {
53- throw SQLiteException (" cipher_migrate failed" )
54- }
55- }
56- }).close()
57- }
39+ try {
40+ driver.executeQuery(
41+ identifier = null ,
42+ sql = " select count(*) from sqlite_master;" ,
43+ mapper = { cursor ->
44+ cursor.next()
45+ QueryResult .Value (cursor.getLong(0 ))
46+ },
47+ parameters = 0 ,
48+ )
49+ return true
50+ } catch (e: SQLiteException ) {
51+ try {
52+ val databaseFile = getDatabasePath(name)
53+ SQLiteDatabase .openOrCreateDatabase(
54+ databaseFile,
55+ dbPass,
56+ null ,
57+ null ,
58+ object : SQLiteDatabaseHook {
59+ override fun preKey (connection : SQLiteConnection ) {
60+ }
5861
59- val data: BlockingEntityStore <Persistable >
60- get() {
61- if (dataStore == null ) {
62- if (BuildConfig .DEBUG ) {
63- // Do not encrypt on debug, because it breaks Stetho
64- val source = AndroidDatabaseSource (this , Models .DEFAULT , Migrations .CURRENT_VERSION )
65- source.setLoggingEnabled(BuildConfig .DEBUG )
66- val configuration = source.configuration
67- dataStore = EntityDataStore (configuration)
68- } else {
69- val dbPass = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) KeystoreHelper .secureValue(KEYSTORE_PASSWORD , true )
70- else KEYSTORE_PASSWORD
71-
72- var source = AndroidSqlCipherDatabaseSource (
73- this ,
74- Models .DEFAULT ,
75- Models .DEFAULT .name,
76- dbPass,
77- Migrations .CURRENT_VERSION
78- )
79- try {
80- // check if database has been decrypted
81- source.readableDatabase.rawQuery(" select count(*) from sqlite_master;" , emptyArray())
82- } catch (e: SQLiteException ) {
83- try {
84- source.close()
85- migrateSqlCipher(Models .DEFAULT .name, dbPass)
86- source = AndroidSqlCipherDatabaseSource (
87- this ,
88- Models .DEFAULT ,
89- Models .DEFAULT .name,
90- dbPass,
91- Migrations .CURRENT_VERSION
92- )
93- source.readableDatabase.rawQuery(" select count(*) from sqlite_master;" , emptyArray())
94- } catch (e: SQLiteException ) {
95- // still not decrypted? then we probably lost the key due to a keystore issue
96- // let's start fresh, there's no reasonable other way to let the user out of this
97- this .deleteDatabase(Models .DEFAULT .getName())
98- source = AndroidSqlCipherDatabaseSource (
99- this ,
100- Models .DEFAULT ,
101- Models .DEFAULT .name,
102- dbPass,
103- Migrations .CURRENT_VERSION
62+ override fun postKey (connection : SQLiteConnection ) {
63+ val result = connection.executeForLong(
64+ " PRAGMA cipher_migrate;" ,
65+ emptyArray(),
66+ null
10467 )
68+ if (result != 0L ) {
69+ throw SQLiteException (" cipher_migrate failed" )
70+ }
10571 }
106- }
107- source.setLoggingEnabled(false )
108-
109- val configuration = source.configuration
110- dataStore = EntityDataStore (configuration)
111- }
72+ }).close()
73+ return true
74+ } catch (e: SQLiteException ) {
75+ // still not decrypted? then we probably lost the key due to a keystore issue
76+ // let's start fresh, there's no reasonable other way to let the user out of this
77+ this .deleteDatabase(name)
78+ return false
11279 }
113- return dataStore!!
11480 }
81+ }
11582
11683 val db: SyncDatabase by lazy {
117- // Access data to init schema through requery if it hasn't been created already
118- data.raw(" PRAGMA user_version;" ).first()
84+ val dbName = " default"
11985
12086 val dbPass = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) KeystoreHelper .secureValue(KEYSTORE_PASSWORD , true )
12187 else KEYSTORE_PASSWORD
12288
12389 val androidDriver = if (BuildConfig .DEBUG ) {
124- AndroidSqliteDriver (
125- schema = SyncDatabase .Schema ,
90+ createDriver(
12691 context = this .applicationContext,
127- name = " default" ,
128- callback = object : AndroidSqliteDriver .Callback (SyncDatabase .Schema ) {
129- override fun onOpen (db : SupportSQLiteDatabase ) {
130- db.setForeignKeyConstraintsEnabled(true )
131- }
132- },
92+ dbName = dbName,
93+ dbPass = null ,
13394 )
13495 } else {
13596 System .loadLibrary(" sqlcipher" )
136- AndroidSqliteDriver (
137- schema = SyncDatabase .Schema ,
97+ val driver = createDriver(
13898 context = this .applicationContext,
139- name = " default" ,
140- callback = object : AndroidSqliteDriver .Callback (SyncDatabase .Schema ) {
141- override fun onOpen (db : SupportSQLiteDatabase ) {
142- db.setForeignKeyConstraintsEnabled(true )
143- }
144- },
145- factory = SupportOpenHelperFactory (dbPass.toByteArray())
99+ dbName = dbName,
100+ dbPass = dbPass,
146101 )
102+
103+ val migrationSuccess = migrateSqlCipher(dbName, dbPass, driver)
104+ if (migrationSuccess) {
105+ driver
106+ } else {
107+ // Re-open database if we had to delete it during migration
108+ driver.close()
109+ createDriver(
110+ context = this .applicationContext,
111+ dbName = dbName,
112+ dbPass = dbPass,
113+ )
114+ }
147115 }
148116
149117 // Uncomment LogSqliteDriver for verbose logging
0 commit comments