]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/databases/CookiesDatabase.cpp
a99290985cd7c5f28e971c3e50250f2567aa5aec
[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 int CookiesDatabase::cookieCount()
150 {
151     // Get a handle for the cookies database.
152     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
153
154     // Instantiate a count cookies query.
155     QSqlQuery countCookiesQuery(cookiesDatabase);
156
157     // Set the query to be forward only.
158     countCookiesQuery.setForwardOnly(true);
159
160     // Prepare the query.
161     countCookiesQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE);
162
163     // Execute the query.
164     countCookiesQuery.exec();
165
166     // Move to the last row.
167     countCookiesQuery.last();
168
169     // Get the number of rows (which is zero based).
170     int numberOfCookies = countCookiesQuery.at() + 1;
171
172     // Return the number of cookies.
173     return numberOfCookies;
174 }
175
176 void CookiesDatabase::deleteAllCookies()
177 {
178     // Get a handle for the cookies database.
179     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
180
181     // Instantiate a delete all cookies query.
182     QSqlQuery deleteAllCookiesQuery(cookiesDatabase);
183
184     // Prepare the delete all cookies query.
185     deleteAllCookiesQuery.prepare("DELETE FROM " + COOKIES_TABLE);
186
187     // Execute the query.
188     deleteAllCookiesQuery.exec();
189 }
190
191 void CookiesDatabase::deleteCookie(const QNetworkCookie &cookie)
192 {
193     // Get a handle for the cookies database.
194     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
195
196     // Instantiate a delete cookie query.
197     QSqlQuery deleteCookieQuery(cookiesDatabase);
198
199     // Prepare the delete cookie query.
200     deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
201
202     // Bind the values.
203     deleteCookieQuery.bindValue(":domain", cookie.domain());
204     deleteCookieQuery.bindValue(":name", QString(cookie.name()));
205     deleteCookieQuery.bindValue(":path", cookie.path());
206
207     // Execute the query.
208     deleteCookieQuery.exec();
209 }
210
211 QList<QNetworkCookie*>* CookiesDatabase::getCookies()
212 {
213     // Get a handle for the cookies database.
214     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
215
216     // Instantiate a cookies query.
217     QSqlQuery cookiesQuery(cookiesDatabase);
218
219     // Set the query to be forward only.
220     cookiesQuery.setForwardOnly(true);
221
222     // Prepare the cookies query.
223     cookiesQuery.prepare("SELECT * FROM " + COOKIES_TABLE);
224
225     // Execute the query.
226     cookiesQuery.exec();
227
228     // Create a cookie list.
229     QList<QNetworkCookie*> *cookieListPointer = new QList<QNetworkCookie*>;
230
231     // Populate the cookie list.
232     while (cookiesQuery.next())
233     {
234         // Create a cookie.
235         QNetworkCookie *cookiePointer = new QNetworkCookie();
236
237         // Populate the cookie.
238         cookiePointer->setDomain(cookiesQuery.value(DOMAIN).toString());
239         cookiePointer->setName(cookiesQuery.value(NAME).toString().toUtf8());
240         cookiePointer->setPath(cookiesQuery.value(PATH).toString());
241         cookiePointer->setExpirationDate(QDateTime::fromString(cookiesQuery.value(EXPIRATION_DATE).toString(), Qt::ISODate));
242         cookiePointer->setHttpOnly(cookiesQuery.value(HTTP_ONLY).toBool());
243         cookiePointer->setSecure(cookiesQuery.value(SECURE).toBool());
244         cookiePointer->setValue(cookiesQuery.value(VALUE).toString().toUtf8());
245
246         // Add the cookie to the list.
247         cookieListPointer->append(cookiePointer);
248     }
249
250     // Return the cookie list.
251     return cookieListPointer;
252 }
253
254 QNetworkCookie* CookiesDatabase::getCookieById(const int &id)
255 {
256     // Get a handle for the cookies database.
257     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
258
259     // Instantiate a cookie query.
260     QSqlQuery cookieQuery(cookiesDatabase);
261
262     // Set the query to be forward only.
263     cookieQuery.setForwardOnly(true);
264
265     // Prepare the cookies query.
266     cookieQuery.prepare("SELECT * FROM " + COOKIES_TABLE + " WHERE " + _ID + " = :id");
267
268     // Bind the values.
269     cookieQuery.bindValue(":id", id);
270
271     // Execute the query.
272     cookieQuery.exec();
273
274     // Move to the first entry.
275     cookieQuery.first();
276
277     // Create a cookie.
278     QNetworkCookie *cookiePointer = new QNetworkCookie();
279
280     // Populate the cookie.
281     cookiePointer->setDomain(cookieQuery.value(DOMAIN).toString());
282     cookiePointer->setName(cookieQuery.value(NAME).toString().toUtf8());
283     cookiePointer->setPath(cookieQuery.value(PATH).toString());
284     cookiePointer->setExpirationDate(QDateTime::fromString(cookieQuery.value(EXPIRATION_DATE).toString(), Qt::ISODate));
285     cookiePointer->setHttpOnly(cookieQuery.value(HTTP_ONLY).toBool());
286     cookiePointer->setSecure(cookieQuery.value(SECURE).toBool());
287     cookiePointer->setValue(cookieQuery.value(VALUE).toString().toUtf8());
288
289     // Return the cookie.
290     return cookiePointer;
291 }
292
293 bool CookiesDatabase::isDurable(const QNetworkCookie &cookie)
294 {
295     // Get a handle for the cookies database.
296     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
297
298     // Instantiate an is durable query.
299     QSqlQuery isDurableQuery(cookiesDatabase);
300
301     // Set the query to be forward only.
302     isDurableQuery.setForwardOnly(true);
303
304     // Prepare the is durable query.
305     isDurableQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
306
307     // Bind the values.
308     isDurableQuery.bindValue(":domain", cookie.domain());
309     isDurableQuery.bindValue(":name", QString(cookie.name()));
310     isDurableQuery.bindValue(":path", cookie.path());
311
312     // Execute the query.
313     isDurableQuery.exec();
314
315     // Move to the first entry.
316     isDurableQuery.first();
317
318     // Return the status of the cookie in the database.
319     return (isDurableQuery.isValid());
320 }
321
322 bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie)
323 {
324     // Get a handle for the cookies database.
325     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
326
327     // Instantiate an is update query.
328     QSqlQuery isUpdateQuery(cookiesDatabase);
329
330     // Prepare the is update query.
331     isUpdateQuery.prepare("SELECT " + EXPIRATION_DATE + " , " + HTTP_ONLY + " , " + SECURE + " , " + VALUE + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " +
332                           NAME + " = :name AND " + PATH + " = :path");
333
334     // Bind the values.
335     isUpdateQuery.bindValue(":domain", cookie.domain());
336     isUpdateQuery.bindValue(":name", QString(cookie.name()));
337     isUpdateQuery.bindValue(":path", cookie.path());
338
339     // Execute the query.
340     isUpdateQuery.exec();
341
342     // Move to the first entry.
343     isUpdateQuery.first();
344
345     // Check to see if the cookie exists.
346     if (isUpdateQuery.isValid())  // The cookie exists in the database.
347     {
348         // Check to see if the cookie data has changed.
349         if ((QDateTime::fromString(isUpdateQuery.value(0).toString(), Qt::ISODate) != cookie.expirationDate()) ||
350             (isUpdateQuery.value(1).toBool() != cookie.isHttpOnly()) ||
351             (isUpdateQuery.value(2).toBool() != cookie.isSecure()) ||
352             (isUpdateQuery.value(3).toString().toUtf8() != cookie.value()))  // The cookies data has changed.
353         {
354             qDebug() << "The durable cookie data has changed.";
355
356             // Return true.
357             return true;
358         }
359         else  // The cookie data has not changed.
360         {
361             qDebug() << "The durable cookie data is unchanged.";
362
363             // Return false.
364             return false;
365         }
366     }
367     else  // The cookie does not exist in the database.
368     {
369         // Return false.
370         return false;
371     }
372 }
373
374 void CookiesDatabase::updateCookie(const QNetworkCookie &cookie)
375 {
376     // Get a handle for the cookies database.
377     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
378
379     // Create the update cookie query.
380     QSqlQuery updateCookieQuery(cookiesDatabase);
381
382     // Prepare the edit cookie query.
383     updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + EXPIRATION_DATE + " = :expiration_date , " + HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " +
384                               VALUE + " = :value WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
385
386     // Bind the values.
387     updateCookieQuery.bindValue(":domain", cookie.domain());
388     updateCookieQuery.bindValue(":name", QString(cookie.name()));
389     updateCookieQuery.bindValue(":path", cookie.path());
390     updateCookieQuery.bindValue(":expiration_date", cookie.expirationDate());
391     updateCookieQuery.bindValue(":http_only", cookie.isHttpOnly());
392     updateCookieQuery.bindValue(":secure", cookie.isSecure());
393     updateCookieQuery.bindValue(":value", QString(cookie.value()));
394
395     // Execute the query.
396     updateCookieQuery.exec();
397 }
398
399 void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetworkCookie &newCookie)
400 {
401     // Get a handle for the cookies database.
402     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
403
404     // Create the old cookie query.
405     QSqlQuery oldCookieQuery(cookiesDatabase);
406
407     // Set the query to be forward only.
408     oldCookieQuery.setForwardOnly(true);
409
410     // Prepare the old cookie query.
411     oldCookieQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
412
413     // Bind the values.
414     oldCookieQuery.bindValue(":domain", oldCookie.domain());
415     oldCookieQuery.bindValue(":name", QString(oldCookie.name()));
416     oldCookieQuery.bindValue(":path", oldCookie.path());
417
418     // Execute the query.
419     oldCookieQuery.exec();
420
421     // Move to the first entry.
422     oldCookieQuery.first();
423
424     // Create the update cookie query.
425     QSqlQuery updateCookieQuery(cookiesDatabase);
426
427     // Prepare the update cookie query.
428     updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + DOMAIN + " = :domain , " + NAME + " = :name , " + PATH + " = :path , " + EXPIRATION_DATE + " = :expiration_date , " +
429                               HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " + VALUE + " = :value WHERE " + _ID + " = :id");
430
431     // Bind the values.
432     updateCookieQuery.bindValue(":id", oldCookieQuery.value(0).toLongLong());
433     updateCookieQuery.bindValue(":domain", newCookie.domain());
434     updateCookieQuery.bindValue(":name", QString(newCookie.name()));
435     updateCookieQuery.bindValue(":path", newCookie.path());
436     updateCookieQuery.bindValue(":expiration_date", newCookie.expirationDate());
437     updateCookieQuery.bindValue(":http_only", newCookie.isHttpOnly());
438     updateCookieQuery.bindValue(":secure", newCookie.isSecure());
439     updateCookieQuery.bindValue(":value", QString(newCookie.value()));
440
441     // Execute the query.
442     updateCookieQuery.exec();
443 }