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