2 * Copyright 2022-2023 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
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.
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.
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/>.
20 // Application headers.
21 #include "CookiesDatabase.h"
23 // Define the private static schema constants.
24 const int CookiesDatabase::SCHEMA_VERSION = 0;
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";
38 // Construct the class.
39 CookiesDatabase::CookiesDatabase() {}
41 void CookiesDatabase::addDatabase()
43 // Add the cookies database.
44 QSqlDatabase cookiesDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), CONNECTION_NAME);
46 // Set the database name.
47 cookiesDatabase.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/cookies.db");
50 if (cookiesDatabase.open()) // Opening the database succeeded.
52 // Check to see if the cookies table already exists.
53 if (cookiesDatabase.tables().contains(COOKIES_TABLE)) // The cookies table exists.
55 // Query the database schema version.
56 QSqlQuery schemaVersionQuery = cookiesDatabase.exec(QStringLiteral("PRAGMA user_version"));
58 // Move to the first record.
59 schemaVersionQuery.first();
61 // Get the current schema versin.
62 int currentSchemaVersion = schemaVersionQuery.value(0).toInt();
64 // Check to see if the schema has been updated.
65 if (currentSchemaVersion < SCHEMA_VERSION)
67 // Run the schema update code.
68 switch (currentSchemaVersion)
73 // Update the schema version.
74 cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
77 else // The cookies table does not exist.
79 // Instantiate a create table query.
80 QSqlQuery createTableQuery(cookiesDatabase);
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)");
94 if (!createTableQuery.exec())
97 qDebug().noquote().nospace() << "Error creating table: " << cookiesDatabase.lastError();
100 // Set the schema version.
101 cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
104 else // Opening the database failed.
106 // Write the last database error message to the debug output.
107 qDebug().noquote().nospace() << "Error opening database: " << cookiesDatabase.lastError();
111 void CookiesDatabase::addCookie(const QNetworkCookie &cookie)
113 // Get a handle for the cookies database.
114 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
116 // Check to see if the cookie already exists in the database.
117 if (isDurable(cookie)) // The cookie already exists.
119 // Update the existing cookie.
120 updateCookie(cookie);
122 else // The cookie doesn't already exist.
124 // Instantiate an add cookie query.
125 QSqlQuery addCookieQuery(cookiesDatabase);
127 // Prepare the add cookie query.
128 addCookieQuery.prepare("INSERT INTO " + COOKIES_TABLE + " (" +
132 EXPIRATION_DATE + ", " +
136 "VALUES (:domain, :name, :path, :expiration_date, :http_only, :secure, :value)"
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()));
148 // Execute the query.
149 addCookieQuery.exec();
153 int CookiesDatabase::cookieCount()
155 // Get a handle for the cookies database.
156 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
158 // Instantiate a count cookies query.
159 QSqlQuery countCookiesQuery(cookiesDatabase);
161 // Set the query to be forward only.
162 countCookiesQuery.setForwardOnly(true);
164 // Prepare the query.
165 countCookiesQuery.prepare("SELECT " + ID + " FROM " + COOKIES_TABLE);
167 // Execute the query.
168 countCookiesQuery.exec();
170 // Move to the last row.
171 countCookiesQuery.last();
173 // Initialize a number of cookies variable.
174 int numberOfCookies = 0;
176 // Check to see if the query is valid (there is at least one cookie).
177 if (countCookiesQuery.isValid())
179 // Get the number of rows (which is zero based) and add one to calculate the number of cookies.
180 numberOfCookies = countCookiesQuery.at() + 1;
183 // Return the number of cookies.
184 return numberOfCookies;
187 void CookiesDatabase::deleteAllCookies()
189 // Get a handle for the cookies database.
190 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
192 // Instantiate a delete all cookies query.
193 QSqlQuery deleteAllCookiesQuery(cookiesDatabase);
195 // Prepare the delete all cookies query.
196 deleteAllCookiesQuery.prepare("DELETE FROM " + COOKIES_TABLE);
198 // Execute the query.
199 deleteAllCookiesQuery.exec();
202 void CookiesDatabase::deleteCookie(const QNetworkCookie &cookie)
204 // Get a handle for the cookies database.
205 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
207 // Instantiate a delete cookie query.
208 QSqlQuery deleteCookieQuery(cookiesDatabase);
210 // Prepare the delete cookie query.
211 deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " +
212 DOMAIN + " = :domain AND " +
213 NAME + " = :name AND " +
217 deleteCookieQuery.bindValue(":domain", cookie.domain());
218 deleteCookieQuery.bindValue(":name", QString(cookie.name()));
219 deleteCookieQuery.bindValue(":path", cookie.path());
221 // Execute the query.
222 deleteCookieQuery.exec();
225 QList<QNetworkCookie*>* CookiesDatabase::getCookies()
227 // Get a handle for the cookies database.
228 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
230 // Instantiate a cookies query.
231 QSqlQuery cookiesQuery(cookiesDatabase);
233 // Set the query to be forward only.
234 cookiesQuery.setForwardOnly(true);
236 // Prepare the cookies query.
237 cookiesQuery.prepare("SELECT * FROM " + COOKIES_TABLE);
239 // Execute the query.
242 // Create a cookie list.
243 QList<QNetworkCookie*> *cookieListPointer = new QList<QNetworkCookie*>;
245 // Populate the cookie list.
246 while (cookiesQuery.next())
249 QNetworkCookie *cookiePointer = new QNetworkCookie();
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());
260 // Add the cookie to the list.
261 cookieListPointer->append(cookiePointer);
264 // Return the cookie list.
265 return cookieListPointer;
268 QNetworkCookie* CookiesDatabase::getCookieById(const int &id)
270 // Get a handle for the cookies database.
271 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
273 // Instantiate a cookie query.
274 QSqlQuery cookieQuery(cookiesDatabase);
276 // Set the query to be forward only.
277 cookieQuery.setForwardOnly(true);
279 // Prepare the cookies query.
280 cookieQuery.prepare("SELECT * FROM " + COOKIES_TABLE + " WHERE " + ID + " = :id");
283 cookieQuery.bindValue(":id", id);
285 // Execute the query.
288 // Move to the first entry.
292 QNetworkCookie *cookiePointer = new QNetworkCookie();
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());
303 // Return the cookie.
304 return cookiePointer;
307 bool CookiesDatabase::isDurable(const QNetworkCookie &cookie)
309 // Get a handle for the cookies database.
310 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
312 // Instantiate an is durable query.
313 QSqlQuery isDurableQuery(cookiesDatabase);
315 // Set the query to be forward only.
316 isDurableQuery.setForwardOnly(true);
318 // Prepare the is durable query.
319 isDurableQuery.prepare("SELECT " + ID + " FROM " + COOKIES_TABLE + " WHERE " +
320 DOMAIN + " = :domain AND " +
321 NAME + " = :name AND " +
325 isDurableQuery.bindValue(":domain", cookie.domain());
326 isDurableQuery.bindValue(":name", QString(cookie.name()));
327 isDurableQuery.bindValue(":path", cookie.path());
329 // Execute the query.
330 isDurableQuery.exec();
332 // Move to the first entry.
333 isDurableQuery.first();
335 // Return the status of the cookie in the database.
336 return (isDurableQuery.isValid());
339 bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie)
341 // Get a handle for the cookies database.
342 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
344 // Instantiate an is update query.
345 QSqlQuery isUpdateQuery(cookiesDatabase);
347 // Prepare the is update query.
348 isUpdateQuery.prepare("SELECT " + EXPIRATION_DATE + " , " +
352 " FROM " + COOKIES_TABLE +
353 " WHERE " + DOMAIN + " = :domain AND " +
354 NAME + " = :name AND " +
358 isUpdateQuery.bindValue(":domain", cookie.domain());
359 isUpdateQuery.bindValue(":name", QString(cookie.name()));
360 isUpdateQuery.bindValue(":path", cookie.path());
362 // Execute the query.
363 isUpdateQuery.exec();
365 // Move to the first entry.
366 isUpdateQuery.first();
368 // Check to see if the cookie exists.
369 if (isUpdateQuery.isValid()) // The cookie exists in the database.
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.
377 //qDebug() << "The durable cookie data has changed.";
382 else // The cookie data has not changed.
384 //qDebug() << "The durable cookie data is unchanged.";
390 else // The cookie does not exist in the database.
397 void CookiesDatabase::updateCookie(const QNetworkCookie &cookie)
399 // Get a handle for the cookies database.
400 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
402 // Create the update cookie query.
403 QSqlQuery updateCookieQuery(cookiesDatabase);
405 // Prepare the edit 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 " +
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()));
424 // Execute the query.
425 updateCookieQuery.exec();
428 void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetworkCookie &newCookie)
430 // Get a handle for the cookies database.
431 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
433 // Create the old cookie query.
434 QSqlQuery oldCookieQuery(cookiesDatabase);
436 // Set the query to be forward only.
437 oldCookieQuery.setForwardOnly(true);
439 // Prepare the old cookie query.
440 oldCookieQuery.prepare("SELECT " + ID + " FROM " + COOKIES_TABLE +
441 " WHERE " + DOMAIN + " = :domain AND " +
442 NAME + " = :name AND " +
446 oldCookieQuery.bindValue(":domain", oldCookie.domain());
447 oldCookieQuery.bindValue(":name", QString(oldCookie.name()));
448 oldCookieQuery.bindValue(":path", oldCookie.path());
450 // Execute the query.
451 oldCookieQuery.exec();
453 // Move to the first entry.
454 oldCookieQuery.first();
456 // Create the update cookie query.
457 QSqlQuery updateCookieQuery(cookiesDatabase);
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");
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()));
480 // Execute the query.
481 updateCookieQuery.exec();