2 * Copyright © 2022 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 database constants.
27 const QString CookiesDatabase::CONNECTION_NAME = "cookies_database";
28 const QString CookiesDatabase::COOKIES_TABLE = "cookies";
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";
40 // Construct the class.
41 CookiesDatabase::CookiesDatabase() {}
43 void CookiesDatabase::addDatabase()
45 // Add the cookies database.
46 QSqlDatabase cookiesDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), CONNECTION_NAME);
48 // Set the database name.
49 cookiesDatabase.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/cookies.db");
52 if (cookiesDatabase.open()) // Opening the database succeeded.
54 // Check to see if the cookies table already exists.
55 if (cookiesDatabase.tables().contains(COOKIES_TABLE)) // The cookies table exists.
57 // Query the database schema version.
58 QSqlQuery schemaVersionQuery = cookiesDatabase.exec(QStringLiteral("PRAGMA user_version"));
60 // Move to the first record.
61 schemaVersionQuery.first();
63 // Get the current schema versin.
64 int currentSchemaVersion = schemaVersionQuery.value(0).toInt();
66 // Check to see if the schema has been updated.
67 if (currentSchemaVersion < SCHEMA_VERSION)
69 // Run the schema update code.
70 switch (currentSchemaVersion)
75 // Update the schema version.
76 cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
79 else // The cookies table does not exist.
81 // Instantiate a create table query.
82 QSqlQuery createTableQuery(cookiesDatabase);
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)"
97 if (!createTableQuery.exec())
100 qDebug().noquote().nospace() << "Error creating table: " << cookiesDatabase.lastError();
103 // Set the schema version.
104 cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
107 else // Opening the database failed.
109 // Write the last database error message to the debug output.
110 qDebug().noquote().nospace() << "Error opening database: " << cookiesDatabase.lastError();
114 void CookiesDatabase::addCookie(const QNetworkCookie &cookie)
116 // Get a handle for the cookies database.
117 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
119 // Check to see if the cookie already exists in the database.
120 if (isDurable(cookie)) // The cookie already exists.
122 // Update the existing cookie.
123 updateCookie(cookie);
125 else // The cookie doesn't already exist.
127 // Instantiate an add cookie query.
128 QSqlQuery addCookieQuery(cookiesDatabase);
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)"
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()));
144 // Execute the query.
145 addCookieQuery.exec();
149 int CookiesDatabase::cookieCount()
151 // Get a handle for the cookies database.
152 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
154 // Instantiate a count cookies query.
155 QSqlQuery countCookiesQuery(cookiesDatabase);
157 // Set the query to be forward only.
158 countCookiesQuery.setForwardOnly(true);
160 // Prepare the query.
161 countCookiesQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE);
163 // Execute the query.
164 countCookiesQuery.exec();
166 // Move to the last row.
167 countCookiesQuery.last();
169 // Initialize a number of cookies variable.
170 int numberOfCookies = 0;
172 // Check to see if the query is valid (there is at least one cookie).
173 if (countCookiesQuery.isValid())
175 // Get the number of rows (which is zero based) and add one to calculate the number of cookies.
176 numberOfCookies = countCookiesQuery.at() + 1;
179 // Return the number of cookies.
180 return numberOfCookies;
183 void CookiesDatabase::deleteAllCookies()
185 // Get a handle for the cookies database.
186 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
188 // Instantiate a delete all cookies query.
189 QSqlQuery deleteAllCookiesQuery(cookiesDatabase);
191 // Prepare the delete all cookies query.
192 deleteAllCookiesQuery.prepare("DELETE FROM " + COOKIES_TABLE);
194 // Execute the query.
195 deleteAllCookiesQuery.exec();
198 void CookiesDatabase::deleteCookie(const QNetworkCookie &cookie)
200 // Get a handle for the cookies database.
201 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
203 // Instantiate a delete cookie query.
204 QSqlQuery deleteCookieQuery(cookiesDatabase);
206 // Prepare the delete cookie query.
207 deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
210 deleteCookieQuery.bindValue(":domain", cookie.domain());
211 deleteCookieQuery.bindValue(":name", QString(cookie.name()));
212 deleteCookieQuery.bindValue(":path", cookie.path());
214 // Execute the query.
215 deleteCookieQuery.exec();
218 QList<QNetworkCookie*>* CookiesDatabase::getCookies()
220 // Get a handle for the cookies database.
221 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
223 // Instantiate a cookies query.
224 QSqlQuery cookiesQuery(cookiesDatabase);
226 // Set the query to be forward only.
227 cookiesQuery.setForwardOnly(true);
229 // Prepare the cookies query.
230 cookiesQuery.prepare("SELECT * FROM " + COOKIES_TABLE);
232 // Execute the query.
235 // Create a cookie list.
236 QList<QNetworkCookie*> *cookieListPointer = new QList<QNetworkCookie*>;
238 // Populate the cookie list.
239 while (cookiesQuery.next())
242 QNetworkCookie *cookiePointer = new QNetworkCookie();
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());
253 // Add the cookie to the list.
254 cookieListPointer->append(cookiePointer);
257 // Return the cookie list.
258 return cookieListPointer;
261 QNetworkCookie* CookiesDatabase::getCookieById(const int &id)
263 // Get a handle for the cookies database.
264 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
266 // Instantiate a cookie query.
267 QSqlQuery cookieQuery(cookiesDatabase);
269 // Set the query to be forward only.
270 cookieQuery.setForwardOnly(true);
272 // Prepare the cookies query.
273 cookieQuery.prepare("SELECT * FROM " + COOKIES_TABLE + " WHERE " + _ID + " = :id");
276 cookieQuery.bindValue(":id", id);
278 // Execute the query.
281 // Move to the first entry.
285 QNetworkCookie *cookiePointer = new QNetworkCookie();
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());
296 // Return the cookie.
297 return cookiePointer;
300 bool CookiesDatabase::isDurable(const QNetworkCookie &cookie)
302 // Get a handle for the cookies database.
303 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
305 // Instantiate an is durable query.
306 QSqlQuery isDurableQuery(cookiesDatabase);
308 // Set the query to be forward only.
309 isDurableQuery.setForwardOnly(true);
311 // Prepare the is durable query.
312 isDurableQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
315 isDurableQuery.bindValue(":domain", cookie.domain());
316 isDurableQuery.bindValue(":name", QString(cookie.name()));
317 isDurableQuery.bindValue(":path", cookie.path());
319 // Execute the query.
320 isDurableQuery.exec();
322 // Move to the first entry.
323 isDurableQuery.first();
325 // Return the status of the cookie in the database.
326 return (isDurableQuery.isValid());
329 bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie)
331 // Get a handle for the cookies database.
332 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
334 // Instantiate an is update query.
335 QSqlQuery isUpdateQuery(cookiesDatabase);
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");
342 isUpdateQuery.bindValue(":domain", cookie.domain());
343 isUpdateQuery.bindValue(":name", QString(cookie.name()));
344 isUpdateQuery.bindValue(":path", cookie.path());
346 // Execute the query.
347 isUpdateQuery.exec();
349 // Move to the first entry.
350 isUpdateQuery.first();
352 // Check to see if the cookie exists.
353 if (isUpdateQuery.isValid()) // The cookie exists in the database.
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.
361 //qDebug() << "The durable cookie data has changed.";
366 else // The cookie data has not changed.
368 //qDebug() << "The durable cookie data is unchanged.";
374 else // The cookie does not exist in the database.
381 void CookiesDatabase::updateCookie(const QNetworkCookie &cookie)
383 // Get a handle for the cookies database.
384 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
386 // Create the update cookie query.
387 QSqlQuery updateCookieQuery(cookiesDatabase);
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");
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()));
402 // Execute the query.
403 updateCookieQuery.exec();
406 void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetworkCookie &newCookie)
408 // Get a handle for the cookies database.
409 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
411 // Create the old cookie query.
412 QSqlQuery oldCookieQuery(cookiesDatabase);
414 // Set the query to be forward only.
415 oldCookieQuery.setForwardOnly(true);
417 // Prepare the old cookie query.
418 oldCookieQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
421 oldCookieQuery.bindValue(":domain", oldCookie.domain());
422 oldCookieQuery.bindValue(":name", QString(oldCookie.name()));
423 oldCookieQuery.bindValue(":path", oldCookie.path());
425 // Execute the query.
426 oldCookieQuery.exec();
428 // Move to the first entry.
429 oldCookieQuery.first();
431 // Create the update cookie query.
432 QSqlQuery updateCookieQuery(cookiesDatabase);
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");
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()));
448 // Execute the query.
449 updateCookieQuery.exec();