]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/databases/DomainsDatabase.cpp
Add a default folder icon to the edit folder dialog. https://redmine.stoutner.com...
[PrivacyBrowserPC.git] / src / databases / DomainsDatabase.cpp
1 /*
2  * Copyright 2022-2024 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
5  *
6  * Privacy Browser PC 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 PC 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 PC.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 // Application headers.
21 #include "DomainsDatabase.h"
22 #include "Settings.h"
23 #include "helpers/UserAgentHelper.h"
24
25 // Define the private static schema constants.
26 const int DomainsDatabase::SCHEMA_VERSION = 6;
27
28 // Define the public static constants.
29 const QString DomainsDatabase::CONNECTION_NAME = "domains_database";
30 const QString DomainsDatabase::CUSTOM_ZOOM_FACTOR = "custom_zoom_factor";
31 const QString DomainsDatabase::DOM_STORAGE = "dom_storage";
32 const QString DomainsDatabase::DOMAIN_NAME = "domain_name";
33 const QString DomainsDatabase::DOMAINS_TABLE = "domains";
34 const QString DomainsDatabase::ID = "_id";
35 const QString DomainsDatabase::JAVASCRIPT = "javascript";
36 const QString DomainsDatabase::LOCAL_STORAGE = "local_storage";
37 const QString DomainsDatabase::USER_AGENT = "user_agent";
38 const QString DomainsDatabase::ZOOM_FACTOR = "zoom_factor";
39
40 // Construct the class.
41 DomainsDatabase::DomainsDatabase() {}
42
43 void DomainsDatabase::addDatabase()
44 {
45     // Add the domain settings database.
46     QSqlDatabase domainsDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), CONNECTION_NAME);
47
48     // Set the database name.
49     domainsDatabase.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/domains.db");
50
51     // Open the database.
52     if (domainsDatabase.open())  // Opening the database succeeded.
53     {
54         // Check to see if the domains table already exists.
55         if (domainsDatabase.tables().contains(DOMAINS_TABLE))  // The domains table already exists.
56         {
57             // Query the database schema version.
58             QSqlQuery schemaVersionQuery = domainsDatabase.exec(QStringLiteral("PRAGMA user_version"));
59
60             // Move to the first record.
61             schemaVersionQuery.first();
62
63             // Get the current schema version.
64             int currentSchemaVersion = schemaVersionQuery.value(0).toInt();
65
66             // Check to see if the schema has been updated.
67             if (currentSchemaVersion < SCHEMA_VERSION)
68             {
69                 // Run the schema update code.
70                 switch (currentSchemaVersion)
71                 {
72                     // Upgrade from schema version 0 to schema version 1.
73                     case 0:
74                     {
75                         // Add the JavaScript column.
76                         domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + JAVASCRIPT + " INTEGER DEFAULT 0");
77
78                         // Fall through to the next case.
79                         [[fallthrough]];
80                     }
81
82                     // Upgrade from schema version 1 to schema version 2.
83                     case 1:
84                     {
85                         // Add the User Agent column.
86                         domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + USER_AGENT + " TEXT DEFAULT '" + UserAgentHelper::SYSTEM_DEFAULT_DATABASE + "'");
87
88                         // Fall through to the next case.
89                         [[fallthrough]];
90                     }
91
92                     // Upgrade from schema version 2 to schema version 3.
93                     case 2:
94                     {
95                         // Add the Zoom Factor columns.
96                         domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + ZOOM_FACTOR + " INTEGER DEFAULT 0");
97                         domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + CUSTOM_ZOOM_FACTOR + " REAL DEFAULT 1.0");
98
99                         // Fall through to the next case.
100                         [[fallthrough]];
101                     }
102
103                     // Upgrade from schema version 3 to schema version 4.
104                     case 3:
105                     {
106                         // Add the DOM Storage column.
107                         domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + DOM_STORAGE + " INTEGER DEFAULT 0");
108
109                         // Fall through to the next case.
110                         [[fallthrough]];
111                     }
112
113                     // Upgrade from schema version 4 to schema version 5.
114                     case 4:
115                     {
116                         // Add the Local Storage column.
117                         domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + LOCAL_STORAGE + " INTEGER DEFAULT 0");
118
119                         // Fall through to the next case.
120                         [[fallthrough]];
121                     }
122
123                     // Upgrade from schema version 5 to schema version 6.
124                     case 5:
125                     {
126                         // Instantiate a spinner query.
127                         QSqlQuery spinnerQuery(domainsDatabase);
128
129                         // Set the query to be forward only (increases performance while iterating over the query).
130                         spinnerQuery.setForwardOnly(true);
131
132                         // Prepare the query.
133                         spinnerQuery.prepare("SELECT " + ID + "," + JAVASCRIPT + "," + LOCAL_STORAGE + "," + DOM_STORAGE + " FROM " + DOMAINS_TABLE);
134
135                         // Execute the query.
136                         spinnerQuery.exec();
137
138                         // Update the spinner values so that enabled is 1 and disabled is 2.
139                         while (spinnerQuery.next())
140                         {
141                             // Initialize the new spinner values.
142                             int newJavaScriptValue = SYSTEM_DEFAULT;
143                             int newLocalStorageValue = SYSTEM_DEFAULT;
144                             int newDomStorageValue = SYSTEM_DEFAULT;
145
146                             // Update the new JavaScript value if needed.
147                             switch (spinnerQuery.value(JAVASCRIPT).toInt())
148                             {
149                                 // Disabled used to be 1.
150                                 case 1:
151                                 {
152                                     // Update the value to be 2.
153                                     newJavaScriptValue = DISABLED;
154
155                                     break;
156                                 }
157
158                                 // Enabled used to be 2.
159                                 case 2:
160                                 {
161                                     // Update the new value to be 1.
162                                     newJavaScriptValue = ENABLED;
163
164                                     break;
165                                 }
166                             }
167
168                             // Update the new local storage value if needed.
169                             switch (spinnerQuery.value(LOCAL_STORAGE).toInt())
170                             {
171                                 // Disabled used to be 1.
172                                 case 1:
173                                 {
174                                     // Update the new value to be 2.
175                                     newLocalStorageValue = DISABLED;
176
177                                     break;
178                                 }
179
180                                 // Enabled used to be 2.
181                                 case 2:
182                                 {
183                                     // Update the new value to be 1.
184                                     newLocalStorageValue = ENABLED;
185
186                                     break;
187                                 }
188                             }
189
190                             // Update the new DOM storage value if needed.
191                             switch (spinnerQuery.value(DOM_STORAGE).toInt())
192                             {
193                                 // Disabled used to be 1.
194                                 case 1:
195                                 {
196                                     // Update the new value to be 2.
197                                     newDomStorageValue = DISABLED;
198
199                                     break;
200                                 }
201
202                                 // Enabled used to be 2.
203                                 case 2:
204                                 {
205                                     // Update the new value to be 1.
206                                     newDomStorageValue = ENABLED;
207
208                                     break;
209                                 }
210                             }
211
212                             // Create an update spinner query.
213                             QSqlQuery updateSpinnerQuery(domainsDatabase);
214
215                             // Prepare the update spinner query.
216                             updateSpinnerQuery.prepare("UPDATE " + DOMAINS_TABLE + " SET " +
217                                                  JAVASCRIPT + " = :javascript , " +
218                                                  LOCAL_STORAGE + " = :local_storage , " +
219                                                  DOM_STORAGE + " = :dom_storage " +
220                                                  " WHERE " + ID + " = :id");
221
222                             // Bind the values.
223                             updateSpinnerQuery.bindValue(":javascript", newJavaScriptValue);
224                             updateSpinnerQuery.bindValue(":local_storage", newLocalStorageValue);
225                             updateSpinnerQuery.bindValue(":dom_storage", newDomStorageValue);
226                             updateSpinnerQuery.bindValue(":id", spinnerQuery.value(ID));
227
228                             // Execute the query.
229                             updateSpinnerQuery.exec();
230                         }
231
232                         // Fall through to the next case.
233                         // [[fallthrough]];
234                     }
235                 }
236
237                 // Update the schema version.
238                 domainsDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
239             }
240         }
241         else  // The domains table does not exist.
242         {
243             // Instantiate a create table query.
244             QSqlQuery createTableQuery(domainsDatabase);
245
246             // Prepare the create table query.
247             createTableQuery.prepare("CREATE TABLE " + DOMAINS_TABLE + "(" +
248                                      ID + " INTEGER PRIMARY KEY, " +
249                                      DOMAIN_NAME + " TEXT, " +
250                                      JAVASCRIPT + " INTEGER DEFAULT 0, " +
251                                      LOCAL_STORAGE + " INTEGER DEFAULT 0, " +
252                                      DOM_STORAGE + " INTEGER DEFAULT 0, " +
253                                      USER_AGENT + " TEXT DEFAULT '" + UserAgentHelper::SYSTEM_DEFAULT_DATABASE + "', " +
254                                      ZOOM_FACTOR + " INTEGER DEFAULT 0, " +
255                                      CUSTOM_ZOOM_FACTOR + " REAL DEFAULT 1.0)");
256
257             // Execute the query.
258             if (!createTableQuery.exec())
259             {
260                 // Log any errors.
261                 qDebug().noquote().nospace() << "Error creating table:  " << domainsDatabase.lastError();
262             }
263
264             // Set the schema version.
265             domainsDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
266         }
267     }
268     else  // Opening the database failed.
269     {
270         // Write the last database error message to the debug output.Settings::zoom
271         qDebug().noquote().nospace() << "Error opening database:  " << domainsDatabase.lastError();
272     }
273 };
274
275 void DomainsDatabase::addDomain(const QString &domainName)
276 {
277     // Add the domain:
278     addDomain(domainName, SYSTEM_DEFAULT, SYSTEM_DEFAULT, SYSTEM_DEFAULT, UserAgentHelper::SYSTEM_DEFAULT_DATABASE, SYSTEM_DEFAULT, Settings::zoomFactor());
279 }
280
281 void DomainsDatabase::addDomain(const QString &domainName, const int javaScriptInt, const int localStorageInt, const int domStorageInt, const QString &userAgentDatabaseString,
282                                 const int zoomFactorInt, const double currentZoomFactorDouble)
283 {
284     // Get a handle for the domains database.
285     QSqlDatabase domainsDatabase = QSqlDatabase::database(CONNECTION_NAME);
286
287     // Instantiate an add domain settings query.
288     QSqlQuery addDomainSettingsQuery(domainsDatabase);
289
290     // Prepare the query.
291     addDomainSettingsQuery.prepare("INSERT INTO " + DomainsDatabase::DOMAINS_TABLE + " (" +
292                                     DomainsDatabase::DOMAIN_NAME + ", " +
293                                     DomainsDatabase::JAVASCRIPT + ", " +
294                                     DomainsDatabase::LOCAL_STORAGE + ", " +
295                                     DomainsDatabase::DOM_STORAGE + ", " +
296                                     DomainsDatabase::USER_AGENT + ", " +
297                                     DomainsDatabase::ZOOM_FACTOR + ", " +
298                                     DomainsDatabase::CUSTOM_ZOOM_FACTOR + ") " +
299                                     "VALUES (:domain_name, :javascript, :local_storage, :dom_storage, :user_agent, :zoom_factor, :custom_zoom_factor)"
300     );
301
302     // Bind the query values.
303     addDomainSettingsQuery.bindValue(":domain_name", domainName);
304     addDomainSettingsQuery.bindValue(":javascript", javaScriptInt);
305     addDomainSettingsQuery.bindValue(":local_storage", localStorageInt);
306     addDomainSettingsQuery.bindValue(":dom_storage", domStorageInt);
307     addDomainSettingsQuery.bindValue(":user_agent", userAgentDatabaseString);
308     addDomainSettingsQuery.bindValue(":zoom_factor", zoomFactorInt);
309     addDomainSettingsQuery.bindValue(":custom_zoom_factor", currentZoomFactorDouble);
310
311     // Execute the query.
312     addDomainSettingsQuery.exec();
313 }
314
315 QSqlQuery DomainsDatabase::getDomainQuery(const QString &hostname)
316 {
317     // Get a handle for the domains database.
318     QSqlDatabase domainsDatabase = QSqlDatabase::database(CONNECTION_NAME);
319
320     // Instantiate the all domain names query.
321     QSqlQuery allDomainNamesQuery(domainsDatabase);
322
323     // Set the query to be forward only (increases performance while iterating over the query).
324     allDomainNamesQuery.setForwardOnly(true);
325
326     // Prepare the query.
327     allDomainNamesQuery.prepare("SELECT " + ID + "," + DOMAIN_NAME + " FROM " + DOMAINS_TABLE);
328
329     // Execute the query.
330     allDomainNamesQuery.exec();
331
332     // Create a domains settings map.
333     QMap<QString, int> domainSettingsMap;
334
335     // Populate the domain settings map.
336     while (allDomainNamesQuery.next())
337     {
338         // Add the domain name and database ID to the map.
339         domainSettingsMap.insert(allDomainNamesQuery.value(DOMAIN_NAME).toString(), allDomainNamesQuery.value(ID).toInt());
340     }
341
342     // Initialize the database ID tracker.
343     int databaseId = -1;
344
345     // Get the database ID if the hostname is found in the domain settings set.
346     if (domainSettingsMap.contains(hostname))
347     {
348         databaseId = domainSettingsMap.value(hostname);
349     }
350
351     // Create a subdomain string.
352     QString subdomain = hostname;
353
354     // Check all the subdomains of the hostname.
355     while ((databaseId == -1) && subdomain.contains("."))  // Stop checking when a match is found or there are no more `.` in the hostname.
356     {
357         // Check to see if the domain settings map contains the subdomain with a `*.` prepended.
358         if (domainSettingsMap.contains("*." + subdomain))
359         {
360             // Get the database ID.
361             databaseId = domainSettingsMap.value("*." + subdomain);
362         }
363
364         // Strip out the first subdomain.
365         subdomain = subdomain.section('.', 1);
366     }
367
368     // Instantiate the domain lookup query.
369     QSqlQuery domainLookupQuery(domainsDatabase);
370
371     // Prepare the domain lookup query.
372     domainLookupQuery.prepare("SELECT * FROM " + DOMAINS_TABLE + " WHERE " + ID + " = " + QString::number(databaseId));
373
374     // Execute the query.
375     domainLookupQuery.exec();
376
377     // Move to the first entry.
378     domainLookupQuery.first();
379
380     // Return the query.
381     return domainLookupQuery;
382 }