]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.kt
First wrong button text in View Headers in night theme. https://redmine.stoutner...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / helpers / DomainsDatabaseHelper.kt
1 /*
2  * Copyright 2017-2024 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
5  *
6  * Privacy Browser Android is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Browser Android is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.helpers
21
22 import android.content.ContentValues
23 import android.content.Context
24 import android.database.Cursor
25 import android.database.DatabaseUtils
26 import android.database.sqlite.SQLiteDatabase
27 import android.database.sqlite.SQLiteOpenHelper
28
29 import androidx.preference.PreferenceManager
30
31 import com.stoutner.privacybrowser.R
32
33 // Define the class constants.
34 private const val SCHEMA_VERSION = 17
35
36 // Define the public database constants.
37 const val DOMAINS_DATABASE = "domains.db"
38 const val DOMAINS_TABLE = "domains"
39
40 // Define the public spinner constants.
41 const val SYSTEM_DEFAULT = 0
42 const val ENABLED = 1
43 const val DISABLED = 2
44 const val LIGHT_THEME = 1
45 const val DARK_THEME = 2
46
47 // Define the public schema constants.
48 const val BLOCK_ALL_THIRD_PARTY_REQUESTS = "blockallthirdpartyrequests"
49 const val COOKIES = "cookies"
50 const val DISPLAY_IMAGES = "displayimages"
51 const val DOMAIN_NAME = "domainname"
52 const val ENABLE_DOM_STORAGE = "enabledomstorage"
53 const val ENABLE_EASYLIST = "enableeasylist"
54 const val ENABLE_EASYPRIVACY = "enableeasyprivacy"
55 const val ENABLE_FANBOYS_ANNOYANCE_LIST = "enablefanboysannoyancelist"
56 const val ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = "enablefanboyssocialblockinglist"
57 const val ENABLE_JAVASCRIPT = "enablejavascript"
58 const val ENABLE_ULTRAPRIVACY = "enableultraprivacy"
59 const val FONT_SIZE = "fontsize"
60 const val ID = "_id"
61 const val IP_ADDRESSES = "ip_addresses"
62 const val PINNED_IP_ADDRESSES = "pinned_ip_addresses"
63 const val PINNED_SSL_CERTIFICATE = "pinnedsslcertificate"
64 const val SSL_END_DATE = "sslenddate"
65 const val SSL_START_DATE = "sslstartdate"
66 const val SSL_ISSUED_BY_COMMON_NAME = "sslissuedbycommonname"
67 const val SSL_ISSUED_BY_ORGANIZATION = "sslissuedbyorganization"
68 const val SSL_ISSUED_BY_ORGANIZATIONAL_UNIT = "sslissuedbyorganizationalunit"
69 const val SSL_ISSUED_TO_COMMON_NAME = "sslissuedtocommonname"
70 const val SSL_ISSUED_TO_ORGANIZATION = "sslissuedtoorganization"
71 const val SSL_ISSUED_TO_ORGANIZATIONAL_UNIT = "sslissuedtoorganizationalunit"
72 const val SWIPE_TO_REFRESH = "swipetorefresh"
73 const val ULTRALIST = "ultralist"
74 const val USER_AGENT = "useragent"
75 const val WEBVIEW_THEME = "webview_theme"
76 const val WIDE_VIEWPORT = "wide_viewport"
77
78 // Define the public table creation constant.
79 const val CREATE_DOMAINS_TABLE = "CREATE TABLE $DOMAINS_TABLE (" +
80         "$ID INTEGER PRIMARY KEY, " +
81         "$DOMAIN_NAME TEXT, " +
82         "$ENABLE_JAVASCRIPT INTEGER, " +
83         "$COOKIES INTEGER, " +
84         "$ENABLE_DOM_STORAGE INTEGER, " +
85         "$USER_AGENT TEXT, " +
86         "$ENABLE_EASYLIST INTEGER, " +
87         "$ENABLE_EASYPRIVACY INTEGER, " +
88         "$ENABLE_FANBOYS_ANNOYANCE_LIST INTEGER, " +
89         "$ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST INTEGER, " +
90         "$ULTRALIST INTEGER, " +
91         "$ENABLE_ULTRAPRIVACY INTEGER, " +
92         "$BLOCK_ALL_THIRD_PARTY_REQUESTS INTEGER, " +
93         "$FONT_SIZE INTEGER, " +
94         "$SWIPE_TO_REFRESH INTEGER, " +
95         "$WEBVIEW_THEME INTEGER, " +
96         "$WIDE_VIEWPORT INTEGER, " +
97         "$DISPLAY_IMAGES INTEGER, " +
98         "$PINNED_SSL_CERTIFICATE BOOLEAN, " +
99         "$SSL_ISSUED_TO_COMMON_NAME TEXT, " +
100         "$SSL_ISSUED_TO_ORGANIZATION TEXT, " +
101         "$SSL_ISSUED_TO_ORGANIZATIONAL_UNIT TEXT, " +
102         "$SSL_ISSUED_BY_COMMON_NAME TEXT, " +
103         "$SSL_ISSUED_BY_ORGANIZATION TEXT, " +
104         "$SSL_ISSUED_BY_ORGANIZATIONAL_UNIT TEXT, " +
105         "$SSL_START_DATE INTEGER, " +
106         "$SSL_END_DATE INTEGER, " +
107         "$PINNED_IP_ADDRESSES BOOLEAN, " +
108         "$IP_ADDRESSES TEXT)"
109
110 class DomainsDatabaseHelper(private val appContext: Context) : SQLiteOpenHelper(appContext, DOMAINS_DATABASE, null, SCHEMA_VERSION) {
111     override fun onCreate(domainsDatabase: SQLiteDatabase) {
112         // Create the domains table.
113         domainsDatabase.execSQL(CREATE_DOMAINS_TABLE)
114     }
115
116     override fun onUpgrade(domainsDatabase: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
117         // Get a handle for the shared preference.
118         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext)
119
120         // Upgrade from schema version 1, first used in Privacy Browser 2.0, to schema version 2, first used in Privacy Browser 2.3.
121         if (oldVersion < 2) {
122             // Add the display images column.
123             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $DISPLAY_IMAGES INTEGER")
124         }
125
126         // Upgrade from schema version 2, first used in Privacy Browser 2.3, to schema version 3, first used in Privacy Browser 2.5.
127         if (oldVersion < 3) {
128             //  Add the SSL certificate columns.
129             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $PINNED_SSL_CERTIFICATE BOOLEAN")
130             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_COMMON_NAME TEXT")
131             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_ORGANIZATION TEXT")
132             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_TO_ORGANIZATIONAL_UNIT TEXT")
133             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_COMMON_NAME TEXT")
134             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_ORGANIZATION TEXT")
135             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_ISSUED_BY_ORGANIZATIONAL_UNIT TEXT")
136             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_START_DATE INTEGER")
137             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SSL_END_DATE INTEGER")
138         }
139
140         // Upgrade from schema version 3, first used in Privacy Browser 2.5, to schema version 4, first used in Privacy Browser 2.6.
141         if (oldVersion < 4) {
142             // Add the night mode column.
143             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN nightmode INTEGER")
144         }
145
146         // Upgrade from schema version 4, first used in Privacy Browser 2.6, to schema version 5, first used in Privacy Browser 2.9.
147         if (oldVersion < 5) {
148             // Add the block lists columns.
149             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_EASYLIST BOOLEAN")
150             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_EASYPRIVACY BOOLEAN")
151             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_FANBOYS_ANNOYANCE_LIST BOOLEAN")
152             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST BOOLEAN")
153
154             // Get the default filter list settings.
155             val easyListEnabled = sharedPreferences.getBoolean(appContext.getString(R.string.easylist_key), true)
156             val easyPrivacyEnabled = sharedPreferences.getBoolean(appContext.getString(R.string.easyprivacy_key), true)
157             val fanboyAnnoyanceListEnabled = sharedPreferences.getBoolean(appContext.getString(R.string.fanboys_annoyance_list_key), true)
158             val fanboySocialBlockingListEnabled = sharedPreferences.getBoolean(appContext.getString(R.string.fanboys_social_blocking_list_key), true)
159
160             // Set EasyList for existing rows according to the current system-wide default.
161             // This can switch to using the variables directly once the API >= 30.  <https://www.sqlite.org/datatype3.html#boolean_datatype>
162             // <https://developer.android.com/reference/android/database/sqlite/package-summary>
163             if (easyListEnabled) {
164                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYLIST = 1")
165             } else {
166                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYLIST = 0")
167             }
168
169             // Set EasyPrivacy for existing rows according to the current system-wide default.
170             if (easyPrivacyEnabled) {
171                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYPRIVACY = 1")
172             } else {
173                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_EASYPRIVACY = 0")
174             }
175
176             // Set Fanboy's Annoyance List for existing rows according to the current system-wide default.
177             if (fanboyAnnoyanceListEnabled) {
178                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_ANNOYANCE_LIST = 1")
179             } else {
180                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_ANNOYANCE_LIST = 0")
181             }
182
183             // Set Fanboy's Social Blocking List for existing rows according to the current system-wide default.
184             if (fanboySocialBlockingListEnabled) {
185                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = 1")
186             } else {
187                 domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST = 0")
188             }
189         }
190
191         // Upgrade from schema version 5, first used in Privacy Browser 2.9, to schema version 6, first used in Privacy Browser 2.11.
192         if (oldVersion < 6) {
193             // Add the swipe to refresh column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
194             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $SWIPE_TO_REFRESH INTEGER")
195         }
196
197         // Upgrade from schema version 6, first used in Privacy Browser 2.11, to schema version 7, first used in Privacy Browser 2.12.
198         if (oldVersion < 7) {
199             // Add the block all third-party requests column.  This defaults to `0`, which is off, so a separate step isn't needed to populate the column.
200             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $BLOCK_ALL_THIRD_PARTY_REQUESTS BOOLEAN")
201         }
202
203         // Upgrade from schema version 7, first used in Privacy Browser 2.12, to schema version 8, first used in Privacy Browser 2.12.
204         // For some reason (lack of planning or attention to detail), the 2.12 update included two schema version jumps.
205         if (oldVersion < 8) {
206             // Add the UltraPrivacy column.
207             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ENABLE_ULTRAPRIVACY BOOLEAN")
208
209             // Enable it for all existing rows.
210             domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ENABLE_ULTRAPRIVACY = 1")
211         }
212
213         // Upgrade from schema version 8, first used in Privacy Browser 2.12, to schema version 9, first used in Privacy Browser 2.16.
214         if (oldVersion < 9) {
215             // Add the pinned IP addresses columns.  These default to `0` and `""`, so a separate step isn't needed to populate the columns.
216             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $PINNED_IP_ADDRESSES BOOLEAN")
217             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $IP_ADDRESSES TEXT")
218         }
219
220         // Upgrade from schema version 9, first used in Privacy Browser 2.16, to schema version 10, first used in Privacy Browser 3.1.
221         if (oldVersion < 10) {
222             // Add the wide viewport column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
223             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $WIDE_VIEWPORT INTEGER")
224         }
225
226         // Upgrade from schema version 10, first used in Privacy Browser 3.1, to schema version 11, first used in Privacy Browser 3.2.
227         if (oldVersion < 11) {
228             // Add the UltraList column.
229             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $ULTRALIST BOOLEAN")
230
231             // Enable it for all existing rows.
232             domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $ULTRALIST = 1")
233         }
234
235         // Upgrade from schema version 11, first used in Privacy Browser 3.2, to schema version 12, first used in Privacy Browser 3.5.
236         if (oldVersion < 12) {
237             // Add the WebView theme column.  This defaults to `0`, which is `System default`, so a separate step isn't needed to populate the column.
238             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $WEBVIEW_THEME INTEGER")
239
240             // `night_mode` was removed.
241             // SQLite amazingly only added a command to drop a column in version 3.35.0.  <https://www.sqlite.org/changes.html>
242             // It will be a while before that is supported in Android.  <https://developer.android.com/reference/android/database/sqlite/package-summary>
243             // Although a new table could be created and all the data copied to it, I think I will just leave the old night mode column.  It will be wiped out the next time an import is run.
244         }
245
246         // Upgrade from schema version 12, first used in Privacy Browser 3.5, to schema version 13, first used in Privacy Browser 3.8.
247         if (oldVersion < 13) {
248             // Add the cookies column.
249             domainsDatabase.execSQL("ALTER TABLE $DOMAINS_TABLE ADD COLUMN $COOKIES BOOLEAN")
250
251             // Copy the data from the old column to the new one.
252             domainsDatabase.execSQL("UPDATE $DOMAINS_TABLE SET $COOKIES = enablefirstpartycookies")
253         }
254
255         // Upgrade from schema version 13, first used in Privacy Browser 3.8, to schema version 14, first used in Privacy Browser 3.11.
256         // This upgrade used to add the X-Requested-With header, but that was removed in schema version 15.
257
258         // Upgrade from schema version 14, first used in Privacy Browser 3.11, to schema version 15, first used in Privacy Browser 3.12.
259         // This upgrade removed `x_requested_with_header`.
260         // SQLite amazingly only added a command to drop a column in version 3.35.0.  <https://www.sqlite.org/changes.html>
261         // It will be a while before that is supported in Android.  <https://developer.android.com/reference/android/database/sqlite/package-summary>
262         // Although a new table could be created and all the data copied to it, I think I will just leave the X-Requested-With column.  It will be wiped out the next time an import is run.
263
264         // Upgrade from schema version 15, first used in Privacy Browser 3.12, to schema version 16, first used in Privacy Browser 3.15.
265         if (oldVersion < 16) {
266             // Get the current switch default values.
267             val javaScriptDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.javascript_key), false)
268             val cookiesDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.cookies_key), false)
269             val domStorageDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.dom_storage_key), false)
270             val easyListDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.easylist_key), true)
271             val easyPrivacyDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.easyprivacy_key), true)
272             val fanboysAnnoyanceListDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.fanboys_annoyance_list_key), true)
273             val fanboysSocialBlockingListDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.fanboys_social_blocking_list), true)
274             val ultraListDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.ultralist_key), true)
275             val ultraPrivacyDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.ultraprivacy_key), true)
276             val blockAllThirdPartyRequestsDefaultValue = sharedPreferences.getBoolean(appContext.getString(R.string.block_all_third_party_requests_key), false)
277
278             // Get a domains cursor.
279             val domainsCursor = domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE", null)
280
281             // Get the domains column indexes.
282             val javaScriptColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_JAVASCRIPT)
283             val cookiesColumnIndex = domainsCursor.getColumnIndexOrThrow(COOKIES)
284             val domStorageColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_DOM_STORAGE)
285             val easyListColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_EASYLIST)
286             val easyPrivacyColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_EASYPRIVACY)
287             val fanboysAnnoyanceListColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_FANBOYS_ANNOYANCE_LIST)
288             val fanboysSocialBlockingListColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST)
289             val ultraListColumnIndex = domainsCursor.getColumnIndexOrThrow(ULTRALIST)
290             val ultraPrivacyColumnIndex = domainsCursor.getColumnIndexOrThrow(ENABLE_ULTRAPRIVACY)
291             val blockAllThirdPartyRequestsColumnIndex = domainsCursor.getColumnIndexOrThrow(BLOCK_ALL_THIRD_PARTY_REQUESTS)
292
293             // Convert the domain from the switch booleans to the spinner integers.
294             for (i in 0 until domainsCursor.count) {
295                 // Move to the current record.
296                 domainsCursor.moveToPosition(i)
297
298                 // Get the domain current values.
299                 val javaScriptDomainCurrentValue = domainsCursor.getInt(javaScriptColumnIndex)
300                 val cookiesDomainCurrentValue = domainsCursor.getInt(cookiesColumnIndex)
301                 val domStorageDomainCurrentValue = domainsCursor.getInt(domStorageColumnIndex)
302                 val easyListDomainCurrentValue = domainsCursor.getInt(easyListColumnIndex)
303                 val easyPrivacyDomainCurrentValue = domainsCursor.getInt(easyPrivacyColumnIndex)
304                 val fanboysAnnoyanceListCurrentValue = domainsCursor.getInt(fanboysAnnoyanceListColumnIndex)
305                 val fanboysSocialBlockingListCurrentValue = domainsCursor.getInt(fanboysSocialBlockingListColumnIndex)
306                 val ultraListCurrentValue = domainsCursor.getInt(ultraListColumnIndex)
307                 val ultraPrivacyCurrentValue = domainsCursor.getInt(ultraPrivacyColumnIndex)
308                 val blockAllThirdPartyRequestsCurrentValue = domainsCursor.getInt(blockAllThirdPartyRequestsColumnIndex)
309
310                 // Instantiate a domain content values.
311                 val domainContentValues = ContentValues()
312
313                 // Populate the domain content values.
314                 domainContentValues.put(ENABLE_JAVASCRIPT, convertFromSwitchToSpinner(javaScriptDefaultValue, javaScriptDomainCurrentValue))
315                 domainContentValues.put(COOKIES, convertFromSwitchToSpinner(cookiesDefaultValue, cookiesDomainCurrentValue))
316                 domainContentValues.put(ENABLE_DOM_STORAGE, convertFromSwitchToSpinner(domStorageDefaultValue, domStorageDomainCurrentValue))
317                 domainContentValues.put(ENABLE_EASYLIST, convertFromSwitchToSpinner(easyListDefaultValue, easyListDomainCurrentValue))
318                 domainContentValues.put(ENABLE_EASYPRIVACY, convertFromSwitchToSpinner(easyPrivacyDefaultValue, easyPrivacyDomainCurrentValue))
319                 domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, convertFromSwitchToSpinner(fanboysAnnoyanceListDefaultValue, fanboysAnnoyanceListCurrentValue))
320                 domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, convertFromSwitchToSpinner(fanboysSocialBlockingListDefaultValue, fanboysSocialBlockingListCurrentValue))
321                 domainContentValues.put(ULTRALIST, convertFromSwitchToSpinner(ultraListDefaultValue, ultraListCurrentValue))
322                 domainContentValues.put(ENABLE_ULTRAPRIVACY, convertFromSwitchToSpinner(ultraPrivacyDefaultValue, ultraPrivacyCurrentValue))
323                 domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, convertFromSwitchToSpinner(blockAllThirdPartyRequestsDefaultValue, blockAllThirdPartyRequestsCurrentValue))
324
325                 // Get the current database ID.
326                 val currentDatabaseId = domainsCursor.getInt(domainsCursor.getColumnIndexOrThrow(ID))
327
328                 // Update the row for the specified database ID.
329                 domainsDatabase.update(DOMAINS_TABLE, domainContentValues, "$ID = $currentDatabaseId", null)
330             }
331
332             // Upgrade from schema version 16, first used in Privacy Browser 3.15, to schema version 17, first used in Privacy Browser 3.18.
333             // This upgrade removed `enableformdata`.
334             // SQLite amazingly only added a command to drop a column in version 3.35.0.  <https://www.sqlite.org/changes.html>
335             // That will not be supported in Android until the minimum API >= 34.  <https://developer.android.com/reference/android/database/sqlite/package-summary>
336             // Although a new table could be created and all the data copied to it, I think I will just leave the `enableformdata` column.  It will be wiped out the next time an import is run.
337
338             // Close the cursor.
339             domainsCursor.close()
340         }
341     }
342
343     val completeCursorOrderedByDomain: Cursor
344         get() {
345             // Get a readable database handle.
346             val domainsDatabase = this.readableDatabase
347
348             // Return everything in the domains table ordered by the domain name.  The cursor can't be closed because it is needed in the calling activity.
349             return domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE ORDER BY $DOMAIN_NAME ASC", null)
350         }
351
352     val domainNameCursorOrderedByDomain: Cursor
353         get() {
354             // Get a readable database handle.
355             val domainsDatabase = this.readableDatabase
356
357             // Return the database id and the domain name in the domains table ordered by the domain name.  The cursor can't be closed because it is needed in the calling activity.
358             return domainsDatabase.rawQuery("SELECT $ID, $DOMAIN_NAME FROM $DOMAINS_TABLE ORDER BY $DOMAIN_NAME ASC", null)
359         }
360     
361     // This method is used to convert the old domain settings switches to spinners.
362     private fun convertFromSwitchToSpinner(systemDefault: Boolean, currentDatabaseInteger: Int): Int {
363         // Return the new spinner integer.
364         return if ((!systemDefault && (currentDatabaseInteger == 0)) ||
365                     (systemDefault && (currentDatabaseInteger == 1)))  // The system default is currently selected.
366             SYSTEM_DEFAULT
367         else if (currentDatabaseInteger == 0)  // The switch is currently disabled and that is not the system default.
368             DISABLED
369         else  // The switch is currently enabled and that is not the system default.
370             ENABLED
371     }
372
373     fun getDomainNameCursorOrderedByDomainExcept(databaseId: Int): Cursor {
374         // Get a readable database handle.
375         val domainsDatabase = this.readableDatabase
376
377         // Return a cursor with the database IDs and domain names except for the specified ID ordered by domain name.  The cursor can't be closed because it is needed in the calling activity.
378         return domainsDatabase.rawQuery("SELECT $ID, $DOMAIN_NAME FROM $DOMAINS_TABLE WHERE $ID IS NOT $databaseId ORDER BY $DOMAIN_NAME ASC", null)
379     }
380
381     fun getCursorForId(databaseId: Int): Cursor {
382         // Get a readable database handle.
383         val domainsDatabase = this.readableDatabase
384
385         // Return a cursor for the specified database ID.  The cursor can't be closed because it is needed in the calling activity.
386         return domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE WHERE $ID = $databaseId", null)
387     }
388
389     fun getCursorForDomainName(domainName: String): Cursor {
390         // Get a readable database handle.
391         val domainsDatabase = this.readableDatabase
392
393         // SQL escape the domain name.
394         val sqlEscapedDomainName = DatabaseUtils.sqlEscapeString(domainName)
395
396         // Return a cursor for the requested domain name.  The cursor can't be closed because it is needed in the calling activity.
397         return domainsDatabase.rawQuery("SELECT * FROM $DOMAINS_TABLE WHERE $DOMAIN_NAME = $sqlEscapedDomainName", null)
398     }
399
400     fun addDomain(contentValues: ContentValues) {
401         // Get a writable database handle.
402         val domainsDatabase = this.writableDatabase
403
404         // Add the new domain.
405         domainsDatabase.insert(DOMAINS_TABLE, null, contentValues)
406
407         // Close the database handle.
408         domainsDatabase.close()
409     }
410
411     fun addDomain(domainName: String): Int {
412         // Add the domain with default settings.
413         return addDomain(domainName, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, appContext.getString(R.string.system_default_user_agent), SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT,
414                          SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT)
415     }
416
417     fun addDomain(domainName: String, javaScriptInt: Int, cookiesInt: Int, domStorageInt: Int, userAgentName: String, easyListInt: Int, easyPrivacyInt: Int, fanboysAnnoyanceListInt: Int,
418                   fanboysSocialBlockingListInt: Int, ultraListInt: Int, ultraPrivacyInt: Int, blockAllThirdPartyRequestsInt: Int, fontSizeInt: Int, swipeToRefreshInt: Int, webViewThemeInt: Int,
419                   wideViewportInt: Int, displayImagesInt: Int): Int {
420         // Instantiate a content values.
421         val domainContentValues = ContentValues()
422
423         // Create entries for the database fields.  The ID is created automatically.  The pinned SSL certificate information is not created unless added by the user.
424         domainContentValues.put(DOMAIN_NAME, domainName)
425         domainContentValues.put(ENABLE_JAVASCRIPT, javaScriptInt)
426         domainContentValues.put(COOKIES, cookiesInt)
427         domainContentValues.put(ENABLE_DOM_STORAGE, domStorageInt)
428         domainContentValues.put(USER_AGENT, userAgentName)
429         domainContentValues.put(ENABLE_EASYLIST, easyListInt)
430         domainContentValues.put(ENABLE_EASYPRIVACY, easyPrivacyInt)
431         domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, fanboysAnnoyanceListInt)
432         domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, fanboysSocialBlockingListInt)
433         domainContentValues.put(ULTRALIST, ultraListInt)
434         domainContentValues.put(ENABLE_ULTRAPRIVACY, ultraPrivacyInt)
435         domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequestsInt)
436         domainContentValues.put(FONT_SIZE, fontSizeInt)
437         domainContentValues.put(SWIPE_TO_REFRESH, swipeToRefreshInt)
438         domainContentValues.put(WEBVIEW_THEME, webViewThemeInt)
439         domainContentValues.put(WIDE_VIEWPORT, wideViewportInt)
440         domainContentValues.put(DISPLAY_IMAGES, displayImagesInt)
441
442         // Get a writable database handle.
443         val domainsDatabase = this.writableDatabase
444
445         // Insert a new row and store the resulting database ID.
446         val newDomainDatabaseId = domainsDatabase.insert(DOMAINS_TABLE, null, domainContentValues).toInt()
447
448         // Close the database handle.
449         domainsDatabase.close()
450
451         // Return the new domain database ID.
452         return newDomainDatabaseId
453     }
454
455     fun updateDomain(databaseId: Int, domainName: String, javaScript: Int, cookies: Int, domStorage: Int, userAgent: String, easyList: Int, easyPrivacy: Int, fanboysAnnoyance: Int,
456                      fanboysSocialBlocking: Int, ultraList: Int, ultraPrivacy: Int, blockAllThirdPartyRequests: Int, fontSize: Int, swipeToRefresh: Int, webViewTheme: Int, wideViewport: Int, displayImages: Int,
457                      pinnedSslCertificate: Boolean, pinnedIpAddresses: Boolean) {
458
459         // Instantiate a content values.
460         val domainContentValues = ContentValues()
461
462         // Add entries for each field in the database.
463         domainContentValues.put(DOMAIN_NAME, domainName)
464         domainContentValues.put(ENABLE_JAVASCRIPT, javaScript)
465         domainContentValues.put(COOKIES, cookies)
466         domainContentValues.put(ENABLE_DOM_STORAGE, domStorage)
467         domainContentValues.put(USER_AGENT, userAgent)
468         domainContentValues.put(ENABLE_EASYLIST, easyList)
469         domainContentValues.put(ENABLE_EASYPRIVACY, easyPrivacy)
470         domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, fanboysAnnoyance)
471         domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, fanboysSocialBlocking)
472         domainContentValues.put(ULTRALIST, ultraList)
473         domainContentValues.put(ENABLE_ULTRAPRIVACY, ultraPrivacy)
474         domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests)
475         domainContentValues.put(FONT_SIZE, fontSize)
476         domainContentValues.put(SWIPE_TO_REFRESH, swipeToRefresh)
477         domainContentValues.put(WEBVIEW_THEME, webViewTheme)
478         domainContentValues.put(WIDE_VIEWPORT, wideViewport)
479         domainContentValues.put(DISPLAY_IMAGES, displayImages)
480         domainContentValues.put(PINNED_SSL_CERTIFICATE, pinnedSslCertificate)
481         domainContentValues.put(PINNED_IP_ADDRESSES, pinnedIpAddresses)
482
483         // Get a writable database handle.
484         val domainsDatabase = this.writableDatabase
485
486         // Update the row for the specified database ID.
487         domainsDatabase.update(DOMAINS_TABLE, domainContentValues, "$ID = $databaseId", null)
488
489         // Close the database handle.
490         domainsDatabase.close()
491     }
492
493     fun updatePinnedSslCertificate(databaseId: Int, sslIssuedToCommonName: String, sslIssuedToOrganization: String, sslIssuedToOrganizationalUnit: String, sslIssuedByCommonName: String,
494                                    sslIssuedByOrganization: String, sslIssuedByOrganizationalUnit: String, sslStartDate: Long, sslEndDate: Long) {
495         // Instantiate a content values.
496         val pinnedSslCertificateContentValues = ContentValues()
497
498         // Add entries for each field in the certificate.
499         pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_COMMON_NAME, sslIssuedToCommonName)
500         pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATION, sslIssuedToOrganization)
501         pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATIONAL_UNIT, sslIssuedToOrganizationalUnit)
502         pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_COMMON_NAME, sslIssuedByCommonName)
503         pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATION, sslIssuedByOrganization)
504         pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, sslIssuedByOrganizationalUnit)
505         pinnedSslCertificateContentValues.put(SSL_START_DATE, sslStartDate)
506         pinnedSslCertificateContentValues.put(SSL_END_DATE, sslEndDate)
507
508         // Get a writable database handle.
509         val domainsDatabase = this.writableDatabase
510
511         // Update the row for the specified database ID.
512         domainsDatabase.update(DOMAINS_TABLE, pinnedSslCertificateContentValues, "$ID = $databaseId", null)
513
514         // Close the database handle.
515         domainsDatabase.close()
516     }
517
518     fun updatePinnedIpAddresses(databaseId: Int, ipAddresses: String) {
519         // Instantiate a content values.
520         val pinnedIpAddressesContentValues = ContentValues()
521
522         // Add the IP addresses to the content values.
523         pinnedIpAddressesContentValues.put(IP_ADDRESSES, ipAddresses)
524
525         // Get a writable database handle.
526         val domainsDatabase = this.writableDatabase
527
528         // Update the row for the database ID.
529         domainsDatabase.update(DOMAINS_TABLE, pinnedIpAddressesContentValues, "$ID = $databaseId", null)
530
531         // Close the database handle.
532         domainsDatabase.close()
533     }
534
535     fun deleteDomain(databaseId: Int) : Int {
536         // Get a writable database handle.
537         val domainsDatabase = this.writableDatabase
538
539         // Delete the row for the specified database ID.
540         val rowsDeleted = domainsDatabase.delete(DOMAINS_TABLE, "$ID = $databaseId", null)
541
542         // Close the database handle.
543         domainsDatabase.close()
544
545         // Return the delete status.
546         return rowsDeleted
547     }
548 }