]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/databases/CookiesDatabase.cpp
adc4f1ba2c108362e59bbcda99276a54c0e4f219
[PrivacyBrowserPC.git] / src / databases / CookiesDatabase.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 "CookiesDatabase.h"
22
23 // Define the private static schema constants.
24 const int CookiesDatabase::SCHEMA_VERSION = 0;
25
26 // Define the public static database constants.
27 const QString CookiesDatabase::CONNECTION_NAME = "cookies_database";
28 const QString CookiesDatabase::COOKIES_TABLE = "cookies";
29
30 // Define the public static database field names.
31 const QString CookiesDatabase::_ID = "_id";
32 const QString CookiesDatabase::DOMAIN = "domain";
33 const QString CookiesDatabase::NAME = "name";
34 const QString CookiesDatabase::PATH = "path";
35 const QString CookiesDatabase::EXPIRATION_DATE = "expiration_date";
36 const QString CookiesDatabase::HTTP_ONLY = "http_only";
37 const QString CookiesDatabase::SECURE = "secure";
38 const QString CookiesDatabase::VALUE = "value";
39
40 // Construct the class.
41 CookiesDatabase::CookiesDatabase() {}
42
43 void CookiesDatabase::addDatabase()
44 {
45     // Add the cookies database.
46     QSqlDatabase cookiesDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), CONNECTION_NAME);
47
48     // Set the database name.
49     cookiesDatabase.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/cookies.db");
50
51     // Open the database.
52     if (cookiesDatabase.open())  // Opening the database succeeded.
53     {
54         // Check to see if the cookies table already exists.
55         if (cookiesDatabase.tables().contains(COOKIES_TABLE))  // The cookies table exists.
56         {
57             // Query the database schema version.
58             QSqlQuery schemaVersionQuery = cookiesDatabase.exec(QStringLiteral("PRAGMA user_version"));
59
60             // Move to the first record.
61             schemaVersionQuery.first();
62
63             // Get the current schema versin.
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 code here.
73                 }
74
75                 // Update the schema version.
76                 cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
77             }
78         }
79         else  // The cookies table does not exist.
80         {
81             // Instantiate a create table query.
82             QSqlQuery createTableQuery(cookiesDatabase);
83
84             // Prepare the create table query.
85             createTableQuery.prepare("CREATE TABLE " + COOKIES_TABLE + "(" +
86                 _ID + " INTEGER PRIMARY KEY, " +
87                 DOMAIN + " TEXT NOT NULL, " +
88                 NAME + " TEXT NOT NULL, " +
89                 PATH + " TEXT NOT NULL, " +
90                 EXPIRATION_DATE + " TEXT, " +
91                 HTTP_ONLY + " INTEGER NOT NULL DEFAULT 0, " +
92                 SECURE + " INTEGER NOT NULL DEFAULT 0, " +
93                 VALUE + " TEXT NOT NULL)"
94             );
95
96             // Execute the query.
97             if (!createTableQuery.exec())
98             {
99                 // Log any errors.
100                 qDebug().noquote().nospace() << "Error creating table:  " << cookiesDatabase.lastError();
101             }
102
103             // Set the schema version.
104             cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
105         }
106     }
107     else  // Opening the database failed.
108     {
109         // Write the last database error message to the debug output.
110         qDebug().noquote().nospace() << "Error opening database:  " << cookiesDatabase.lastError();
111     }
112 }
113
114 void CookiesDatabase::addCookie(const QNetworkCookie &cookie)
115 {
116     // Get a handle for the cookies database.
117     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
118
119     // Check to see if the cookie already exists in the database.
120     if (isDurable(cookie))  // The cookie already exists.
121     {
122         // Update the existing cookie.
123         updateCookie(cookie);
124     }
125     else  // The cookie doesn't already exist.
126     {
127         // Instantiate an add cookie query.
128         QSqlQuery addCookieQuery(cookiesDatabase);
129
130         // Prepare the add cookie query.
131         addCookieQuery.prepare("INSERT INTO " + COOKIES_TABLE + " (" + DOMAIN + ", " + NAME + ", " + PATH + ", " + EXPIRATION_DATE + ", " + HTTP_ONLY + ", " + SECURE + ", " + VALUE + ") "
132                             "VALUES (:domain, :name, :path, :expiration_date, :http_only, :secure, :value)"
133         );
134
135         // Bind the values.
136         addCookieQuery.bindValue(":domain", cookie.domain());
137         addCookieQuery.bindValue(":name", QString(cookie.name()));
138         addCookieQuery.bindValue(":path", cookie.path());
139         addCookieQuery.bindValue(":expiration_date", cookie.expirationDate());
140         addCookieQuery.bindValue(":http_only", cookie.isHttpOnly());
141         addCookieQuery.bindValue(":secure", cookie.isSecure());
142         addCookieQuery.bindValue(":value", QString(cookie.value()));
143
144         // Execute the query.
145         addCookieQuery.exec();
146     }
147 }
148
149 void CookiesDatabase::deleteAllCookies()
150 {
151     // Get a handle for the cookies database.
152     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
153
154     // Instantiate a delete all cookies query.
155     QSqlQuery deleteAllCookiesQuery(cookiesDatabase);
156
157     // Prepare the delete all cookies query.
158     deleteAllCookiesQuery.prepare("DELETE FROM " + COOKIES_TABLE);
159
160     // Execute the query.
161     deleteAllCookiesQuery.exec();
162 }
163
164 void CookiesDatabase::deleteCookie(const QNetworkCookie &cookie)
165 {
166     // Get a handle for the cookies database.
167     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
168
169     // Instantiate a delete cookie query.
170     QSqlQuery deleteCookieQuery(cookiesDatabase);
171
172     // Prepare the delete cookie query.
173     deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
174
175     // Bind the values.
176     deleteCookieQuery.bindValue(":domain", cookie.domain());
177     deleteCookieQuery.bindValue(":name", QString(cookie.name()));
178     deleteCookieQuery.bindValue(":path", cookie.path());
179
180     // Execute the query.
181     deleteCookieQuery.exec();
182 }
183
184 QList<QNetworkCookie*>* CookiesDatabase::getCookies()
185 {
186     // Get a handle for the cookies database.
187     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
188
189     // Instantiate a cookies query.
190     QSqlQuery cookiesQuery(cookiesDatabase);
191
192     // Set the query to be forward only.
193     cookiesQuery.setForwardOnly(true);
194
195     // Prepare the cookies query.
196     cookiesQuery.prepare("SELECT * FROM " + COOKIES_TABLE);
197
198     // Execute the query.
199     cookiesQuery.exec();
200
201     // Create a cookie list.
202     QList<QNetworkCookie*> *cookieListPointer = new QList<QNetworkCookie*>;
203
204     // Populate the cookie list.
205     while (cookiesQuery.next())
206     {
207         // Create a cookie.
208         QNetworkCookie *cookiePointer = new QNetworkCookie();
209
210         // Populate the cookie.
211         cookiePointer->setDomain(cookiesQuery.value(DOMAIN).toString());
212         cookiePointer->setName(cookiesQuery.value(NAME).toString().toUtf8());
213         cookiePointer->setPath(cookiesQuery.value(PATH).toString());
214         cookiePointer->setExpirationDate(QDateTime::fromString(cookiesQuery.value(EXPIRATION_DATE).toString(), Qt::ISODate));
215         cookiePointer->setHttpOnly(cookiesQuery.value(HTTP_ONLY).toBool());
216         cookiePointer->setSecure(cookiesQuery.value(SECURE).toBool());
217         cookiePointer->setValue(cookiesQuery.value(VALUE).toString().toUtf8());
218
219         // Add the cookie to the list.
220         cookieListPointer->append(cookiePointer);
221     }
222
223     // Return the cookie list.
224     return cookieListPointer;
225 }
226
227 bool CookiesDatabase::isDurable(const QNetworkCookie &cookie)
228 {
229     // Get a handle for the cookies database.
230     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
231
232     // Instantiate an is durable query.
233     QSqlQuery isDurableQuery(cookiesDatabase);
234
235     // Set the query to be forward only.
236     isDurableQuery.setForwardOnly(true);
237
238     // Prepare the is durable query.
239     isDurableQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
240
241     // Bind the values.
242     isDurableQuery.bindValue(":domain", cookie.domain());
243     isDurableQuery.bindValue(":name", QString(cookie.name()));
244     isDurableQuery.bindValue(":path", cookie.path());
245
246     // Execute the query.
247     isDurableQuery.exec();
248
249     // Move to the first entry.
250     isDurableQuery.first();
251
252     // Return the status of the cookie in the database.
253     return (isDurableQuery.isValid());
254 }
255
256 bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie)
257 {
258     // Get a handle for the cookies database.
259     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
260
261     // Instantiate an is update query.
262     QSqlQuery isUpdateQuery(cookiesDatabase);
263
264     // Prepare the is update query.
265     isUpdateQuery.prepare("SELECT " + EXPIRATION_DATE + " , " + HTTP_ONLY + " , " + SECURE + " , " + VALUE + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " +
266                           NAME + " = :name AND " + PATH + " = :path");
267
268     // Bind the values.
269     isUpdateQuery.bindValue(":domain", cookie.domain());
270     isUpdateQuery.bindValue(":name", QString(cookie.name()));
271     isUpdateQuery.bindValue(":path", cookie.path());
272
273     // Execute the query.
274     isUpdateQuery.exec();
275
276     // Move to the first entry.
277     isUpdateQuery.first();
278
279     // Check to see if the cookie exists.
280     if (isUpdateQuery.isValid())  // The cookie exists in the database.
281     {
282         // Check to see if the cookie data has changed.
283         if ((QDateTime::fromString(isUpdateQuery.value(0).toString(), Qt::ISODate) != cookie.expirationDate()) ||
284             (isUpdateQuery.value(1).toBool() != cookie.isHttpOnly()) ||
285             (isUpdateQuery.value(2).toBool() != cookie.isSecure()) ||
286             (isUpdateQuery.value(3).toString().toUtf8() != cookie.value()))  // The cookies data has changed.
287         {
288             qDebug() << "The durable cookie data has changed.";
289
290             // Return true.
291             return true;
292         }
293         else  // The cookie data has not changed.
294         {
295             qDebug() << "The durable cookie data is unchanged.";
296
297             // Return false.
298             return false;
299         }
300     }
301     else  // The cookie does not exist in the database.
302     {
303         // Return false.
304         return false;
305     }
306 }
307
308 void CookiesDatabase::updateCookie(const QNetworkCookie &cookie)
309 {
310     // Get a handle for the cookies database.
311     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
312
313     // Create the update cookie query.
314     QSqlQuery updateCookieQuery(cookiesDatabase);
315
316     // Prepare the edit cookie query.
317     updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + EXPIRATION_DATE + " = :expiration_date , " + HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " +
318                               VALUE + " = :value WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
319
320     // Bind the values.
321     updateCookieQuery.bindValue(":domain", cookie.domain());
322     updateCookieQuery.bindValue(":name", QString(cookie.name()));
323     updateCookieQuery.bindValue(":path", cookie.path());
324     updateCookieQuery.bindValue(":expiration_date", cookie.expirationDate());
325     updateCookieQuery.bindValue(":http_only", cookie.isHttpOnly());
326     updateCookieQuery.bindValue(":secure", cookie.isSecure());
327     updateCookieQuery.bindValue(":value", QString(cookie.value()));
328
329     // Execute the query.
330     updateCookieQuery.exec();
331 }
332
333 void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetworkCookie &newCookie)
334 {
335     // Get a handle for the cookies database.
336     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
337
338     // Create the old cookie query.
339     QSqlQuery oldCookieQuery(cookiesDatabase);
340
341     // Set the query to be forward only.
342     oldCookieQuery.setForwardOnly(true);
343
344     // Prepare the old cookie query.
345     oldCookieQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
346
347     // Bind the values.
348     oldCookieQuery.bindValue(":domain", oldCookie.domain());
349     oldCookieQuery.bindValue(":name", QString(oldCookie.name()));
350     oldCookieQuery.bindValue(":path", oldCookie.path());
351
352     // Execute the query.
353     oldCookieQuery.exec();
354
355     // Move to the first entry.
356     oldCookieQuery.first();
357
358     // Create the update cookie query.
359     QSqlQuery updateCookieQuery(cookiesDatabase);
360
361     // Prepare the update cookie query.
362     updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + DOMAIN + " = :domain , " + NAME + " = :name , " + PATH + " = :path , " + EXPIRATION_DATE + " = :expiration_date , " +
363                               HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " + VALUE + " = :value WHERE " + _ID + " = :id");
364
365     // Bind the values.
366     updateCookieQuery.bindValue(":id", oldCookieQuery.value(0).toLongLong());
367     updateCookieQuery.bindValue(":domain", newCookie.domain());
368     updateCookieQuery.bindValue(":name", QString(newCookie.name()));
369     updateCookieQuery.bindValue(":path", newCookie.path());
370     updateCookieQuery.bindValue(":expiration_date", newCookie.expirationDate());
371     updateCookieQuery.bindValue(":http_only", newCookie.isHttpOnly());
372     updateCookieQuery.bindValue(":secure", newCookie.isSecure());
373     updateCookieQuery.bindValue(":value", QString(newCookie.value()));
374
375     // Execute the query.
376     updateCookieQuery.exec();
377 }