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 void CookiesDatabase::deleteAllCookies()
151 // Get a handle for the cookies database.
152 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
154 // Instantiate a delete all cookies query.
155 QSqlQuery deleteAllCookiesQuery(cookiesDatabase);
157 // Prepare the delete all cookies query.
158 deleteAllCookiesQuery.prepare("DELETE FROM " + COOKIES_TABLE);
160 // Execute the query.
161 deleteAllCookiesQuery.exec();
164 void CookiesDatabase::deleteCookie(const QNetworkCookie &cookie)
166 // Get a handle for the cookies database.
167 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
169 // Instantiate a delete cookie query.
170 QSqlQuery deleteCookieQuery(cookiesDatabase);
172 // Prepare the delete cookie query.
173 deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
176 deleteCookieQuery.bindValue(":domain", cookie.domain());
177 deleteCookieQuery.bindValue(":name", QString(cookie.name()));
178 deleteCookieQuery.bindValue(":path", cookie.path());
180 // Execute the query.
181 deleteCookieQuery.exec();
184 QList<QNetworkCookie*>* CookiesDatabase::getCookies()
186 // Get a handle for the cookies database.
187 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
189 // Instantiate a cookies query.
190 QSqlQuery cookiesQuery(cookiesDatabase);
192 // Set the query to be forward only.
193 cookiesQuery.setForwardOnly(true);
195 // Prepare the cookies query.
196 cookiesQuery.prepare("SELECT * FROM " + COOKIES_TABLE);
198 // Execute the query.
201 // Create a cookie list.
202 QList<QNetworkCookie*> *cookieListPointer = new QList<QNetworkCookie*>;
204 // Populate the cookie list.
205 while (cookiesQuery.next())
208 QNetworkCookie *cookiePointer = new QNetworkCookie();
210 // Populate the cookie.
211 cookiePointer->setDomain(cookiesQuery.value(DOMAIN).toString());
212 cookiePointer->setName(cookiesQuery.value(NAME).toString().toUtf8());
213 cookiePointer->setPath(cookiesQuery.value(PATH).toString());
214 cookiePointer->setExpirationDate(QDateTime::fromString(cookiesQuery.value(EXPIRATION_DATE).toString(), Qt::ISODate));
215 cookiePointer->setHttpOnly(cookiesQuery.value(HTTP_ONLY).toBool());
216 cookiePointer->setSecure(cookiesQuery.value(SECURE).toBool());
217 cookiePointer->setValue(cookiesQuery.value(VALUE).toString().toUtf8());
219 // Add the cookie to the list.
220 cookieListPointer->append(cookiePointer);
223 // Return the cookie list.
224 return cookieListPointer;
227 bool CookiesDatabase::isDurable(const QNetworkCookie &cookie)
229 // Get a handle for the cookies database.
230 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
232 // Instantiate an is durable query.
233 QSqlQuery isDurableQuery(cookiesDatabase);
235 // Set the query to be forward only.
236 isDurableQuery.setForwardOnly(true);
238 // Prepare the is durable query.
239 isDurableQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
242 isDurableQuery.bindValue(":domain", cookie.domain());
243 isDurableQuery.bindValue(":name", QString(cookie.name()));
244 isDurableQuery.bindValue(":path", cookie.path());
246 // Execute the query.
247 isDurableQuery.exec();
249 // Move to the first entry.
250 isDurableQuery.first();
252 // Return the status of the cookie in the database.
253 return (isDurableQuery.isValid());
256 bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie)
258 // Get a handle for the cookies database.
259 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
261 // Instantiate an is update query.
262 QSqlQuery isUpdateQuery(cookiesDatabase);
264 // Prepare the is update query.
265 isUpdateQuery.prepare("SELECT " + EXPIRATION_DATE + " , " + HTTP_ONLY + " , " + SECURE + " , " + VALUE + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " +
266 NAME + " = :name AND " + PATH + " = :path");
269 isUpdateQuery.bindValue(":domain", cookie.domain());
270 isUpdateQuery.bindValue(":name", QString(cookie.name()));
271 isUpdateQuery.bindValue(":path", cookie.path());
273 // Execute the query.
274 isUpdateQuery.exec();
276 // Move to the first entry.
277 isUpdateQuery.first();
279 // Check to see if the cookie exists.
280 if (isUpdateQuery.isValid()) // The cookie exists in the database.
282 // Check to see if the cookie data has changed.
283 if ((QDateTime::fromString(isUpdateQuery.value(0).toString(), Qt::ISODate) != cookie.expirationDate()) ||
284 (isUpdateQuery.value(1).toBool() != cookie.isHttpOnly()) ||
285 (isUpdateQuery.value(2).toBool() != cookie.isSecure()) ||
286 (isUpdateQuery.value(3).toString().toUtf8() != cookie.value())) // The cookies data has changed.
288 qDebug() << "The durable cookie data has changed.";
293 else // The cookie data has not changed.
295 qDebug() << "The durable cookie data is unchanged.";
301 else // The cookie does not exist in the database.
308 void CookiesDatabase::updateCookie(const QNetworkCookie &cookie)
310 // Get a handle for the cookies database.
311 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
313 // Create the update cookie query.
314 QSqlQuery updateCookieQuery(cookiesDatabase);
316 // Prepare the edit cookie query.
317 updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + EXPIRATION_DATE + " = :expiration_date , " + HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " +
318 VALUE + " = :value WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
321 updateCookieQuery.bindValue(":domain", cookie.domain());
322 updateCookieQuery.bindValue(":name", QString(cookie.name()));
323 updateCookieQuery.bindValue(":path", cookie.path());
324 updateCookieQuery.bindValue(":expiration_date", cookie.expirationDate());
325 updateCookieQuery.bindValue(":http_only", cookie.isHttpOnly());
326 updateCookieQuery.bindValue(":secure", cookie.isSecure());
327 updateCookieQuery.bindValue(":value", QString(cookie.value()));
329 // Execute the query.
330 updateCookieQuery.exec();
333 void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetworkCookie &newCookie)
335 // Get a handle for the cookies database.
336 QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
338 // Create the old cookie query.
339 QSqlQuery oldCookieQuery(cookiesDatabase);
341 // Set the query to be forward only.
342 oldCookieQuery.setForwardOnly(true);
344 // Prepare the old cookie query.
345 oldCookieQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
348 oldCookieQuery.bindValue(":domain", oldCookie.domain());
349 oldCookieQuery.bindValue(":name", QString(oldCookie.name()));
350 oldCookieQuery.bindValue(":path", oldCookie.path());
352 // Execute the query.
353 oldCookieQuery.exec();
355 // Move to the first entry.
356 oldCookieQuery.first();
358 // Create the update cookie query.
359 QSqlQuery updateCookieQuery(cookiesDatabase);
361 // Prepare the update cookie query.
362 updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + DOMAIN + " = :domain , " + NAME + " = :name , " + PATH + " = :path , " + EXPIRATION_DATE + " = :expiration_date , " +
363 HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " + VALUE + " = :value WHERE " + _ID + " = :id");
366 updateCookieQuery.bindValue(":id", oldCookieQuery.value(0).toLongLong());
367 updateCookieQuery.bindValue(":domain", newCookie.domain());
368 updateCookieQuery.bindValue(":name", QString(newCookie.name()));
369 updateCookieQuery.bindValue(":path", newCookie.path());
370 updateCookieQuery.bindValue(":expiration_date", newCookie.expirationDate());
371 updateCookieQuery.bindValue(":http_only", newCookie.isHttpOnly());
372 updateCookieQuery.bindValue(":secure", newCookie.isSecure());
373 updateCookieQuery.bindValue(":value", QString(newCookie.value()));
375 // Execute the query.
376 updateCookieQuery.exec();