+/*
+ * 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 "DurableCookiesDialog.h"
+#include "ui_DurableCookiesDialog.h"
+#include "databases/CookiesDatabase.h"
+#include "delegates/ViewOnlyDelegate.h"
+
+// KDE Frameworks headers.
+#include <KLocalizedString>
+
+DurableCookiesDialog::DurableCookiesDialog() : QDialog(nullptr)
+{
+ // Set the dialog window title.
+ setWindowTitle(i18nc("The durable cookies dialog window title", "Durable Cookies"));
+
+ // Set the window modality.
+ setWindowModality(Qt::WindowModality::ApplicationModal);
+
+ // Instantiate the durable cookies dialog UI.
+ Ui::DurableCookiesDialog durableCookiesDialogUi;
+
+ // Setup the UI.
+ durableCookiesDialogUi.setupUi(this);
+
+ // Get a handle for the table view.
+ QTableView *tableViewPointer = durableCookiesDialogUi.tableView;
+
+ // Create the durable cookies table model.
+ durableCookiesTableModelPointer = new QSqlTableModel(nullptr, QSqlDatabase::database(CookiesDatabase::CONNECTION_NAME));
+
+ // Set the table.
+ durableCookiesTableModelPointer->setTable(CookiesDatabase::COOKIES_TABLE);
+
+ // Set the edit strategy.
+ durableCookiesTableModelPointer->setEditStrategy(QSqlTableModel::OnManualSubmit);
+
+ // Populate the model.
+ durableCookiesTableModelPointer->select();
+
+ // Set the model.
+ tableViewPointer->setModel(durableCookiesTableModelPointer);
+
+ // Instantiate the view only delegate.
+ ViewOnlyDelegate *viewOnlyDelegatePointer = new ViewOnlyDelegate();
+
+ // Disable editing the first column.
+ tableViewPointer->setItemDelegateForColumn(0, viewOnlyDelegatePointer);
+
+ // Optimize the width of the columns.
+ tableViewPointer->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+
+ // Get the seleciton model.
+ tableSelectionModelPointer = tableViewPointer->selectionModel();
+
+ // Update the UI when the selection changes.
+ connect(tableSelectionModelPointer, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateUi()));
+
+ // Update the UI when the table model data changes.
+ connect(durableCookiesTableModelPointer, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(updateUi()));
+
+ // Get handles for the buttons.
+ QPushButton *addCookieButtonPointer = durableCookiesDialogUi.addCookieButton;
+ deleteCookieButtonPointer = durableCookiesDialogUi.deleteCookieButton;
+ deleteAllCookiesButtonPointer = durableCookiesDialogUi.deleteAllCookiesButton;
+ QDialogButtonBox *dialogButtonBoxPointer = durableCookiesDialogUi.dialogButtonBox;
+ resetButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Reset);
+ applyButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Apply);
+ QPushButton *okButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Ok);
+
+ // Set the OK button to be the default.
+ okButtonPointer->setDefault(true);
+
+ // Connect the buttons.
+ connect(addCookieButtonPointer, SIGNAL(clicked()), this, SLOT(addCookie()));
+ connect(deleteCookieButtonPointer, SIGNAL(clicked()), this, SLOT(deleteCookie()));
+ connect(deleteAllCookiesButtonPointer, SIGNAL(clicked()), this, SLOT(deleteAllCookies()));
+ connect(resetButtonPointer, SIGNAL(clicked()), this, SLOT(reset()));
+ connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(ok()));
+ connect(applyButtonPointer, SIGNAL(clicked()), this, SLOT(apply()));
+ connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject()));
+
+ // Connect the table model signals.
+ connect(durableCookiesTableModelPointer, SIGNAL(beforeDelete(int)), this, SLOT(beforeDelete(int)));
+ connect(durableCookiesTableModelPointer, SIGNAL(beforeUpdate(int, QSqlRecord &)), this, SLOT(beforeUpdate(int, QSqlRecord &)));
+ connect(durableCookiesTableModelPointer, SIGNAL(beforeInsert(QSqlRecord &)), this, SLOT(newCookie(QSqlRecord &)));
+
+ // Update the UI.
+ updateUi();
+}
+
+void DurableCookiesDialog::addCookie() const
+{
+ // Add a new row to the bottom of the table.
+ durableCookiesTableModelPointer->insertRow(durableCookiesTableModelPointer->rowCount());
+
+ // Update the UI.
+ updateUi();
+}
+
+void DurableCookiesDialog::apply() const
+{
+ // Submit all pending changes.
+ durableCookiesTableModelPointer->submitAll();
+
+ // Update the UI.
+ updateUi();
+}
+
+void DurableCookiesDialog::beforeDelete(int row) const
+{
+ // Get the SQL record for the row to be deleted.
+ QSqlRecord sqlRecord = durableCookiesTableModelPointer->record(row);
+
+ // Create a network cookie.
+ QNetworkCookie cookie;
+
+ // Populate the network cookie.
+ cookie.setDomain(sqlRecord.value(CookiesDatabase::DOMAIN).toString());
+ cookie.setName(sqlRecord.value(CookiesDatabase::NAME).toByteArray());
+ cookie.setPath(sqlRecord.value(CookiesDatabase::PATH).toString());
+
+ // Delete the cookie.
+ emit deletingCookie(cookie);
+}
+
+void DurableCookiesDialog::beforeUpdate(int row, QSqlRecord &sqlRecord) const
+{
+ // Tell the compiler to ignore the unused row parameter.
+ (void) row;
+
+ // Delete the old cookie if the core attributes are changing.
+ if (sqlRecord.isGenerated(CookiesDatabase::DOMAIN) || sqlRecord.isGenerated(CookiesDatabase::NAME) || sqlRecord.isGenerated(CookiesDatabase::PATH))
+ {
+ // Get the ID of the cookie
+ int id = sqlRecord.value(CookiesDatabase::_ID).toInt();
+
+ // Get the cookie.
+ QNetworkCookie *cookiePointer = CookiesDatabase::getCookieById(id);
+
+ // Delete the cookie.
+ emit deletingCookie(*cookiePointer);
+ }
+
+ // Add the new cookie, which modifies any existing cookies with the same core attributes.
+ newCookie(sqlRecord);
+}
+
+void DurableCookiesDialog::deleteAllCookies() const
+{
+ // Mark all the cookies for deletion.
+ durableCookiesTableModelPointer->removeRows(0, durableCookiesTableModelPointer->rowCount());
+
+ // Update the UI.
+ updateUi();
+}
+
+void DurableCookiesDialog::deleteCookie() const
+{
+ // Delete the currently selected row.
+ durableCookiesTableModelPointer->removeRow(tableSelectionModelPointer->selectedRows()[0].row());
+
+ // Update the UI.
+ updateUi();
+}
+
+void DurableCookiesDialog::newCookie(QSqlRecord &sqlRecord) const
+{
+ // Create a network cookie.
+ QNetworkCookie cookie;
+
+ // Populate the network cookie from the SQL record.
+ cookie.setDomain(sqlRecord.value(CookiesDatabase::DOMAIN).toString());
+ cookie.setName(sqlRecord.value(CookiesDatabase::NAME).toByteArray());
+ cookie.setPath(sqlRecord.value(CookiesDatabase::PATH).toString());
+ cookie.setExpirationDate(QDateTime::fromString(sqlRecord.value(CookiesDatabase::EXPIRATION_DATE).toString(), Qt::ISODate));
+ cookie.setHttpOnly(sqlRecord.value(CookiesDatabase::HTTP_ONLY).toBool());
+ cookie.setSecure(sqlRecord.value(CookiesDatabase::SECURE).toBool());
+ cookie.setValue(sqlRecord.value(CookiesDatabase::VALUE).toByteArray());
+
+ // Update the cookie in the cookies dialog tree, cookies list, and cookie store.
+ emit addingCookie(cookie, false);
+}
+
+void DurableCookiesDialog::ok()
+{
+ // Submit all pending changes.
+ durableCookiesTableModelPointer->submitAll();
+
+ // Update the parent UI.
+ updateParentUi();
+
+ // Close the dialog.
+ accept();
+}
+
+void DurableCookiesDialog::reset() const
+{
+ // Cancel all pending changes.
+ durableCookiesTableModelPointer->revertAll();
+
+ // Update the UI.
+ updateUi();
+}
+
+void DurableCookiesDialog::updateUi() const
+{
+ // Update the delete button status.
+ deleteCookieButtonPointer->setEnabled(tableSelectionModelPointer->hasSelection());
+
+ // Update the delete all button status.
+ deleteAllCookiesButtonPointer->setEnabled(durableCookiesTableModelPointer->rowCount() > 0);
+
+ // Update the reset button status.
+ resetButtonPointer->setEnabled(durableCookiesTableModelPointer->isDirty());
+
+ // Update the apply button status.
+ applyButtonPointer->setEnabled(durableCookiesTableModelPointer->isDirty());
+
+ // Update the parent UI.
+ emit updateParentUi();
+}