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 // Get the number of rows (which is zero based).
170 int numberOfCookies = countCookiesQuery.at() + 1;
172 // Return the number of cookies.
173 return numberOfCookies;
176 void CookiesDatabase::deleteAllCookies()
178 // Get a handle for the cookies database.
179 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
181 // Instantiate a delete all cookies query.
182 QSqlQuery deleteAllCookiesQuery(cookiesDatabase);
184 // Prepare the delete all cookies query.
185 deleteAllCookiesQuery.prepare("DELETE FROM " + COOKIES_TABLE);
187 // Execute the query.
188 deleteAllCookiesQuery.exec();
191 void CookiesDatabase::deleteCookie(const QNetworkCookie &cookie)
193 // Get a handle for the cookies database.
194 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
196 // Instantiate a delete cookie query.
197 QSqlQuery deleteCookieQuery(cookiesDatabase);
199 // Prepare the delete cookie query.
200 deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
203 deleteCookieQuery.bindValue(":domain", cookie.domain());
204 deleteCookieQuery.bindValue(":name", QString(cookie.name()));
205 deleteCookieQuery.bindValue(":path", cookie.path());
207 // Execute the query.
208 deleteCookieQuery.exec();
211 QList<QNetworkCookie*>* CookiesDatabase::getCookies()
213 // Get a handle for the cookies database.
214 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
216 // Instantiate a cookies query.
217 QSqlQuery cookiesQuery(cookiesDatabase);
219 // Set the query to be forward only.
220 cookiesQuery.setForwardOnly(true);
222 // Prepare the cookies query.
223 cookiesQuery.prepare("SELECT * FROM " + COOKIES_TABLE);
225 // Execute the query.
228 // Create a cookie list.
229 QList<QNetworkCookie*> *cookieListPointer = new QList<QNetworkCookie*>;
231 // Populate the cookie list.
232 while (cookiesQuery.next())
235 QNetworkCookie *cookiePointer = new QNetworkCookie();
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());
246 // Add the cookie to the list.
247 cookieListPointer->append(cookiePointer);
250 // Return the cookie list.
251 return cookieListPointer;
254 QNetworkCookie* CookiesDatabase::getCookieById(const int &id)
256 // Get a handle for the cookies database.
257 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
259 // Instantiate a cookie query.
260 QSqlQuery cookieQuery(cookiesDatabase);
262 // Set the query to be forward only.
263 cookieQuery.setForwardOnly(true);
265 // Prepare the cookies query.
266 cookieQuery.prepare("SELECT * FROM " + COOKIES_TABLE + " WHERE " + _ID + " = :id");
269 cookieQuery.bindValue(":id", id);
271 // Execute the query.
274 // Move to the first entry.
278 QNetworkCookie *cookiePointer = new QNetworkCookie();
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());
289 // Return the cookie.
290 return cookiePointer;
293 bool CookiesDatabase::isDurable(const QNetworkCookie &cookie)
295 // Get a handle for the cookies database.
296 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
298 // Instantiate an is durable query.
299 QSqlQuery isDurableQuery(cookiesDatabase);
301 // Set the query to be forward only.
302 isDurableQuery.setForwardOnly(true);
304 // Prepare the is durable query.
305 isDurableQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
308 isDurableQuery.bindValue(":domain", cookie.domain());
309 isDurableQuery.bindValue(":name", QString(cookie.name()));
310 isDurableQuery.bindValue(":path", cookie.path());
312 // Execute the query.
313 isDurableQuery.exec();
315 // Move to the first entry.
316 isDurableQuery.first();
318 // Return the status of the cookie in the database.
319 return (isDurableQuery.isValid());
322 bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie)
324 // Get a handle for the cookies database.
325 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
327 // Instantiate an is update query.
328 QSqlQuery isUpdateQuery(cookiesDatabase);
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");
335 isUpdateQuery.bindValue(":domain", cookie.domain());
336 isUpdateQuery.bindValue(":name", QString(cookie.name()));
337 isUpdateQuery.bindValue(":path", cookie.path());
339 // Execute the query.
340 isUpdateQuery.exec();
342 // Move to the first entry.
343 isUpdateQuery.first();
345 // Check to see if the cookie exists.
346 if (isUpdateQuery.isValid()) // The cookie exists in the database.
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.
354 qDebug() << "The durable cookie data has changed.";
359 else // The cookie data has not changed.
361 qDebug() << "The durable cookie data is unchanged.";
367 else // The cookie does not exist in the database.
374 void CookiesDatabase::updateCookie(const QNetworkCookie &cookie)
376 // Get a handle for the cookies database.
377 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
379 // Create the update cookie query.
380 QSqlQuery updateCookieQuery(cookiesDatabase);
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");
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()));
395 // Execute the query.
396 updateCookieQuery.exec();
399 void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetworkCookie &newCookie)
401 // Get a handle for the cookies database.
402 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
404 // Create the old cookie query.
405 QSqlQuery oldCookieQuery(cookiesDatabase);
407 // Set the query to be forward only.
408 oldCookieQuery.setForwardOnly(true);
410 // Prepare the old cookie query.
411 oldCookieQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
414 oldCookieQuery.bindValue(":domain", oldCookie.domain());
415 oldCookieQuery.bindValue(":name", QString(oldCookie.name()));
416 oldCookieQuery.bindValue(":path", oldCookie.path());
418 // Execute the query.
419 oldCookieQuery.exec();
421 // Move to the first entry.
422 oldCookieQuery.first();
424 // Create the update cookie query.
425 QSqlQuery updateCookieQuery(cookiesDatabase);
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");
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()));
441 // Execute the query.
442 updateCookieQuery.exec();