]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/helpers/DomainsDatabaseHelper.cpp
Enable Domain Settings with wildcard subdomains.
[PrivacyBrowserPC.git] / src / helpers / DomainsDatabaseHelper.cpp
1 /*
2  * Copyright © 2022 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 "DomainsDatabaseHelper.h"
22
23 // Define the public static domain constants.
24 const QString DomainsDatabaseHelper::CONNECTION_NAME = "domains_database";
25 const QString DomainsDatabaseHelper::DOMAINS_TABLE = "domains";
26
27 // Define the private static schema constants.
28 const int DomainsDatabaseHelper::SCHEMA_VERSION = 1;
29
30 // Define the public static database field names.
31 const QString DomainsDatabaseHelper::_ID = "_id";
32 const QString DomainsDatabaseHelper::DOMAIN_NAME = "domain_name";
33 const QString DomainsDatabaseHelper::JAVASCRIPT = "javascript";
34
35 // The default constructor.
36 DomainsDatabaseHelper::DomainsDatabaseHelper() {}
37
38 void DomainsDatabaseHelper::addDatabase()
39 {
40     // Add the domain settings database.
41     QSqlDatabase domainsDatabase = QSqlDatabase::addDatabase("QSQLITE", CONNECTION_NAME);
42
43     // Set the database name.
44     domainsDatabase.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/domains.db");
45
46     // Open the database.
47     if (domainsDatabase.open())  // Opening the database succeeded.
48     {
49         // Check to see if the domains table already exists.
50         if (domainsDatabase.tables().contains(DOMAINS_TABLE))
51         {
52             // Query the database schema version.
53             QSqlQuery getSchemaVersionQuery = domainsDatabase.exec("PRAGMA user_version");
54
55             // Move to the first record.
56             getSchemaVersionQuery.first();
57
58             // Get the current schema version.
59             int currentSchemaVersion = getSchemaVersionQuery.value(0).toInt();
60
61             // Check to see if the schama has been updated.
62             if (SCHEMA_VERSION > currentSchemaVersion)
63             {
64                 // Run schema update code.
65                 switch (currentSchemaVersion)
66                 {
67                     // Upgrade from schema version 0.
68                     case 0:
69                     {
70                         // Add the JavaScript column.
71                         domainsDatabase.exec("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + JAVASCRIPT + " INTEGER DEFAULT 0");
72
73                         // Set the default value.
74                         domainsDatabase.exec("UPDATE " + DOMAINS_TABLE + " SET " + JAVASCRIPT + " = 0" );
75                     }
76                 }
77
78                 // Update the schema version.
79                 domainsDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
80             }
81         }
82         else
83         {
84             // Instantiate a create table query.
85             QSqlQuery createTableQuery(domainsDatabase);
86
87             // Prepare the create table query.
88             createTableQuery.prepare("CREATE TABLE " + DOMAINS_TABLE + "(" +
89                 _ID + " INTEGER PRIMARY KEY, " +
90                 DOMAIN_NAME + " TEXT, " +
91                 JAVASCRIPT + " INTEGER DEFAULT 0)"
92             );
93
94             // Execute the query.
95             if (!createTableQuery.exec())
96             {
97                 // Log any errors.
98                 qDebug().noquote().nospace() << "Error creating table:  " << domainsDatabase.lastError();
99             }
100
101             // Set the schema version.
102             domainsDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
103         }
104     }
105     else  // Opening the database failed.
106     {
107         // Write the last database error message to the debug output.
108         qDebug().noquote().nospace() << "Error opening database:  " << domainsDatabase.lastError();
109     }
110 };
111
112 QSqlQuery DomainsDatabaseHelper::getDomainQuery(const QString &hostname)
113 {
114     // Get a handle for the domains database.
115     QSqlDatabase domainsDatabase = QSqlDatabase::database(CONNECTION_NAME);
116
117     // Instantiate the all domain names query.
118     QSqlQuery allDomainNamesQuery(domainsDatabase);
119
120     // Set the query to be forward only (increases performance while iterating over the query).
121     allDomainNamesQuery.setForwardOnly(true);
122
123     // Prepare the query.
124     allDomainNamesQuery.prepare("SELECT " + _ID + "," + DOMAIN_NAME + " FROM " + DOMAINS_TABLE);
125
126     // Execute the query.
127     allDomainNamesQuery.exec();
128
129     // Create a domains settings map.
130     QMap<QString, int> domainSettingsMap;
131
132     // Populate the domain settings map.
133     while (allDomainNamesQuery.next())
134     {
135         // Add the domain name and database ID to the map.
136         domainSettingsMap.insert(allDomainNamesQuery.record().field(DomainsDatabaseHelper::DOMAIN_NAME).value().toString(),
137                                  allDomainNamesQuery.record().field(DomainsDatabaseHelper::_ID).value().toInt());
138     }
139
140     // Initialize the database ID tracker.
141     int databaseId = -1;
142
143     // Get the database ID if the hostname is found in the domain settings set.
144     if (domainSettingsMap.contains(hostname))
145     {
146         databaseId = domainSettingsMap.value(hostname);
147     }
148
149     // Create a subdomain string.
150     QString subdomain = hostname;
151
152     // Check all the subdomains of the hostname.
153     while ((databaseId == -1) && subdomain.contains("."))  // Stop checking when a match is found or there are no more `.` in the hostname.
154     {
155         // Check to see if the domain settings map contains the subdomain with a `*.` prepended.
156         if (domainSettingsMap.contains("*." + subdomain))
157         {
158             // Get the database ID.
159             databaseId = domainSettingsMap.value("*." + subdomain);
160         }
161
162         // Strip out the first subdomain.
163         subdomain = subdomain.section('.', 1);
164     }
165
166     // Instantiate the domain lookup query.
167     QSqlQuery domainLookupQuery(domainsDatabase);
168
169     // Prepare the domain lookup query.
170     domainLookupQuery.prepare("SELECT * FROM " + DOMAINS_TABLE + " WHERE " + _ID + " = " + QString::number(databaseId));
171
172     // Execute the query.
173     domainLookupQuery.exec();
174
175     // Move to the first entry.
176     domainLookupQuery.first();
177
178     // Return the query.
179     return domainLookupQuery;
180 }