--- /dev/null
+/*
+ * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
+ *
+ * Privacy Browser PC is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Privacy Browser PC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Privacy Browser PC. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Application headers.
+#include "CookiesDatabase.h"
+
+// Define the private static schema constants.
+const int CookiesDatabase::SCHEMA_VERSION = 0;
+
+// Define the public static database constants.
+const QString CookiesDatabase::CONNECTION_NAME = "cookies_database";
+const QString CookiesDatabase::COOKIES_TABLE = "cookies";
+
+// Define the public static database field names.
+const QString CookiesDatabase::_ID = "_id";
+const QString CookiesDatabase::DOMAIN = "domain";
+const QString CookiesDatabase::NAME = "name";
+const QString CookiesDatabase::PATH = "path";
+const QString CookiesDatabase::EXPIRATION_DATE = "expiration_date";
+const QString CookiesDatabase::HTTP_ONLY = "http_only";
+const QString CookiesDatabase::SECURE = "secure";
+const QString CookiesDatabase::VALUE = "value";
+
+// Construct the class.
+CookiesDatabase::CookiesDatabase() {}
+
+void CookiesDatabase::addDatabase()
+{
+ // Add the cookies database.
+ QSqlDatabase cookiesDatabase = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), CONNECTION_NAME);
+
+ // Set the database name.
+ cookiesDatabase.setDatabaseName(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/cookies.db");
+
+ // Open the database.
+ if (cookiesDatabase.open()) // Opening the database succeeded.
+ {
+ // Check to see if the cookies table already exists.
+ if (cookiesDatabase.tables().contains(COOKIES_TABLE)) // The cookies table exists.
+ {
+ // Query the database schema version.
+ QSqlQuery schemaVersionQuery = cookiesDatabase.exec(QStringLiteral("PRAGMA user_version"));
+
+ // Move to the first record.
+ schemaVersionQuery.first();
+
+ // Get the current schema versin.
+ int currentSchemaVersion = schemaVersionQuery.value(0).toInt();
+
+ // Check to see if the schema has been updated.
+ if (currentSchemaVersion < SCHEMA_VERSION)
+ {
+ // Run the schema update code.
+ switch (currentSchemaVersion)
+ {
+ // Upgrade code here.
+ }
+
+ // Update the schema version.
+ cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
+ }
+ }
+ else // The cookies table does not exist.
+ {
+ // Instantiate a create table query.
+ QSqlQuery createTableQuery(cookiesDatabase);
+
+ // Prepare the create table query.
+ createTableQuery.prepare("CREATE TABLE " + COOKIES_TABLE + "(" +
+ _ID + " INTEGER PRIMARY KEY, " +
+ DOMAIN + " TEXT NOT NULL, " +
+ NAME + " TEXT NOT NULL, " +
+ PATH + " TEXT NOT NULL, " +
+ EXPIRATION_DATE + " TEXT, " +
+ HTTP_ONLY + " INTEGER NOT NULL DEFAULT 0, " +
+ SECURE + " INTEGER NOT NULL DEFAULT 0, " +
+ VALUE + " TEXT NOT NULL)"
+ );
+
+ // Execute the query.
+ if (!createTableQuery.exec())
+ {
+ // Log any errors.
+ qDebug().noquote().nospace() << "Error creating table: " << cookiesDatabase.lastError();
+ }
+
+ // Set the schema version.
+ cookiesDatabase.exec("PRAGMA user_version = " + QString::number(SCHEMA_VERSION));
+ }
+ }
+ else // Opening the database failed.
+ {
+ // Write the last database error message to the debug output.
+ qDebug().noquote().nospace() << "Error opening database: " << cookiesDatabase.lastError();
+ }
+}
+
+void CookiesDatabase::addCookie(const QNetworkCookie &cookie)
+{
+ // Get a handle for the cookies database.
+ QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+ // Check to see if the cookie already exists in the database.
+ if (isDurable(cookie)) // The cookie already exists.
+ {
+ // Update the existing cookie.
+ updateCookie(cookie);
+ }
+ else // The cookie doesn't already exist.
+ {
+ // Instantiate an add cookie query.
+ QSqlQuery addCookieQuery(cookiesDatabase);
+
+ // Prepare the add cookie query.
+ addCookieQuery.prepare("INSERT INTO " + COOKIES_TABLE + " (" + DOMAIN + ", " + NAME + ", " + PATH + ", " + EXPIRATION_DATE + ", " + HTTP_ONLY + ", " + SECURE + ", " + VALUE + ") "
+ "VALUES (:domain, :name, :path, :expiration_date, :http_only, :secure, :value)"
+ );
+
+ // Bind the values.
+ addCookieQuery.bindValue(":domain", cookie.domain());
+ addCookieQuery.bindValue(":name", QString(cookie.name()));
+ addCookieQuery.bindValue(":path", cookie.path());
+ addCookieQuery.bindValue(":expiration_date", cookie.expirationDate());
+ addCookieQuery.bindValue(":http_only", cookie.isHttpOnly());
+ addCookieQuery.bindValue(":secure", cookie.isSecure());
+ addCookieQuery.bindValue(":value", QString(cookie.value()));
+
+ // Execute the query.
+ addCookieQuery.exec();
+ }
+}
+
+void CookiesDatabase::deleteAllCookies()
+{
+ // Get a handle for the cookies database.
+ QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+ // Instantiate a delete all cookies query.
+ QSqlQuery deleteAllCookiesQuery(cookiesDatabase);
+
+ // Prepare the delete all cookies query.
+ deleteAllCookiesQuery.prepare("DELETE FROM " + COOKIES_TABLE);
+
+ // Execute the query.
+ deleteAllCookiesQuery.exec();
+}
+
+void CookiesDatabase::deleteCookie(const QNetworkCookie &cookie)
+{
+ // Get a handle for the cookies database.
+ QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+ // Instantiate a delete cookie query.
+ QSqlQuery deleteCookieQuery(cookiesDatabase);
+
+ // Prepare the delete cookie query.
+ deleteCookieQuery.prepare("DELETE FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
+
+ // Bind the values.
+ deleteCookieQuery.bindValue(":domain", cookie.domain());
+ deleteCookieQuery.bindValue(":name", QString(cookie.name()));
+ deleteCookieQuery.bindValue(":path", cookie.path());
+
+ // Execute the query.
+ deleteCookieQuery.exec();
+}
+
+QList<QNetworkCookie*>* CookiesDatabase::getCookies()
+{
+ // Get a handle for the cookies database.
+ QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+ // Instantiate a cookies query.
+ QSqlQuery cookiesQuery(cookiesDatabase);
+
+ // Set the query to be forward only.
+ cookiesQuery.setForwardOnly(true);
+
+ // Prepare the cookies query.
+ cookiesQuery.prepare("SELECT * FROM " + COOKIES_TABLE);
+
+ // Execute the query.
+ cookiesQuery.exec();
+
+ // Create a cookie list.
+ QList<QNetworkCookie*> *cookieListPointer = new QList<QNetworkCookie*>;
+
+ // Populate the cookie list.
+ while (cookiesQuery.next())
+ {
+ // Create a cookie.
+ QNetworkCookie *cookiePointer = new QNetworkCookie();
+
+ // Populate the cookie.
+ cookiePointer->setDomain(cookiesQuery.value(DOMAIN).toString());
+ cookiePointer->setName(cookiesQuery.value(NAME).toString().toUtf8());
+ cookiePointer->setPath(cookiesQuery.value(PATH).toString());
+ cookiePointer->setExpirationDate(QDateTime::fromString(cookiesQuery.value(EXPIRATION_DATE).toString(), Qt::ISODate));
+ cookiePointer->setHttpOnly(cookiesQuery.value(HTTP_ONLY).toBool());
+ cookiePointer->setSecure(cookiesQuery.value(SECURE).toBool());
+ cookiePointer->setValue(cookiesQuery.value(VALUE).toString().toUtf8());
+
+ // Add the cookie to the list.
+ cookieListPointer->append(cookiePointer);
+ }
+
+ // Return the cookie list.
+ return cookieListPointer;
+}
+
+bool CookiesDatabase::isDurable(const QNetworkCookie &cookie)
+{
+ // Get a handle for the cookies database.
+ QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+ // Instantiate an is durable query.
+ QSqlQuery isDurableQuery(cookiesDatabase);
+
+ // Set the query to be forward only.
+ isDurableQuery.setForwardOnly(true);
+
+ // Prepare the is durable query.
+ isDurableQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
+
+ // Bind the values.
+ isDurableQuery.bindValue(":domain", cookie.domain());
+ isDurableQuery.bindValue(":name", QString(cookie.name()));
+ isDurableQuery.bindValue(":path", cookie.path());
+
+ // Execute the query.
+ isDurableQuery.exec();
+
+ // Move to the first entry.
+ isDurableQuery.first();
+
+ // Return the status of the cookie in the database.
+ return (isDurableQuery.isValid());
+}
+
+bool CookiesDatabase::isUpdate(const QNetworkCookie &cookie)
+{
+ // Get a handle for the cookies database.
+ QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+ // Instantiate an is update query.
+ QSqlQuery isUpdateQuery(cookiesDatabase);
+
+ // Prepare the is update query.
+ isUpdateQuery.prepare("SELECT " + EXPIRATION_DATE + " , " + HTTP_ONLY + " , " + SECURE + " , " + VALUE + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " +
+ NAME + " = :name AND " + PATH + " = :path");
+
+ // Bind the values.
+ isUpdateQuery.bindValue(":domain", cookie.domain());
+ isUpdateQuery.bindValue(":name", QString(cookie.name()));
+ isUpdateQuery.bindValue(":path", cookie.path());
+
+ // Execute the query.
+ isUpdateQuery.exec();
+
+ // Move to the first entry.
+ isUpdateQuery.first();
+
+ // Check to see if the cookie exists.
+ if (isUpdateQuery.isValid()) // The cookie exists in the database.
+ {
+ // Check to see if the cookie data has changed.
+ if ((QDateTime::fromString(isUpdateQuery.value(0).toString(), Qt::ISODate) != cookie.expirationDate()) ||
+ (isUpdateQuery.value(1).toBool() != cookie.isHttpOnly()) ||
+ (isUpdateQuery.value(2).toBool() != cookie.isSecure()) ||
+ (isUpdateQuery.value(3).toString().toUtf8() != cookie.value())) // The cookies data has changed.
+ {
+ qDebug() << "The durable cookie data has changed.";
+
+ // Return true.
+ return true;
+ }
+ else // The cookie data has not changed.
+ {
+ qDebug() << "The durable cookie data is unchanged.";
+
+ // Return false.
+ return false;
+ }
+ }
+ else // The cookie does not exist in the database.
+ {
+ // Return false.
+ return false;
+ }
+}
+
+void CookiesDatabase::updateCookie(const QNetworkCookie &cookie)
+{
+ // Get a handle for the cookies database.
+ QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+ // Create the update cookie query.
+ QSqlQuery updateCookieQuery(cookiesDatabase);
+
+ // Prepare the edit cookie query.
+ updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + EXPIRATION_DATE + " = :expiration_date , " + HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " +
+ VALUE + " = :value WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
+
+ // Bind the values.
+ updateCookieQuery.bindValue(":domain", cookie.domain());
+ updateCookieQuery.bindValue(":name", QString(cookie.name()));
+ updateCookieQuery.bindValue(":path", cookie.path());
+ updateCookieQuery.bindValue(":expiration_date", cookie.expirationDate());
+ updateCookieQuery.bindValue(":http_only", cookie.isHttpOnly());
+ updateCookieQuery.bindValue(":secure", cookie.isSecure());
+ updateCookieQuery.bindValue(":value", QString(cookie.value()));
+
+ // Execute the query.
+ updateCookieQuery.exec();
+}
+
+void CookiesDatabase::updateCookie(const QNetworkCookie &oldCookie, const QNetworkCookie &newCookie)
+{
+ // Get a handle for the cookies database.
+ QSqlDatabase cookiesDatabase = QSqlDatabase::database(CONNECTION_NAME);
+
+ // Create the old cookie query.
+ QSqlQuery oldCookieQuery(cookiesDatabase);
+
+ // Set the query to be forward only.
+ oldCookieQuery.setForwardOnly(true);
+
+ // Prepare the old cookie query.
+ oldCookieQuery.prepare("SELECT " + _ID + " FROM " + COOKIES_TABLE + " WHERE " + DOMAIN + " = :domain AND " + NAME + " = :name AND " + PATH + " = :path");
+
+ // Bind the values.
+ oldCookieQuery.bindValue(":domain", oldCookie.domain());
+ oldCookieQuery.bindValue(":name", QString(oldCookie.name()));
+ oldCookieQuery.bindValue(":path", oldCookie.path());
+
+ // Execute the query.
+ oldCookieQuery.exec();
+
+ // Move to the first entry.
+ oldCookieQuery.first();
+
+ // Create the update cookie query.
+ QSqlQuery updateCookieQuery(cookiesDatabase);
+
+ // Prepare the update cookie query.
+ updateCookieQuery.prepare("UPDATE " + COOKIES_TABLE + " SET " + DOMAIN + " = :domain , " + NAME + " = :name , " + PATH + " = :path , " + EXPIRATION_DATE + " = :expiration_date , " +
+ HTTP_ONLY + " = :http_only , " + SECURE + " = :secure , " + VALUE + " = :value WHERE " + _ID + " = :id");
+
+ // Bind the values.
+ updateCookieQuery.bindValue(":id", oldCookieQuery.value(0).toLongLong());
+ updateCookieQuery.bindValue(":domain", newCookie.domain());
+ updateCookieQuery.bindValue(":name", QString(newCookie.name()));
+ updateCookieQuery.bindValue(":path", newCookie.path());
+ updateCookieQuery.bindValue(":expiration_date", newCookie.expirationDate());
+ updateCookieQuery.bindValue(":http_only", newCookie.isHttpOnly());
+ updateCookieQuery.bindValue(":secure", newCookie.isSecure());
+ updateCookieQuery.bindValue(":value", QString(newCookie.value()));
+
+ // Execute the query.
+ updateCookieQuery.exec();
+}