]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/databases/CookiesDatabase.cpp
Add a default folder icon to the edit folder dialog. https://redmine.stoutner.com...
[PrivacyBrowserPC.git] / src / databases / CookiesDatabase.cpp
1 /*
2  * Copyright 2022-2023 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 constants.
27 const QString CookiesDatabase::CONNECTION_NAME = "cookies_database";
28 const QString CookiesDatabase::COOKIES_TABLE = "cookies";
29 const QString CookiesDatabase::DOMAIN = "domain";
30 const QString CookiesDatabase::EXPIRATION_DATE = "expiration_date";
31 const QString CookiesDatabase::HTTP_ONLY = "http_only";
32 const QString CookiesDatabase::ID = "_id";
33 const QString CookiesDatabase::NAME = "name";
34 const QString CookiesDatabase::PATH = "path";
35 const QString CookiesDatabase::SECURE = "secure";
36 const QString CookiesDatabase::VALUE = "value";
37
38 // Construct the class.
39 CookiesDatabase::CookiesDatabase() {}
40
41 void CookiesDatabase::addDatabase()
42 {
43     // Add the cookies database.
44     QSqlDatabase cookiesDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), CONNECTION_NAME);
45
46     // Set the database name.
47     cookiesDatabase.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/cookies.db");
48
49     // Open the database.
50     if (cookiesDatabase.open())  // Opening the database succeeded.
51     {
52         // Check to see if the cookies table already exists.
53         if (cookiesDatabase.tables().contains(COOKIES_TABLE))  // The cookies table exists.
54         {
55             // Query the database schema version.
56             QSqlQuery schemaVersionQuery = cookiesDatabase.exec(QStringLiteral("PRAGMA user_version"));
57
58             // Move to the first record.
59             schemaVersionQuery.first();
60
61             // Get the current schema versin.
62             int currentSchemaVersion = schemaVersionQuery.value(0).toInt();
63
64             // Check to see if the schema has been updated.
65             if (currentSchemaVersion < SCHEMA_VERSION)
66             {
67                 // Run the schema update code.
68                 switch (currentSchemaVersion)
69                 {
70                     // Upgrade code here.
71                 }
72
73                 // Update the schema version.
74                 cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
75             }
76         }
77         else  // The cookies table does not exist.
78         {
79             // Instantiate a create table query.
80             QSqlQuery createTableQuery(cookiesDatabase);
81
82             // Prepare the create table query.
83             createTableQuery.prepare("CREATE TABLE " + COOKIES_TABLE + "(" +
84                                      ID + " INTEGER PRIMARY KEY, " +
85                                      DOMAIN + " TEXT NOT NULL, " +
86                                      NAME + " TEXT NOT NULL, " +
87                                      PATH + " TEXT NOT NULL, " +
88                                      EXPIRATION_DATE + " TEXT, " +
89                                      HTTP_ONLY + " INTEGER NOT NULL DEFAULT 0, " +
90                                      SECURE + " INTEGER NOT NULL DEFAULT 0, " +
91                                      VALUE + " TEXT NOT NULL)");
92
93             // Execute the query.
94             if (!createTableQuery.exec())
95             {
96                 // Log any errors.
97                 qDebug().noquote().nospace() << "Error creating table:  " << cookiesDatabase.lastError();
98             }
99
100             // Set the schema version.
101             cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
102         }
103     }
104     else  // Opening the database failed.
105     {
106         // Write the last database error message to the debug output.
107         qDebug().noquote().nospace() << "Error opening database:  " << cookiesDatabase.lastError();
108     }
109 }
110
111 void CookiesDatabase::addCookie(const QNetworkCookie &cookie)
112 {
113     // Get a handle for the cookies database.
114     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
115
116     // Check to see if the cookie already exists in the database.
117     if (isDurable(cookie))  // The cookie already exists.
118     {
119         // Update the existing cookie.
120         updateCookie(cookie);
121     }
122     else  // The cookie doesn't already exist.
123     {
124         // Instantiate an add cookie query.
125         QSqlQuery addCookieQuery(cookiesDatabase);
126
127         // Prepare the add cookie query.
128         addCookieQuery.prepare("INSERT INTO " + COOKIES_TABLE + " (" +
129                                 DOMAIN + ", " +
130                                 NAME + ", " +
131                                 PATH + ", " +
132                                 EXPIRATION_DATE + ", " +
133                                 HTTP_ONLY + ", " +
134                                 SECURE + ", " +
135                                 VALUE + ") "
136                                 "VALUES (:domain, :name, :path, :expiration_date, :http_only, :secure, :value)"
137         );
138
139         // Bind the values.
140         addCookieQuery.bindValue(":domain", cookie.domain());
141         addCookieQuery.bindValue(":name", QString(cookie.name()));
142         addCookieQuery.bindValue(":path", cookie.path());
143         addCookieQuery.bindValue(":expiration_date", cookie.expirationDate());
144         addCookieQuery.bindValue(":http_only", cookie.isHttpOnly());
145         addCookieQuery.bindValue(":secure", cookie.isSecure());
146         addCookieQuery.bindValue(":value", QString(cookie.value()));
147
148         // Execute the query.
149         addCookieQuery.exec();
150     }
151 }
152
153 int CookiesDatabase::cookieCount()
154 {
155     // Get a handle for the cookies database.
156     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
157
158     // Instantiate a count cookies query.
159     QSqlQuery countCookiesQuery(cookiesDatabase);
160
161     // Set the query to be forward only.
162     countCookiesQuery.setForwardOnly(true);
163
164     // Prepare the query.
165     countCookiesQuery.prepare("SELECT " + ID + " FROM " + COOKIES_TABLE);
166
167     // Execute the query.
168     countCookiesQuery.exec();
169
170     // Move to the last row.
171     countCookiesQuery.last();
172
173     // Initialize a number of cookies variable.
174     int numberOfCookies = 0;
175
176     // Check to see if the query is valid (there is at least one cookie).
177     if (countCookiesQuery.isValid())
178     {
179         // Get the number of rows (which is zero based) and add one to calculate the number of cookies.
180         numberOfCookies = countCookiesQuery.at() + 1;
181     }
182
183     // Return the number of cookies.
184     return numberOfCookies;
185 }
186
187 void CookiesDatabase::deleteAllCookies()
188 {
189     // Get a handle for the cookies database.
190     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
191
192     // Instantiate a delete all cookies query.
193     QSqlQuery deleteAllCookiesQuery(cookiesDatabase);
194
195     // Prepare the delete all cookies query.
196     deleteAllCookiesQuery.prepare("DELETE FROM " + COOKIES_TABLE);
197
198     // Execute the query.
199     deleteAllCookiesQuery.exec();
200 }
201
202 void CookiesDatabase::deleteCookie(const QNetworkCookie &cookie)
203 {
204     // Get a handle for the cookies database.
205     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
206
207     // Instantiate a delete cookie query.
208     QSqlQuery deleteCookieQuery(cookiesDatabase);
209
210     // Prepare the delete cookie query.
211     deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " +
212                               DOMAIN + " = :domain AND " +
213                               NAME + " = :name AND " +
214                               PATH + " = :path");
215
216     // Bind the values.
217     deleteCookieQuery.bindValue(":domain", cookie.domain());
218     deleteCookieQuery.bindValue(":name", QString(cookie.name()));
219     deleteCookieQuery.bindValue(":path", cookie.path());
220
221     // Execute the query.
222     deleteCookieQuery.exec();
223 }
224
225 QList<QNetworkCookie*>* CookiesDatabase::getCookies()
226 {
227     // Get a handle for the cookies database.
228     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
229
230     // Instantiate a cookies query.
231     QSqlQuery cookiesQuery(cookiesDatabase);
232
233     // Set the query to be forward only, which is more performant.
234     cookiesQuery.setForwardOnly(true);
235
236     // Prepare the cookies query.
237     cookiesQuery.prepare("SELECT * FROM " + COOKIES_TABLE);
238
239     // Execute the query.
240     cookiesQuery.exec();
241
242     // Create a cookie list.
243     QList<QNetworkCookie*> *cookieListPointer = new QList<QNetworkCookie*>;
244
245     // Populate the cookie list.
246     while (cookiesQuery.next())
247     {
248         // Create a cookie.
249         QNetworkCookie *cookiePointer = new QNetworkCookie();
250
251         // Populate the cookie.
252         cookiePointer->setDomain(cookiesQuery.value(DOMAIN).toString());
253         cookiePointer->setName(cookiesQuery.value(NAME).toString().toUtf8());
254         cookiePointer->setPath(cookiesQuery.value(PATH).toString());
255         cookiePointer->setExpirationDate(QDateTime::fromString(cookiesQuery.value(EXPIRATION_DATE).toString(), Qt::ISODate));
256         cookiePointer->setHttpOnly(cookiesQuery.value(HTTP_ONLY).toBool());
257         cookiePointer->setSecure(cookiesQuery.value(SECURE).toBool());
258         cookiePointer->setValue(cookiesQuery.value(VALUE).toString().toUtf8());
259
260         // Add the cookie to the list.
261         cookieListPointer->append(cookiePointer);
262     }
263
264     // Return the cookie list.
265     return cookieListPointer;
266 }
267
268 QNetworkCookie* CookiesDatabase::getCookieById(const int &id)
269 {
270     // Get a handle for the cookies database.
271     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
272
273     // Instantiate a cookie query.
274     QSqlQuery cookieQuery(cookiesDatabase);
275
276     // Set the query to be forward only.
277     cookieQuery.setForwardOnly(true);
278
279     // Prepare the cookies query.
280     cookieQuery.prepare("SELECT * FROM " + COOKIES_TABLE + " WHERE " + ID + " = :id");
281
282     // Bind the values.
283     cookieQuery.bindValue(":id", id);
284
285     // Execute the query.
286     cookieQuery.exec();
287
288     // Move to the first entry.
289     cookieQuery.first();
290
291     // Create a cookie.
292     QNetworkCookie *cookiePointer = new QNetworkCookie();
293
294     // Populate the cookie.
295     cookiePointer->setDomain(cookieQuery.value(DOMAIN).toString());
296     cookiePointer->setName(cookieQuery.value(NAME).toString().toUtf8());
297     cookiePointer->setPath(cookieQuery.value(PATH).toString());
298     cookiePointer->setExpirationDate(QDateTime::fromString(cookieQuery.value(EXPIRATION_DATE).toString(), Qt::ISODate));
299     cookiePointer->setHttpOnly(cookieQuery.value(HTTP_ONLY).toBool());
300     cookiePointer->setSecure(cookieQuery.value(SECURE).toBool());
301     cookiePointer->setValue(cookieQuery.value(VALUE).toString().toUtf8());
302
303     // Return the cookie.
304     return cookiePointer;
305 }
306
307 bool CookiesDatabase::isDurable(const QNetworkCookie &cookie)
308 {
309     // Get a handle for the cookies database.
310     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
311
312     // Instantiate an is durable query.
313     QSqlQuery isDurableQuery(cookiesDatabase);
314
315     // Set the query to be forward only.
316     isDurableQuery.setForwardOnly(true);
317
318     // Prepare the is durable query.
319     isDurableQuery.prepare("SELECT " + ID + " FROM " + COOKIES_TABLE + " WHERE " +
320                             DOMAIN + " = :domain AND " +
321                             NAME + " = :name AND " +
322                             PATH + " = :path");
323
324     // Bind the values.
325     isDurableQuery.bindValue(":domain", cookie.domain());
326     isDurableQuery.bindValue(":name", QString(cookie.name()));
327     isDurableQuery.bindValue(":path", cookie.path());
328
329     // Execute the query.
330     isDurableQuery.exec();
331
332     // Move to the first entry.
333     isDurableQuery.first();
334
335     // Return the status of the cookie in the database.
336     return (isDurableQuery.isValid());
337 }
338
339 bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie)
340 {
341     // Get a handle for the cookies database.
342     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
343
344     // Instantiate an is update query.
345     QSqlQuery isUpdateQuery(cookiesDatabase);
346
347     // Prepare the is update query.
348     isUpdateQuery.prepare("SELECT " + EXPIRATION_DATE + " , " +
349                           HTTP_ONLY + " , " +
350                           SECURE + " , " +
351                           VALUE +
352                           " FROM " + COOKIES_TABLE +
353                           " WHERE " + DOMAIN + " = :domain AND " +
354                           NAME + " = :name AND " +
355                           PATH + " = :path");
356
357     // Bind the values.
358     isUpdateQuery.bindValue(":domain", cookie.domain());
359     isUpdateQuery.bindValue(":name", QString(cookie.name()));
360     isUpdateQuery.bindValue(":path", cookie.path());
361
362     // Execute the query.
363     isUpdateQuery.exec();
364
365     // Move to the first entry.
366     isUpdateQuery.first();
367
368     // Check to see if the cookie exists.
369     if (isUpdateQuery.isValid())  // The cookie exists in the database.
370     {
371         // Check to see if the cookie data has changed.
372         if ((QDateTime::fromString(isUpdateQuery.value(EXPIRATION_DATE).toString(), Qt::ISODate) != cookie.expirationDate()) ||
373             (isUpdateQuery.value(HTTP_ONLY).toBool() != cookie.isHttpOnly()) ||
374             (isUpdateQuery.value(SECURE).toBool() != cookie.isSecure()) ||
375             (isUpdateQuery.value(VALUE).toString().toUtf8() != cookie.value()))  // The cookies data has changed.
376         {
377             //qDebug() << "The durable cookie data has changed.";
378
379             // Return true.
380             return true;
381         }
382         else  // The cookie data has not changed.
383         {
384             //qDebug() << "The durable cookie data is unchanged.";
385
386             // Return false.
387             return false;
388         }
389     }
390     else  // The cookie does not exist in the database.
391     {
392         // Return false.
393         return false;
394     }
395 }
396
397 void CookiesDatabase::updateCookie(const QNetworkCookie &cookie)
398 {
399     // Get a handle for the cookies database.
400     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
401
402     // Create the update cookie query.
403     QSqlQuery updateCookieQuery(cookiesDatabase);
404
405     // Prepare the update cookie query.
406     updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE +
407                               " SET " + EXPIRATION_DATE + " = :expiration_date , " +
408                               HTTP_ONLY + " = :http_only , " +
409                               SECURE + " = :secure , " +
410                               VALUE + " = :value " +
411                               "WHERE " + DOMAIN + " = :domain AND " +
412                               NAME + " = :name AND " +
413                               PATH + " = :path");
414
415     // Bind the values.
416     updateCookieQuery.bindValue(":domain", cookie.domain());
417     updateCookieQuery.bindValue(":name", QString(cookie.name()));
418     updateCookieQuery.bindValue(":path", cookie.path());
419     updateCookieQuery.bindValue(":expiration_date", cookie.expirationDate());
420     updateCookieQuery.bindValue(":http_only", cookie.isHttpOnly());
421     updateCookieQuery.bindValue(":secure", cookie.isSecure());
422     updateCookieQuery.bindValue(":value", QString(cookie.value()));
423
424     // Execute the query.
425     updateCookieQuery.exec();
426 }
427
428 void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetworkCookie &newCookie)
429 {
430     // Get a handle for the cookies database.
431     QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
432
433     // Create the old cookie query.
434     QSqlQuery oldCookieQuery(cookiesDatabase);
435
436     // Set the query to be forward only.
437     oldCookieQuery.setForwardOnly(true);
438
439     // Prepare the old cookie query.
440     oldCookieQuery.prepare("SELECT " + ID + " FROM " + COOKIES_TABLE +
441                            " WHERE " + DOMAIN + " = :domain AND " +
442                            NAME + " = :name AND " +
443                            PATH + " = :path");
444
445     // Bind the values.
446     oldCookieQuery.bindValue(":domain", oldCookie.domain());
447     oldCookieQuery.bindValue(":name", QString(oldCookie.name()));
448     oldCookieQuery.bindValue(":path", oldCookie.path());
449
450     // Execute the query.
451     oldCookieQuery.exec();
452
453     // Move to the first entry.
454     oldCookieQuery.first();
455
456     // Create the update cookie query.
457     QSqlQuery updateCookieQuery(cookiesDatabase);
458
459     // Prepare the update cookie query.
460     updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE +
461                               " SET " + DOMAIN + " = :domain , " +
462                               NAME + " = :name , " +
463                               PATH + " = :path , " +
464                               EXPIRATION_DATE + " = :expiration_date , " +
465                               HTTP_ONLY + " = :http_only , " +
466                               SECURE + " = :secure , " +
467                               VALUE + " = :value " +
468                               "WHERE " + ID + " = :id");
469
470     // Bind the values.
471     updateCookieQuery.bindValue(":id", oldCookieQuery.value(0).toLongLong());
472     updateCookieQuery.bindValue(":domain", newCookie.domain());
473     updateCookieQuery.bindValue(":name", QString(newCookie.name()));
474     updateCookieQuery.bindValue(":path", newCookie.path());
475     updateCookieQuery.bindValue(":expiration_date", newCookie.expirationDate());
476     updateCookieQuery.bindValue(":http_only", newCookie.isHttpOnly());
477     updateCookieQuery.bindValue(":secure", newCookie.isSecure());
478     updateCookieQuery.bindValue(":value", QString(newCookie.value()));
479
480     // Execute the query.
481     updateCookieQuery.exec();
482 }