]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blobdiff - src/dialogs/CookiesDialog.cpp
Block all CSP requests. https://redmine.stoutner.com/issues/1193
[PrivacyBrowserPC.git] / src / dialogs / CookiesDialog.cpp
index fa9679c457a4b4e58e906457cd367ca2ed577c30..db65776d2460d8c8cee59cb9c17f64e965a8cd1b 100644 (file)
@@ -1,25 +1,26 @@
-/*
- * Copyright © 2022 Soren Stoutner <soren@stoutner.com>.
+/* SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-FileCopyrightText: 2022-2024 Soren Stoutner <soren@stoutner.com>
  *
- * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
+ * 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.
+ * This program 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.
+ * This program 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/>.
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
 // Application headers.
 #include "AddOrEditCookieDialog.h"
 #include "CookiesDialog.h"
+#include "DurableCookiesDialog.h"
 #include "ui_CookiesDialog.h"
 #include "databases/CookiesDatabase.h"
 
@@ -32,7 +33,7 @@
 #include <QShortcut>
 #include <QUrl>
 
-// Construct the class.
+// Define the cookie sort predicate.
 bool cookieSortPredicate(const QNetworkCookie &leftHandCookie, const QNetworkCookie &rightHandCookie)
 {
     // Check to see if the domains are identical.
@@ -56,19 +57,35 @@ bool cookieSortPredicate(const QNetworkCookie &leftHandCookie, const QNetworkCoo
         QString leftHandDomain = leftHandCookie.domain();
         QString rightHandDomain = rightHandCookie.domain();
 
-        // Get the top level domains.
-        QString leftHandTopLevelDomain = leftHandDomain.section('.', -1);
-        QString rightHandTopLevelDomain = rightHandDomain.section('.', -1);
-
-        // Get the second level domains.
-        QString leftHandSecondLevelDomain = leftHandDomain.section('.', -2);
-        QString rightHandSecondLevelDomain = rightHandDomain.section('.', -2);
+        // Create the strings.
+        QString leftHandTopLevelDomain;
+        QString rightHandTopLevelDomain;
+        QString leftHandSecondLevelDomain;
+        QString rightHandSecondLevelDomain;
+        QString leftHandThirdLevelDomain;
+        QString rightHandThirdLevelDomain;
 
-        // Get the third level domains.
-        QString leftHandThirdLevelDomain = leftHandDomain.section('.', -3);
-        QString rightHandThirdLevelDomain = rightHandDomain.section('.', -3);
+        // Get the number of dots in the strings.
+        int leftHandDots = leftHandDomain.count(QLatin1Char('.'));
+        int rightHandDots = rightHandDomain.count(QLatin1Char('.'));
 
-        // Check to see if the top level domains are the same.
+        // Get the top level domains.
+        leftHandTopLevelDomain = leftHandDomain.section(QLatin1Char('.'), -1);
+        rightHandTopLevelDomain = rightHandDomain.section(QLatin1Char('.'), -1);
+
+        // Get the second level domains if they contain at least one dot.
+        if (leftHandDots >= 1)
+            leftHandSecondLevelDomain = leftHandDomain.section(QLatin1Char('.'), -2);
+        if (rightHandDots >= 1)
+            rightHandSecondLevelDomain = rightHandDomain.section(QLatin1Char('.'), -2);
+
+        // Get the third level domains if they contain at least two dots.
+        if (leftHandDots >= 2)
+            leftHandThirdLevelDomain = leftHandDomain.section(QLatin1Char('.'), -3);
+        if (rightHandDots >= 2)
+            rightHandThirdLevelDomain = rightHandDomain.section(QLatin1Char('.'), -3);
+
+        // Check to see if the top level domains are the same.  Segments, like third level domains, that don't exist will be blank, which will cause the sorting to group similar domains.
         if (leftHandTopLevelDomain == rightHandTopLevelDomain)
         {
             // Check to see if the second level domains are the same.
@@ -97,10 +114,10 @@ bool cookieSortPredicate(const QNetworkCookie &leftHandCookie, const QNetworkCoo
             // Sort the cookies by the top level domain.
             return (leftHandTopLevelDomain < rightHandTopLevelDomain);
         }
-
     }
 }
 
+// Construct the class.
 CookiesDialog::CookiesDialog(std::list<QNetworkCookie> *originalCookieListPointer) : QDialog(nullptr), cookieListPointer(originalCookieListPointer)
 {
     // Set the dialog window title.
@@ -137,7 +154,7 @@ CookiesDialog::CookiesDialog(std::list<QNetworkCookie> *originalCookieListPointe
     treeModelPointer->horizontalHeaderItem(0)->setToolTip(i18nc("The cookie Name tool tip.",
                                                                         "The name identifies the cookie.  Each cookie has a unique combination of domain, name, and path."));
     treeModelPointer->horizontalHeaderItem(1)->setToolTip(i18nc("The cookie Durable tool tip",
-                                                                        "Durable cookies pursist across restarts, irrespective of the expiration date. All other cookies are deleted when Privacy Browser closes, irrespective of the expiration date."));
+                                                                        "Durable cookies persist across restarts, irrespective of the expiration date. All other cookies are deleted when Privacy Browser closes, irrespective of the expiration date."));
     treeModelPointer->horizontalHeaderItem(2)->setToolTip(i18nc("The cookie Path tool tip.", "Websites can restrict cookie access to subpath of their URL."));
     treeModelPointer->horizontalHeaderItem(3)->setToolTip(i18nc("The cookie Expiration Date tool tip.",
                                                                         "Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes."));
@@ -150,7 +167,7 @@ CookiesDialog::CookiesDialog(std::list<QNetworkCookie> *originalCookieListPointe
     cookieListPointer->sort(cookieSortPredicate);
 
     // Create the current domain string.
-    QString currentDomainString = "";
+    QString currentDomainString = QLatin1String("");
 
     // Create the current domain item pointer.
     QStandardItem *currentDomainItemPointer;
@@ -184,13 +201,13 @@ CookiesDialog::CookiesDialog(std::list<QNetworkCookie> *originalCookieListPointe
         QList<QStandardItem*> cookieItemList;
 
         // Create the cookie items.
-        QStandardItem *nameItemPointer = new QStandardItem(QString(cookie.name()));
-        QStandardItem *durableItemPointer = new QStandardItem(QString(isDurable ? i18n("yes") : i18n("no")));
-        QStandardItem *pathItemPointer = new QStandardItem(QString(cookie.path()));
-        QStandardItem *expirationDateItemPointer = new QStandardItem(QString(cookie.expirationDate().toString()));
-        QStandardItem *isHttpOnlyItemPointer = new QStandardItem(QString(cookie.isHttpOnly() ? i18n("yes") : i18n("no")));
-        QStandardItem *isSecureItemPointer = new QStandardItem(QString(cookie.isSecure() ? i18n("yes") : i18n("no")));
-        QStandardItem *valueItemPointer = new QStandardItem(QString(cookie.value()));
+        QStandardItem *nameItemPointer = new QStandardItem(QLatin1String(cookie.name()));
+        QStandardItem *durableItemPointer = new QStandardItem(isDurable ? i18n("yes") : i18n("no"));
+        QStandardItem *pathItemPointer = new QStandardItem(cookie.path());
+        QStandardItem *expirationDateItemPointer = new QStandardItem(cookie.expirationDate().toString());
+        QStandardItem *isHttpOnlyItemPointer = new QStandardItem(cookie.isHttpOnly() ? i18n("yes") : i18n("no"));
+        QStandardItem *isSecureItemPointer = new QStandardItem(cookie.isSecure() ? i18n("yes") : i18n("no"));
+        QStandardItem *valueItemPointer = new QStandardItem(QLatin1String(cookie.value()));
 
         // Populate the cookie standard item list.
         cookieItemList.append(nameItemPointer);
@@ -214,10 +231,10 @@ CookiesDialog::CookiesDialog(std::list<QNetworkCookie> *originalCookieListPointe
     // Don't elide the Value field (or any other field).
     treeViewPointer->setTextElideMode(Qt::ElideNone);
 
-    // Indicate that all the rows are the same height, wich improves performance.
+    // Indicate that all the rows are the same height, which improves performance.
     treeViewPointer->setUniformRowHeights(true);
 
-    // Disable editing.
+    // Disable editing in the tree view.
     treeViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers);
 
     // Set the tree model.
@@ -233,28 +250,31 @@ CookiesDialog::CookiesDialog(std::list<QNetworkCookie> *originalCookieListPointe
     addCookieButtonPointer = cookiesDialogUi.addCookieButton;
     editCookieButtonPointer = cookiesDialogUi.editCookieButton;
     deleteCookieButtonPointer = cookiesDialogUi.deleteCookieButton;
+    deleteAllButtonPointer = cookiesDialogUi.deleteAllCookiesButton;
     QDialogButtonBox *dialogButtonBoxPointer = cookiesDialogUi.dialogButtonBox;
     QPushButton *closeButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Close);
 
-    // Add a delete all button to the dialog button box.
-    deleteAllButtonPointer = dialogButtonBoxPointer->addButton(i18nc("Delete all cookies button", "Delete all"), QDialogButtonBox::ActionRole);
+    // Add buttons to the dialog button box.
+    durableCookiesButtonPointer = dialogButtonBoxPointer->addButton(i18nc("View the durable cookies button", "Durable cookies - %1", CookiesDatabase::cookieCount()),
+                                                                    QDialogButtonBox::ActionRole);
 
-    // Set the delete all button icon.
-    deleteAllButtonPointer->setIcon(QIcon::fromTheme("delete"));
+    // Set the button icons.
+    durableCookiesButtonPointer->setIcon(QIcon::fromTheme(QLatin1String("view-visible"), QIcon::fromTheme(QLatin1String("appointment-new"))));
 
     // Connect the buttons.
     connect(addCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showAddCookieDialog()));
     connect(editCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showEditCookieDialog()));
     connect(deleteCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showDeleteCookieMessageBox()));
+    connect(durableCookiesButtonPointer, SIGNAL(clicked()), this, SLOT(showDurableCookiesDialog()));
     connect(deleteAllButtonPointer, SIGNAL(clicked()), this, SLOT(showDeleteAllMessageBox()));
     connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject()));
 
-    // Set the cancel button to be the default.
+    // Set the close button to be the default.
     closeButtonPointer->setDefault(true);
 
     // Create the keyboard shortcuts.
     QShortcut *aShortcutPointer = new QShortcut(QKeySequence(i18nc("The add cookie key shortcut.", "a")), this);
-    QShortcut *eShortcutPointer = new QShortcut(QKeySequence(i18nc("The edit cookie key shorcut.", "e")), this);
+    QShortcut *eShortcutPointer = new QShortcut(QKeySequence(i18nc("The edit cookie key shortcut.", "e")), this);
     QShortcut *dShortcutPointer = new QShortcut(QKeySequence(i18nc("The delete cookie key shortcut.", "d")), this);
     QShortcut *deleteShortcutPointer = new QShortcut(QKeySequence::Delete, this);
     QShortcut *lShortcutPointer = new QShortcut(QKeySequence(i18nc("The delete all key shortcut.", "l")), this);
@@ -280,7 +300,7 @@ CookiesDialog::CookiesDialog(std::list<QNetworkCookie> *originalCookieListPointe
 void CookiesDialog::addCookieFromDialog(const QNetworkCookie &cookie, const bool &isDurable) const
 {
     // Add the cookie to the cookie list and the cookie store.
-    emit addCookie(cookie);
+    Q_EMIT addCookie(cookie);
 
     // Get the new domain string.
     QString newDomain = cookie.domain();
@@ -297,47 +317,75 @@ void CookiesDialog::addCookieFromDialog(const QNetworkCookie &cookie, const bool
         // Create the domain name item.
         domainNameItemPointer = new QStandardItem(newDomain);
 
-        // Create the insert domain row number.
-        int insertDomainRowNumber = 0;
-
         // Get the number of domains in the tree.
         int numberOfDomains = treeModelPointer->invisibleRootItem()->rowCount();
 
-        // Get the new domain strings.
-        QString newDomainTopLevelDomain = newDomain.section('.', -1);
-        QString newDomainSecondLevelDomain = newDomain.section('.', -2);
-        QString newDomainThirdLevelDomain = newDomain.section('.', -3);
+        // Create the insert domain row number and initially set it to be at the end of the list.
+        int insertDomainRowNumber = numberOfDomains;
 
-        // Iterate through all the domains.
-        for (int i = 0; i < numberOfDomains; ++i)
-        {
-            // Get the current domain strings.
-            QString currentDomain = treeModelPointer->invisibleRootItem()->child(i, 0)->index().data().toString();
-            QString currentDomainTopLevelDomain = currentDomain.section('.', -1);
-            QString currentDomainSecondLevelDomain = currentDomain.section('.', -2);
-            QString currentDomainThirdLevelDomain = currentDomain.section('.', -3);
+        // Create the new domain strings.
+        QString newDomainTopLevelDomain;
+        QString newDomainSecondLevelDomain;
+        QString newDomainThirdLevelDomain;
+
+        // Get the number of dots in the new domain string.
+        int newDomainDots = newDomain.count(QLatin1Char('.'));
+
+        // Get the new top level domain.
+        newDomainTopLevelDomain = newDomain.section(QLatin1Char('.'), -1);
+
+        // Get the new second level domain if it contains at least one dot.
+        if (newDomainDots >= 1)
+            newDomainSecondLevelDomain = newDomain.section(QLatin1Char('.'), -2);
+
+        // Get the new third level domain if it contains at least two dots.
+        if (newDomainDots >= 2)
+            newDomainThirdLevelDomain = newDomain.section(QLatin1Char('.'), -3);
+
+        // Create while loop trackers.
+        bool locationFound = false;
+        int currentRow = 0;
+
+        // Check to see if the new domain should be inserted after an existing domain.
+        while (!locationFound && (currentRow < numberOfDomains)) {
+            // Get the current domain string.
+            QString currentDomain = treeModelPointer->invisibleRootItem()->child(currentRow, 0)->index().data().toString();
+
+            // Create the current domain strings.
+            QString currentDomainTopLevelDomain;
+            QString currentDomainSecondLevelDomain;
+            QString currentDomainThirdLevelDomain;
+
+            // Get the number of dots in the current domain string.
+            int currentDomainDots = currentDomain.count(QLatin1Char('.'));
+
+            // Get the current top level domain.
+            currentDomainTopLevelDomain = currentDomain.section(QLatin1Char('.'), -1);
+
+            // Get the current second level domain if it contains at least one dot.
+            if (currentDomainDots >= 1)
+                currentDomainSecondLevelDomain = currentDomain.section(QLatin1Char('.'), -2);
+
+            // Get the current third level domain if it contains at least two dots.
+            if (currentDomainDots >= 2)
+                currentDomainThirdLevelDomain = currentDomain.section(QLatin1Char('.'), -3);
 
             // Check to see if the new domain should be inserted after the current domain.
-            if (newDomainTopLevelDomain > currentDomainTopLevelDomain)
-            {
-                // Insert the new domain after the current domain.
-                insertDomainRowNumber = i + 1;
-            }
-            else if ((newDomainTopLevelDomain == currentDomainTopLevelDomain) && (newDomainSecondLevelDomain > currentDomainSecondLevelDomain))
-            {
-                // Insert the new domain after the current domain.
-                insertDomainRowNumber = i + 1;
-            }
-            else if ((newDomainSecondLevelDomain == currentDomainSecondLevelDomain) && (newDomainThirdLevelDomain > currentDomainThirdLevelDomain))
+            // Segments, like third level domains, that do not exist will be blank, which will cause the sorting to group similar domains.
+            if ((newDomainTopLevelDomain < currentDomainTopLevelDomain) ||  // The new top level domain `.com` is less than the current top level domain `.org`.
+                ((newDomainTopLevelDomain == currentDomainTopLevelDomain) && ((newDomainSecondLevelDomain < currentDomainSecondLevelDomain) ||  // `jessica.com` is less than `stoutner.com`.
+                ((newDomainSecondLevelDomain == currentDomainSecondLevelDomain) && ((newDomainThirdLevelDomain < currentDomainThirdLevelDomain) ||  // `apache.stoutner.com` < `www.stoutner.com`.
+                ((newDomainThirdLevelDomain == currentDomainThirdLevelDomain) && (newDomain < currentDomain)))))))  // `first.www.stoutner.com` < `second.www.stoutner.com`.
             {
-                // Insert the new domain after the current domain.
-                insertDomainRowNumber = i + 1;
-            }
-            else if ((newDomainThirdLevelDomain == currentDomainThirdLevelDomain) && (newDomain > currentDomain))
-            {
-                // Insert the new domain after the current domain.
-                insertDomainRowNumber = i + 1;
+                // Insert the domain at the current row (because the rows are 0 based, this will insert it before the first domain where all the above checks fail).
+                insertDomainRowNumber = currentRow;
+
+                // Mark the location as found.
+                locationFound = true;
             }
+
+            // Move to the next row.
+            ++currentRow;
         }
 
         // Add the domain to the tree.
@@ -350,7 +398,7 @@ void CookiesDialog::addCookieFromDialog(const QNetworkCookie &cookie, const bool
     }
 
     // Get strings for the new cookie name and path (used later in the placement of the row).
-    QString newCookieName = QString(cookie.name());
+    QString newCookieName = QLatin1String(cookie.name());
     QString newCookiePath = QString(cookie.path());
 
     // Create a cookie item list.
@@ -358,12 +406,12 @@ void CookiesDialog::addCookieFromDialog(const QNetworkCookie &cookie, const bool
 
     // Create the cookie items.
     QStandardItem *nameItemPointer = new QStandardItem(newCookieName);
-    QStandardItem *durableItemPointer = new QStandardItem(QString(isDurable ? i18n("yes") : i18n("no")));
+    QStandardItem *durableItemPointer = new QStandardItem(isDurable ? i18n("yes") : i18n("no"));
     QStandardItem *pathItemPointer = new QStandardItem(newCookiePath);
-    QStandardItem *expirationDateItemPointer = new QStandardItem(QString(cookie.expirationDate().toString()));
-    QStandardItem *isHttpOnlyItemPointer = new QStandardItem(QString(cookie.isHttpOnly() ? i18n("yes") : i18n("no")));
-    QStandardItem *isSecureItemPointer = new QStandardItem(QString(cookie.isSecure() ? i18n("yes") : i18n("no")));
-    QStandardItem *valueItemPointer = new QStandardItem(QString(cookie.value()));
+    QStandardItem *expirationDateItemPointer = new QStandardItem(cookie.expirationDate().toString());
+    QStandardItem *isHttpOnlyItemPointer = new QStandardItem(cookie.isHttpOnly() ? i18n("yes") : i18n("no"));
+    QStandardItem *isSecureItemPointer = new QStandardItem(cookie.isSecure() ? i18n("yes") : i18n("no"));
+    QStandardItem *valueItemPointer = new QStandardItem(QLatin1String(cookie.value()));
 
     // Populate the cookie item list.
     cookieItemList.append(nameItemPointer);
@@ -374,41 +422,41 @@ void CookiesDialog::addCookieFromDialog(const QNetworkCookie &cookie, const bool
     cookieItemList.append(isSecureItemPointer);
     cookieItemList.append(valueItemPointer);
 
-    // Create the insert cookie row number.
-    int insertCookieRowNumber = 0;
-
-    // Create the remove existing row tracker.
-    bool removeExistingRow = false;
-
     // Get the number of cookies in the domain.
     int numberOfCookies = domainNameItemPointer->rowCount();
 
-    // Iterate through the cookies for this domain.
-    for (int i = 0; i < numberOfCookies; ++i)
+    // Create the insert cookie row number and initially set it to be at the end of the list.
+    int insertCookieRowNumber = numberOfCookies;
+
+    // Create the trackers.
+    bool removeExistingRow = false;
+    bool rowFound = false;
+    int currentRow = 0;
+
+    while (!rowFound && currentRow < numberOfCookies)
     {
         // Get the current cookie name and path at the indicated row.
-        QString currentCookieName = domainNameItemPointer->child(i, 0)->index().data().toString();
-        QString currentCookiePath = domainNameItemPointer->child(i, 2)->index().data().toString();
+        QString currentCookieName = domainNameItemPointer->child(currentRow, 0)->index().data().toString();
+        QString currentCookiePath = domainNameItemPointer->child(currentRow, 2)->index().data().toString();
 
         // Check to see if the new cookie should be inserted after the current cookie.
-        if (newCookieName > currentCookieName)  // The new cookie name comes after the current cookie name.
-        {
-            // Insert the new cookie after the current cookie.
-            insertCookieRowNumber = i + 1;
-        }
-        else if ((newCookieName == currentCookieName) && (newCookiePath > currentCookiePath))  // The names are the same, but the new cookie path comes after the current cookie path.
+        if ((newCookieName < currentCookieName) ||  // The new cookie name comes before the current cookie name.
+            ((newCookieName == currentCookieName) && ((newCookiePath < currentCookiePath) ||  // The names are the same, but the new cookie path comes before the current cookie path.
+            (newCookiePath == currentCookiePath))))  // The core attributes of the cookies are the same.
         {
-            // Insert the new cookie after the current cookie.
-            insertCookieRowNumber = i + 1;
-        }
-        else if ((newCookieName == currentCookieName) && (newCookiePath == currentCookiePath))  // The cookies are the same.
-        {
-            // Remove the existing cookie in this row.
-            removeExistingRow = true;
+            // Remove the existing cookie if the core attributes are the same.
+            if ((newCookieName == currentCookieName) && (newCookiePath == currentCookiePath))
+                removeExistingRow = true;
 
-            // Insert the cookie in it's place.
-            insertCookieRowNumber = i;
+            // Insert the new cookie at this row.
+            insertCookieRowNumber = currentRow;
+
+            // Mark the row as found.
+            rowFound = true;
         }
+
+        // Move to the next row.
+        ++currentRow;
     }
 
     // Remove the existing row if it is being edited.
@@ -459,7 +507,7 @@ void CookiesDialog::deleteCookie(const QModelIndex &modelIndex, const bool &dele
         treeModelPointer->removeRow(modelIndex.row(), modelIndex.parent());
 
         // Delete the cookie from the cookie list and cookie store.
-        emit deleteCookie(cookieToDelete);
+        Q_EMIT deleteCookie(cookieToDelete);
 
         // Delete the cookie from the durable cookies database.
         if (isDurable)
@@ -484,6 +532,48 @@ void CookiesDialog::deleteDomain(const QModelIndex &modelIndex, const bool &dele
         treeModelPointer->removeRow(modelIndex.row(), parentIndex);
 }
 
+void CookiesDialog::deleteCookieFromDatabase(const QNetworkCookie &cookie) const
+{
+    // Get a list of the matching domains.  There should only be one item in this list
+    QList<QStandardItem *> domainList = treeModelPointer->findItems(cookie.domain());
+
+    // Find any matching cookies.
+    for (QStandardItem *domainItemPointer : domainList)
+    {
+        // Get the number of cookies in the domain.
+        int numberOfCookies = domainItemPointer->rowCount();
+
+        // Initialize the tracking variables.
+        bool cookieFound = false;
+        int currentRow = 0;
+
+        // Find the cookie in the tree model.
+        while (!cookieFound && (currentRow < numberOfCookies))
+        {
+            // Get the name item.
+            QStandardItem *nameItemPointer = domainItemPointer->child(currentRow);
+
+            // Get the name model index.
+            QModelIndex nameModelIndex = nameItemPointer->index();
+
+            // Check to see if the name and the path match.
+            if ((nameModelIndex.data().toString() == QLatin1String(cookie.name())) && (nameModelIndex.siblingAtColumn(2).data().toString() == cookie.path()))
+            {
+                // Set the current index.
+                treeSelectionModelPointer->setCurrentIndex(nameModelIndex, QItemSelectionModel::ClearAndSelect);
+
+                // Delete the cookie.
+                deleteCookieFromDialog(cookie);
+
+                // Mark the cookie as found.
+                cookieFound = true;
+            }
+
+            // Move to the next row.
+            ++currentRow;
+        }
+    }
+}
 
 void CookiesDialog::deleteCookieFromDialog(const QNetworkCookie &cookie) const
 {
@@ -501,13 +591,13 @@ void CookiesDialog::deleteCookieFromDialog(const QNetworkCookie &cookie) const
         treeModelPointer->removeRow(parentIndex.row(), parentIndex.parent());
 
     // Delete the cookie from the cookie list and cookie store.
-    emit deleteCookie(cookie);
+    Q_EMIT deleteCookie(cookie);
 }
 
-void CookiesDialog::showAddCookieDialog() const
+void CookiesDialog::showAddCookieDialog()
 {
     // Instantiate an add cookie dialog.
-    QDialog *addCookieDialogPointer = new AddOrEditCookieDialog(AddOrEditCookieDialog::AddCookie);
+    QDialog *addCookieDialogPointer = new AddOrEditCookieDialog(this, AddOrEditCookieDialog::AddCookie);
 
     // Show the dialog.
     addCookieDialogPointer->show();
@@ -552,7 +642,7 @@ void CookiesDialog::showDeleteAllMessageBox() const
         if (deleteDurableCookiesCheckBox.isChecked())  // Delete everything.
         {
             // Delete all the cookies.
-            emit deleteAllCookies();
+            Q_EMIT deleteAllCookies();
 
             // Clear the tree model.
             treeModelPointer->clear();
@@ -654,7 +744,21 @@ void CookiesDialog::showDeleteCookieMessageBox() const
     }
 }
 
-void CookiesDialog::showEditCookieDialog() const
+void CookiesDialog::showDurableCookiesDialog()
+{
+    // Instantiate a durable cookies dialog.
+    QDialog *durableCookiesDialogPointer = new DurableCookiesDialog(this);
+
+    // Show the dialog.
+    durableCookiesDialogPointer->show();
+
+    // Process cookie changes.
+    connect(durableCookiesDialogPointer, SIGNAL(addingCookie(QNetworkCookie, bool)), this, SLOT(addCookieFromDialog(QNetworkCookie, bool)));
+    connect(durableCookiesDialogPointer, SIGNAL(deletingCookie(QNetworkCookie)), this, SLOT(deleteCookieFromDatabase(QNetworkCookie)));
+    connect(durableCookiesDialogPointer, SIGNAL(updateParentUi()), this, SLOT(updateUi()));
+}
+
+void CookiesDialog::showEditCookieDialog()
 {
     // Get the current model index.
     QModelIndex currentIndex = treeSelectionModelPointer->currentIndex();
@@ -679,7 +783,7 @@ void CookiesDialog::showEditCookieDialog() const
     }
 
     // Instantiate an edit cookie dialog.
-    QDialog *editCookieDialogPointer = new AddOrEditCookieDialog(AddOrEditCookieDialog::EditCookie, &cookieToEdit, currentIndex.siblingAtColumn(1).data().toString() == i18n("yes"));
+    QDialog *editCookieDialogPointer = new AddOrEditCookieDialog(this, AddOrEditCookieDialog::EditCookie, &cookieToEdit, currentIndex.siblingAtColumn(1).data().toString() == i18n("yes"));
 
     // Show the dialog.
     editCookieDialogPointer->show();
@@ -718,4 +822,7 @@ void CookiesDialog::updateUi() const
         // Reset the button text.
         deleteCookieButtonPointer->setText(i18nc("Delete cookie button.", "&Delete cookie"));
     }
+
+    // Update the text of the durable cookies button.
+    durableCookiesButtonPointer->setText(i18nc("View the durable cookies button", "Durable cookies - %1", CookiesDatabase::cookieCount()));
 }