]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/dialogs/CookiesDialog.cpp
Add a default folder icon to the edit folder dialog. https://redmine.stoutner.com...
[PrivacyBrowserPC.git] / src / dialogs / CookiesDialog.cpp
1 /*
2  * Copyright 2022-2024 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 // Application headers.
21 #include "AddOrEditCookieDialog.h"
22 #include "CookiesDialog.h"
23 #include "DurableCookiesDialog.h"
24 #include "ui_CookiesDialog.h"
25 #include "databases/CookiesDatabase.h"
26
27 // KDE Frameworks headers.
28 #include <KLocalizedString>
29
30 // Qt toolkit headers.
31 #include <QDateTime>
32 #include <QMessageBox>
33 #include <QShortcut>
34 #include <QUrl>
35
36 // Define the cookie sort predicate.
37 bool cookieSortPredicate(const QNetworkCookie &leftHandCookie, const QNetworkCookie &rightHandCookie)
38 {
39     // Check to see if the domains are identical.
40     if (leftHandCookie.domain() == rightHandCookie.domain())
41     {
42         // Check to see if the names are identical.
43         if (leftHandCookie.name() == rightHandCookie.name())
44         {
45             // Sort the cookies by the path.
46             return (leftHandCookie.path() < rightHandCookie.path());
47         }
48         else  // The name are not identical.
49         {
50             // Sort the cookies by the name.
51             return (leftHandCookie.name() < rightHandCookie.name());
52         }
53     }
54     else  // The domains are not identical.
55     {
56         // Get copies of the domains.
57         QString leftHandDomain = leftHandCookie.domain();
58         QString rightHandDomain = rightHandCookie.domain();
59
60         // Create the strings.
61         QString leftHandTopLevelDomain;
62         QString rightHandTopLevelDomain;
63         QString leftHandSecondLevelDomain;
64         QString rightHandSecondLevelDomain;
65         QString leftHandThirdLevelDomain;
66         QString rightHandThirdLevelDomain;
67
68         // Get the number of dots in the strings.
69         int leftHandDots = leftHandDomain.count(QLatin1Char('.'));
70         int rightHandDots = rightHandDomain.count(QLatin1Char('.'));
71
72         // Get the top level domains.
73         leftHandTopLevelDomain = leftHandDomain.section('.', -1);
74         rightHandTopLevelDomain = rightHandDomain.section('.', -1);
75
76         // Get the second level domains if they contain at least one dot.
77         if (leftHandDots >= 1)
78             leftHandSecondLevelDomain = leftHandDomain.section('.', -2);
79         if (rightHandDots >= 1)
80             rightHandSecondLevelDomain = rightHandDomain.section('.', -2);
81
82         // Get the third level domains if they contain at least two dots.
83         if (leftHandDots >= 2)
84             leftHandThirdLevelDomain = leftHandDomain.section('.', -3);
85         if (rightHandDots >= 2)
86             rightHandThirdLevelDomain = rightHandDomain.section('.', -3);
87
88         // 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.
89         if (leftHandTopLevelDomain == rightHandTopLevelDomain)
90         {
91             // Check to see if the second level domains are the same.
92             if (leftHandSecondLevelDomain == rightHandSecondLevelDomain)
93             {
94                 // Check to see if the third level domains are the same.
95                 if (leftHandThirdLevelDomain == rightHandThirdLevelDomain)
96                 {
97                     // Sort the cookies by the full domain because they share the same third level domain.
98                     return (leftHandDomain < rightHandDomain);
99                 }
100                 else  // The second level domains are the same, but the third level domains are different.
101                 {
102                     // Sort the cookies by the third level domains.
103                     return (leftHandThirdLevelDomain < rightHandThirdLevelDomain);
104                 }
105             }
106             else  // The top level domains are the same, but the second level domains are diferent.
107             {
108                 // Sort the cookies by the second level domain.
109                 return (leftHandSecondLevelDomain < rightHandSecondLevelDomain);
110             }
111         }
112         else  // The top level domains are different.
113         {
114             // Sort the cookies by the top level domain.
115             return (leftHandTopLevelDomain < rightHandTopLevelDomain);
116         }
117     }
118 }
119
120 // Construct the class.
121 CookiesDialog::CookiesDialog(std::list<QNetworkCookie> *originalCookieListPointer) : QDialog(nullptr), cookieListPointer(originalCookieListPointer)
122 {
123     // Set the dialog window title.
124     setWindowTitle(i18nc("The cookies dialog window title", "Cookies"));
125
126     // Set the window modality.
127     setWindowModality(Qt::WindowModality::ApplicationModal);
128
129     // Instantiate the cookie settings dialog UI.
130     Ui::CookiesDialog cookiesDialogUi;
131
132     // Setup the UI.
133     cookiesDialogUi.setupUi(this);
134
135     // Get a handle for the tree view.
136     treeViewPointer = cookiesDialogUi.treeView;
137
138     // Initialize the tree model.
139     treeModelPointer = new QStandardItemModel();
140
141     // Set the column count.
142     treeModelPointer->setColumnCount(7);
143
144     // Set the tree header data.
145     treeModelPointer->setHeaderData(0, Qt::Horizontal, i18nc("The cookie Name header.", "Name"));
146     treeModelPointer->setHeaderData(1, Qt::Horizontal, i18nc("The cookie Durable header.", "Durable"));
147     treeModelPointer->setHeaderData(2, Qt::Horizontal, i18nc("The cookie Path header.", "Path"));
148     treeModelPointer->setHeaderData(3, Qt::Horizontal, i18nc("The cookie Expiration Date header.", "Expiration Date"));
149     treeModelPointer->setHeaderData(4, Qt::Horizontal, i18nc("The cookie HTTP Only header.", "HTTP Only"));
150     treeModelPointer->setHeaderData(5, Qt::Horizontal, i18nc("The cookie Secure header.", "Secure"));
151     treeModelPointer->setHeaderData(6, Qt::Horizontal, i18nc("The cookie Value header.", "Value"));
152
153     // Set the tree header tool tips.
154     treeModelPointer->horizontalHeaderItem(0)->setToolTip(i18nc("The cookie Name tool tip.",
155                                                                         "The name identifies the cookie.  Each cookie has a unique combination of domain, name, and path."));
156     treeModelPointer->horizontalHeaderItem(1)->setToolTip(i18nc("The cookie Durable tool tip",
157                                                                         "Durable cookies persist across restarts, irrespective of the expiration date. All other cookies are deleted when Privacy Browser closes, irrespective of the expiration date."));
158     treeModelPointer->horizontalHeaderItem(2)->setToolTip(i18nc("The cookie Path tool tip.", "Websites can restrict cookie access to subpath of their URL."));
159     treeModelPointer->horizontalHeaderItem(3)->setToolTip(i18nc("The cookie Expiration Date tool tip.",
160                                                                         "Cookies without an expiration date are known as session cookies and are expected to be deleted every time the browser closes."));
161     treeModelPointer->horizontalHeaderItem(4)->setToolTip(i18nc("The cookie HTTP Only tool tip.",
162                                                                         "Restrict cookie access to HTTP (and HTTPS). This prevents JavaScript from accessing the cookie, which hardens it against cross-site scripting attacks."));
163     treeModelPointer->horizontalHeaderItem(5)->setToolTip(i18nc("The cookie Secure tool tip.", "Only allow the cookie to be transferred across HTTPS (as opposed to HTTP)."));
164     treeModelPointer->horizontalHeaderItem(6)->setToolTip(i18nc("The cookie Value tool tip.", "The value contains the cookie data."));
165
166     // Sort the cookie list.
167     cookieListPointer->sort(cookieSortPredicate);
168
169     // Create the current domain string.
170     QString currentDomainString = "";
171
172     // Create the current domain item pointer.
173     QStandardItem *currentDomainItemPointer;
174
175     // Populate the cookie tree view.
176     for (QNetworkCookie cookie : *cookieListPointer)
177     {
178         // Get the cookie domain.
179         QString cookieDomain = cookie.domain();
180
181         // Check to see if the cookie is a member of the current domain.
182         if (cookieDomain != currentDomainString)  // Create a new domain in the tree.
183         {
184             // Create the domain name item.
185             QStandardItem *domainNameItemPointer = new QStandardItem(cookieDomain);
186
187             // Add the domain to the tree.
188             treeModelPointer->invisibleRootItem()->appendRow(domainNameItemPointer);
189
190             // Update the current domain string.
191             currentDomainString = cookieDomain;
192
193             // Update the current domain item pointer.
194             currentDomainItemPointer = domainNameItemPointer;
195         }
196
197         // Check to see if the cookie is durable.
198         bool isDurable = CookiesDatabase::isDurable(cookie);
199
200         // Create a list for the cookie items.
201         QList<QStandardItem*> cookieItemList;
202
203         // Create the cookie items.
204         QStandardItem *nameItemPointer = new QStandardItem(QString(cookie.name()));
205         QStandardItem *durableItemPointer = new QStandardItem(isDurable ? i18n("yes") : i18n("no"));
206         QStandardItem *pathItemPointer = new QStandardItem(cookie.path());
207         QStandardItem *expirationDateItemPointer = new QStandardItem(cookie.expirationDate().toString());
208         QStandardItem *isHttpOnlyItemPointer = new QStandardItem(cookie.isHttpOnly() ? i18n("yes") : i18n("no"));
209         QStandardItem *isSecureItemPointer = new QStandardItem(cookie.isSecure() ? i18n("yes") : i18n("no"));
210         QStandardItem *valueItemPointer = new QStandardItem(QString(cookie.value()));
211
212         // Populate the cookie standard item list.
213         cookieItemList.append(nameItemPointer);
214         cookieItemList.append(durableItemPointer);
215         cookieItemList.append(pathItemPointer);
216         cookieItemList.append(expirationDateItemPointer);
217         cookieItemList.append(isHttpOnlyItemPointer);
218         cookieItemList.append(isSecureItemPointer);
219         cookieItemList.append(valueItemPointer);
220
221         // Add the cookie to the tree.
222         currentDomainItemPointer->appendRow(cookieItemList);
223     }
224
225     // Auto resize the headers.
226     treeViewPointer->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
227
228     // Disable stretching the last section.  Otherwise, the Value field will be truncated to the width of the window when a row is expanded.
229     treeViewPointer->header()->setStretchLastSection(false);
230
231     // Don't elide the Value field (or any other field).
232     treeViewPointer->setTextElideMode(Qt::ElideNone);
233
234     // Indicate that all the rows are the same height, which improves performance.
235     treeViewPointer->setUniformRowHeights(true);
236
237     // Disable editing in the tree view.
238     treeViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers);
239
240     // Set the tree model.
241     treeViewPointer->setModel(treeModelPointer);
242
243     // Get a handle for the tree selection model.
244     treeSelectionModelPointer = treeViewPointer->selectionModel();
245
246     // Listen for selection changes.
247     connect(treeSelectionModelPointer, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateUi()));
248
249     // Get handles for the buttons.
250     addCookieButtonPointer = cookiesDialogUi.addCookieButton;
251     editCookieButtonPointer = cookiesDialogUi.editCookieButton;
252     deleteCookieButtonPointer = cookiesDialogUi.deleteCookieButton;
253     deleteAllButtonPointer = cookiesDialogUi.deleteAllCookiesButton;
254     QDialogButtonBox *dialogButtonBoxPointer = cookiesDialogUi.dialogButtonBox;
255     QPushButton *closeButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Close);
256
257     // Add buttons to the dialog button box.
258     durableCookiesButtonPointer = dialogButtonBoxPointer->addButton(i18nc("View the durable cookies button", "Durable cookies - %1", CookiesDatabase::cookieCount()),
259                                                                     QDialogButtonBox::ActionRole);
260
261     // Set the button icons.
262     durableCookiesButtonPointer->setIcon(QIcon::fromTheme("view-visible", QIcon::fromTheme(QLatin1String("appointment-new"))));
263
264     // Connect the buttons.
265     connect(addCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showAddCookieDialog()));
266     connect(editCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showEditCookieDialog()));
267     connect(deleteCookieButtonPointer, SIGNAL(clicked()), this, SLOT(showDeleteCookieMessageBox()));
268     connect(durableCookiesButtonPointer, SIGNAL(clicked()), this, SLOT(showDurableCookiesDialog()));
269     connect(deleteAllButtonPointer, SIGNAL(clicked()), this, SLOT(showDeleteAllMessageBox()));
270     connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject()));
271
272     // Set the close button to be the default.
273     closeButtonPointer->setDefault(true);
274
275     // Create the keyboard shortcuts.
276     QShortcut *aShortcutPointer = new QShortcut(QKeySequence(i18nc("The add cookie key shortcut.", "a")), this);
277     QShortcut *eShortcutPointer = new QShortcut(QKeySequence(i18nc("The edit cookie key shortcut.", "e")), this);
278     QShortcut *dShortcutPointer = new QShortcut(QKeySequence(i18nc("The delete cookie key shortcut.", "d")), this);
279     QShortcut *deleteShortcutPointer = new QShortcut(QKeySequence::Delete, this);
280     QShortcut *lShortcutPointer = new QShortcut(QKeySequence(i18nc("The delete all key shortcut.", "l")), this);
281     QShortcut *cShortcutPointer = new QShortcut(QKeySequence(i18nc("The close key shortcut.", "c")), this);
282     QShortcut *quitShortcutPointer = new QShortcut(QKeySequence::Quit, this);
283
284     // Connect the keyboard shortcuts to the buttons.
285     connect(aShortcutPointer, SIGNAL(activated()), addCookieButtonPointer, SLOT(click()));
286     connect(eShortcutPointer, SIGNAL(activated()), editCookieButtonPointer, SLOT(click()));
287     connect(dShortcutPointer, SIGNAL(activated()), deleteCookieButtonPointer, SLOT(click()));
288     connect(deleteShortcutPointer, SIGNAL(activated()), deleteCookieButtonPointer, SLOT(click()));
289     connect(lShortcutPointer, SIGNAL(activated()), deleteAllButtonPointer, SLOT(click()));
290     connect(cShortcutPointer, SIGNAL(activated()), closeButtonPointer, SLOT(click()));
291     connect(quitShortcutPointer, SIGNAL(activated()), closeButtonPointer, SLOT(click()));
292
293     // Edit a cookie when it is double clicked.
294     connect(treeViewPointer, SIGNAL(doubleClicked(QModelIndex)), editCookieButtonPointer, SLOT(click()));
295
296     // Update the UI.
297     updateUi();
298 };
299
300 void CookiesDialog::addCookieFromDialog(const QNetworkCookie &cookie, const bool &isDurable) const
301 {
302     // Add the cookie to the cookie list and the cookie store.
303     emit addCookie(cookie);
304
305     // Get the new domain string.
306     QString newDomain = cookie.domain();
307
308     // Check to see if the domain already exists in the model.
309     QList<QStandardItem*> currentDomainItemList = treeModelPointer->findItems(newDomain);
310
311     // Create a domain item pointer.
312     QStandardItem *domainNameItemPointer;
313
314     // Prepare the domain item pointer.
315     if (currentDomainItemList.isEmpty())  // The domain doesn't currently exist in the tree.
316     {
317         // Create the domain name item.
318         domainNameItemPointer = new QStandardItem(newDomain);
319
320         // Get the number of domains in the tree.
321         int numberOfDomains = treeModelPointer->invisibleRootItem()->rowCount();
322
323         // Create the insert domain row number and initially set it to be at the end of the list.
324         int insertDomainRowNumber = numberOfDomains;
325
326         // Create the new domain strings.
327         QString newDomainTopLevelDomain;
328         QString newDomainSecondLevelDomain;
329         QString newDomainThirdLevelDomain;
330
331         // Get the number of dots in the new domain string.
332         int newDomainDots = newDomain.count(QLatin1Char('.'));
333
334         // Get the new top level domain.
335         newDomainTopLevelDomain = newDomain.section('.', -1);
336
337         // Get the new second level domain if it contains at least one dot.
338         if (newDomainDots >= 1)
339             newDomainSecondLevelDomain = newDomain.section('.', -2);
340
341         // Get the new third level domain if it contains at least two dots.
342         if (newDomainDots >= 2)
343             newDomainThirdLevelDomain = newDomain.section('.', -3);
344
345         // Create while loop trackers.
346         bool locationFound = false;
347         int currentRow = 0;
348
349         // Check to see if the new domain should be inserted after an existing domain.
350         while (!locationFound && (currentRow < numberOfDomains)) {
351             // Get the current domain string.
352             QString currentDomain = treeModelPointer->invisibleRootItem()->child(currentRow, 0)->index().data().toString();
353
354             // Create the current domain strings.
355             QString currentDomainTopLevelDomain;
356             QString currentDomainSecondLevelDomain;
357             QString currentDomainThirdLevelDomain;
358
359             // Get the number of dots in the current domain string.
360             int currentDomainDots = currentDomain.count(QLatin1Char('.'));
361
362             // Get the current top level domain.
363             currentDomainTopLevelDomain = currentDomain.section('.', -1);
364
365             // Get the current second level domain if it contains at least one dot.
366             if (currentDomainDots >= 1)
367                 currentDomainSecondLevelDomain = currentDomain.section('.', -2);
368
369             // Get the current third level domain if it contains at least two dots.
370             if (currentDomainDots >= 2)
371                 currentDomainThirdLevelDomain = currentDomain.section('.', -3);
372
373             // Check to see if the new domain should be inserted after the current domain.
374             // Segments, like third level domains, that do not exist will be blank, which will cause the sorting to group similar domains.
375             if ((newDomainTopLevelDomain < currentDomainTopLevelDomain) ||  // The new top level domain `.com` is less than the current top level domain `.org`.
376                 ((newDomainTopLevelDomain == currentDomainTopLevelDomain) && ((newDomainSecondLevelDomain < currentDomainSecondLevelDomain) ||  // `jessica.com` is less than `stoutner.com`.
377                 ((newDomainSecondLevelDomain == currentDomainSecondLevelDomain) && ((newDomainThirdLevelDomain < currentDomainThirdLevelDomain) ||  // `apache.stoutner.com` < `www.stoutner.com`.
378                 ((newDomainThirdLevelDomain == currentDomainThirdLevelDomain) && (newDomain < currentDomain)))))))  // `first.www.stoutner.com` < `second.www.stoutner.com`.
379             {
380                 // 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).
381                 insertDomainRowNumber = currentRow;
382
383                 // Mark the location as found.
384                 locationFound = true;
385             }
386
387             // Move to the next row.
388             ++currentRow;
389         }
390
391         // Add the domain to the tree.
392         treeModelPointer->invisibleRootItem()->insertRow(insertDomainRowNumber, domainNameItemPointer);
393     }
394     else  // The domain already exists in the tree.
395     {
396         // Use the current domain standard item.
397         domainNameItemPointer = currentDomainItemList[0];
398     }
399
400     // Get strings for the new cookie name and path (used later in the placement of the row).
401     QString newCookieName = QString(cookie.name());
402     QString newCookiePath = QString(cookie.path());
403
404     // Create a cookie item list.
405     QList<QStandardItem*> cookieItemList;
406
407     // Create the cookie items.
408     QStandardItem *nameItemPointer = new QStandardItem(newCookieName);
409     QStandardItem *durableItemPointer = new QStandardItem(QString(isDurable ? i18n("yes") : i18n("no")));
410     QStandardItem *pathItemPointer = new QStandardItem(newCookiePath);
411     QStandardItem *expirationDateItemPointer = new QStandardItem(QString(cookie.expirationDate().toString()));
412     QStandardItem *isHttpOnlyItemPointer = new QStandardItem(QString(cookie.isHttpOnly() ? i18n("yes") : i18n("no")));
413     QStandardItem *isSecureItemPointer = new QStandardItem(QString(cookie.isSecure() ? i18n("yes") : i18n("no")));
414     QStandardItem *valueItemPointer = new QStandardItem(QString(cookie.value()));
415
416     // Populate the cookie item list.
417     cookieItemList.append(nameItemPointer);
418     cookieItemList.append(durableItemPointer);
419     cookieItemList.append(pathItemPointer);
420     cookieItemList.append(expirationDateItemPointer);
421     cookieItemList.append(isHttpOnlyItemPointer);
422     cookieItemList.append(isSecureItemPointer);
423     cookieItemList.append(valueItemPointer);
424
425     // Get the number of cookies in the domain.
426     int numberOfCookies = domainNameItemPointer->rowCount();
427
428     // Create the insert cookie row number and initially set it to be at the end of the list.
429     int insertCookieRowNumber = numberOfCookies;
430
431     // Create the trackers.
432     bool removeExistingRow = false;
433     bool rowFound = false;
434     int currentRow = 0;
435
436     while (!rowFound && currentRow < numberOfCookies)
437     {
438         // Get the current cookie name and path at the indicated row.
439         QString currentCookieName = domainNameItemPointer->child(currentRow, 0)->index().data().toString();
440         QString currentCookiePath = domainNameItemPointer->child(currentRow, 2)->index().data().toString();
441
442         // Check to see if the new cookie should be inserted after the current cookie.
443         if ((newCookieName < currentCookieName) ||  // The new cookie name comes before the current cookie name.
444             ((newCookieName == currentCookieName) && ((newCookiePath < currentCookiePath) ||  // The names are the same, but the new cookie path comes before the current cookie path.
445             (newCookiePath == currentCookiePath))))  // The core attributes of the cookies are the same.
446         {
447             // Remove the existing cookie if the core attributes are the same.
448             if ((newCookieName == currentCookieName) && (newCookiePath == currentCookiePath))
449                 removeExistingRow = true;
450
451             // Insert the new cookie at this row.
452             insertCookieRowNumber = currentRow;
453
454             // Mark the row as found.
455             rowFound = true;
456         }
457
458         // Move to the next row.
459         ++currentRow;
460     }
461
462     // Remove the existing row if it is being edited.
463     if (removeExistingRow)
464         domainNameItemPointer->removeRow(insertCookieRowNumber);
465
466     // Add the cookie to the tree model.
467     domainNameItemPointer->insertRow(insertCookieRowNumber, cookieItemList);
468
469     // Get the new cookie model index.
470     QModelIndex newCookieIndex = nameItemPointer->index();
471
472     // Set the new cookie to be the current index.
473     treeViewPointer->setCurrentIndex(newCookieIndex);
474
475     // Expand the parent of the new cookie.
476     treeViewPointer->expand(newCookieIndex.parent());
477 }
478
479 void CookiesDialog::deleteCookie(const QModelIndex &modelIndex, const bool &deleteDurableCookies) const
480 {
481     // Create a partial cookie.
482     QNetworkCookie partialCookie;
483
484     // Populate the partial cookie from the current model index.
485     partialCookie.setDomain(modelIndex.parent().siblingAtColumn(0).data().toString());
486     partialCookie.setName(modelIndex.siblingAtColumn(0).data().toString().toUtf8());
487     partialCookie.setPath(modelIndex.siblingAtColumn(2).data().toString());
488
489     // Create a cookie to delete.
490     QNetworkCookie cookieToDelete;
491
492     // Check if the cookie is durable.
493     bool isDurable = CookiesDatabase::isDurable(partialCookie);
494
495     // Only delete durable cookies if directed.
496     if (deleteDurableCookies || !isDurable)
497     {
498         // Search for the partial cookie in the cookie list.
499         for (QNetworkCookie cookie : *cookieListPointer)
500         {
501             // Store the cookie to delete if it has the same identifier as the partial cookie.
502             if (cookie.hasSameIdentifier(partialCookie))
503                 cookieToDelete = cookie;
504         }
505
506         // Remove the cookie from the tree model.
507         treeModelPointer->removeRow(modelIndex.row(), modelIndex.parent());
508
509         // Delete the cookie from the cookie list and cookie store.
510         emit deleteCookie(cookieToDelete);
511
512         // Delete the cookie from the durable cookies database.
513         if (isDurable)
514             CookiesDatabase::deleteCookie(cookieToDelete);
515     }
516 }
517
518 void CookiesDialog::deleteDomain(const QModelIndex &modelIndex, const bool &deleteDurableCookies) const
519 {
520     // Get the parent index.
521     QModelIndex parentIndex = modelIndex.parent();
522
523     // Get the number of cookies in the domain.
524     int numberOfCookies = treeModelPointer->rowCount(modelIndex);
525
526     // Delete each child cookie, starting from the bottom.
527     for (int i = numberOfCookies; i > 0; --i)
528         deleteCookie(treeModelPointer->index(i-1, 0, modelIndex), deleteDurableCookies);
529
530     // Remove the domain if all the cookies have been deleted.
531     if (treeModelPointer->rowCount(modelIndex) == 0)
532         treeModelPointer->removeRow(modelIndex.row(), parentIndex);
533 }
534
535 void CookiesDialog::deleteCookieFromDatabase(const QNetworkCookie &cookie) const
536 {
537     // Get a list of the matching domains.  There should only be one item in this list
538     QList<QStandardItem *> domainList = treeModelPointer->findItems(cookie.domain());
539
540     // Find any matching cookies.
541     for (QStandardItem *domainItemPointer : domainList)
542     {
543         // Get the number of cookies in the domain.
544         int numberOfCookies = domainItemPointer->rowCount();
545
546         // Initialize the tracking variables.
547         bool cookieFound = false;
548         int currentRow = 0;
549
550         // Find the cookie in the tree model.
551         while (!cookieFound && (currentRow < numberOfCookies))
552         {
553             // Get the name item.
554             QStandardItem *nameItemPointer = domainItemPointer->child(currentRow);
555
556             // Get the name model index.
557             QModelIndex nameModelIndex = nameItemPointer->index();
558
559             // Check to see if the name and the path match.
560             if ((nameModelIndex.data().toString() == cookie.name()) && (nameModelIndex.siblingAtColumn(2).data().toString() == cookie.path()))
561             {
562                 // Set the current index.
563                 treeSelectionModelPointer->setCurrentIndex(nameModelIndex, QItemSelectionModel::ClearAndSelect);
564
565                 // Delete the cookie.
566                 deleteCookieFromDialog(cookie);
567
568                 // Mark the cookie as found.
569                 cookieFound = true;
570             }
571
572             // Move to the next row.
573             ++currentRow;
574         }
575     }
576 }
577
578 void CookiesDialog::deleteCookieFromDialog(const QNetworkCookie &cookie) const
579 {
580     // Get the current model index.
581     QModelIndex currentIndex = treeSelectionModelPointer->currentIndex();
582
583     // Get the parent index.
584     QModelIndex parentIndex = currentIndex.parent();
585
586     // Remove the cookie from the tree model.
587     treeModelPointer->removeRow(currentIndex.row(), parentIndex);
588
589     // Remove the domain from the tree model if its only cookie has been deleted.
590     if (treeModelPointer->rowCount(parentIndex) == 0)
591         treeModelPointer->removeRow(parentIndex.row(), parentIndex.parent());
592
593     // Delete the cookie from the cookie list and cookie store.
594     emit deleteCookie(cookie);
595 }
596
597 void CookiesDialog::showAddCookieDialog()
598 {
599     // Instantiate an add cookie dialog.
600     QDialog *addCookieDialogPointer = new AddOrEditCookieDialog(this, AddOrEditCookieDialog::AddCookie);
601
602     // Show the dialog.
603     addCookieDialogPointer->show();
604
605     // Add the cookie if directed.
606     connect(addCookieDialogPointer, SIGNAL(addCookie(QNetworkCookie, bool)), this, SLOT(addCookieFromDialog(QNetworkCookie, bool)));
607 }
608
609 void CookiesDialog::showDeleteAllMessageBox() const
610 {
611     // Instantiate a delete all message box.
612     QMessageBox deleteAllCookiesMessageBox;
613
614     // Set the icon.
615     deleteAllCookiesMessageBox.setIcon(QMessageBox::Warning);
616
617     // Set the window title.
618     deleteAllCookiesMessageBox.setWindowTitle(i18nc("Delete all cookies dialog title", "Delete All Cookies"));
619
620     // Set the text.
621     deleteAllCookiesMessageBox.setText(i18nc("Delete all cookies dialog text", "Delete all cookies?"));
622
623     // Create a delete durable cookies check box.
624     QCheckBox deleteDurableCookiesCheckBox(i18nc("Delete durable cookies check box", "Delete even if durable"));
625
626     // Add the check box to the dialog.
627     deleteAllCookiesMessageBox.setCheckBox(&deleteDurableCookiesCheckBox);
628
629     // Set the standard buttons.
630     deleteAllCookiesMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
631
632     // Set the default button.
633     deleteAllCookiesMessageBox.setDefaultButton(QMessageBox::No);
634
635     // Display the dialog and capture the return value.
636     int returnValue = deleteAllCookiesMessageBox.exec();
637
638     // Delete all cookies if instructed.
639     if (returnValue == QMessageBox::Yes)
640     {
641         // Only delete durable cookies if requested.
642         if (deleteDurableCookiesCheckBox.isChecked())  // Delete everything.
643         {
644             // Delete all the cookies.
645             emit deleteAllCookies();
646
647             // Clear the tree model.
648             treeModelPointer->clear();
649
650             // Delete the durable cookies from the database.
651             CookiesDatabase::deleteAllCookies();
652
653             // Update the UI.
654             updateUi();
655         }
656         else  // Only delete cookies that are not durable.
657         {
658             // Get the root model index.
659             QModelIndex rootIndex = treeModelPointer->invisibleRootItem()->index();
660
661             // Get the number of domains.
662             int numberOfDomains = treeModelPointer->rowCount(rootIndex);
663
664             // Delete each domain, starting from the bottom.
665             for (int i = numberOfDomains; i > 0; --i)
666                 deleteDomain(treeModelPointer->index(i - 1, 0, rootIndex), deleteDurableCookiesCheckBox.isChecked());
667         }
668     }
669 }
670
671 void CookiesDialog::showDeleteCookieMessageBox() const
672 {
673     // Get the current model index.
674     QModelIndex currentIndex = treeSelectionModelPointer->currentIndex();
675
676     // Determine if a domain is selected.
677     bool isDomain = treeModelPointer->hasChildren(currentIndex);
678
679     // Instantiate a delete cookie message box.
680     QMessageBox deleteCookieMessageBox;
681
682     // Set the icon.
683     deleteCookieMessageBox.setIcon(QMessageBox::Warning);
684
685     // Create a delete durable cookies check box.
686     QCheckBox deleteDurableCookiesCheckBox(i18nc("Delete durable cookies check box", "Delete even if durable"));
687
688     if (isDomain)  // A domain is selected.
689     {
690         // Get the number of cookies.
691         int numberOfCookiesToDelete = treeModelPointer->rowCount(currentIndex);
692
693         // Set the window title.
694         deleteCookieMessageBox.setWindowTitle(i18ncp("Delete cookies dialog title", "Delete %1 Cookie", "Delete 1% Cookies", numberOfCookiesToDelete));
695
696         // Set the text.
697         deleteCookieMessageBox.setText(i18ncp("Delete cookies dialog text", "Delete %1 cookie?", "Delete %1 cookies?", numberOfCookiesToDelete));
698     }
699     else  // A single cookie is selected.
700     {
701         // Set the window title.
702         deleteCookieMessageBox.setWindowTitle(i18nc("Delete cookie dialog title", "Delete 1 Cookie"));
703
704         // Set the text.
705         deleteCookieMessageBox.setText(i18nc("Delete cookie dialog text", "Delete 1 cookie?"));
706
707         // Check the box.
708         deleteDurableCookiesCheckBox.setChecked(true);
709     }
710
711     // Add the check box to the dialog.
712     deleteCookieMessageBox.setCheckBox(&deleteDurableCookiesCheckBox);
713
714     // Set the standard buttons.
715     deleteCookieMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
716
717     // Set the default button.
718     deleteCookieMessageBox.setDefaultButton(QMessageBox::No);
719
720     // Display the dialog and capture the return value.
721     int returnValue = deleteCookieMessageBox.exec();
722
723     // Delete the cookie if instructed.
724     if (returnValue == QMessageBox::Yes)
725     {
726         // Delete the cookies according to the selection.
727         if (isDomain)  // A domain is selected.
728         {
729             // Delete the domain.
730             deleteDomain(currentIndex, deleteDurableCookiesCheckBox.isChecked());
731         }
732         else  // A single cookie is selected.
733         {
734             // Get the parent model index.
735             QModelIndex parentIndex = currentIndex.parent();
736
737             // Delete the cookie.
738             deleteCookie(currentIndex, deleteDurableCookiesCheckBox.isChecked());
739
740             // Remove the domain row if its only cookie has been deleted.
741             if (treeModelPointer->rowCount(parentIndex) == 0)
742                 treeModelPointer->removeRow(parentIndex.row(), parentIndex.parent());
743         }
744     }
745 }
746
747 void CookiesDialog::showDurableCookiesDialog()
748 {
749     // Instantiate a durable cookies dialog.
750     QDialog *durableCookiesDialogPointer = new DurableCookiesDialog(this);
751
752     // Show the dialog.
753     durableCookiesDialogPointer->show();
754
755     // Process cookie changes.
756     connect(durableCookiesDialogPointer, SIGNAL(addingCookie(QNetworkCookie, bool)), this, SLOT(addCookieFromDialog(QNetworkCookie, bool)));
757     connect(durableCookiesDialogPointer, SIGNAL(deletingCookie(QNetworkCookie)), this, SLOT(deleteCookieFromDatabase(QNetworkCookie)));
758     connect(durableCookiesDialogPointer, SIGNAL(updateParentUi()), this, SLOT(updateUi()));
759 }
760
761 void CookiesDialog::showEditCookieDialog()
762 {
763     // Get the current model index.
764     QModelIndex currentIndex = treeSelectionModelPointer->currentIndex();
765
766     // Create a partial cookie.
767     QNetworkCookie partialCookie;
768
769     // Populate the partial cookie from the current model index.
770     partialCookie.setDomain(currentIndex.parent().siblingAtColumn(0).data().toString());
771     partialCookie.setName(currentIndex.siblingAtColumn(0).data().toString().toUtf8());
772     partialCookie.setPath(currentIndex.siblingAtColumn(2).data().toString());
773
774     // Create a cookie to edit.
775     QNetworkCookie cookieToEdit;
776
777     // Search for the partial cookie in the cookie list.
778     for (QNetworkCookie cookie : *cookieListPointer)
779     {
780         // Store the cookie to edit if it has the same identifier as the partial cookie.
781         if (cookie.hasSameIdentifier(partialCookie))
782             cookieToEdit = cookie;
783     }
784
785     // Instantiate an edit cookie dialog.
786     QDialog *editCookieDialogPointer = new AddOrEditCookieDialog(this, AddOrEditCookieDialog::EditCookie, &cookieToEdit, currentIndex.siblingAtColumn(1).data().toString() == i18n("yes"));
787
788     // Show the dialog.
789     editCookieDialogPointer->show();
790
791     // Process cookie events.
792     connect(editCookieDialogPointer, SIGNAL(addCookie(QNetworkCookie, bool)), this, SLOT(addCookieFromDialog(QNetworkCookie, bool)));
793     connect(editCookieDialogPointer, SIGNAL(deleteCookie(QNetworkCookie)), this, SLOT(deleteCookieFromDialog(QNetworkCookie)));
794 }
795
796 void CookiesDialog::updateUi() const
797 {
798     // Get the current index of the first column.
799     QModelIndex currentIndex = treeSelectionModelPointer->currentIndex().siblingAtColumn(0);
800
801     // Set the status of the buttons.
802     editCookieButtonPointer->setEnabled(treeSelectionModelPointer->hasSelection() && !treeModelPointer->hasChildren(currentIndex));
803     deleteCookieButtonPointer->setEnabled(treeSelectionModelPointer->hasSelection());;
804     deleteAllButtonPointer->setEnabled(treeModelPointer->hasChildren(treeModelPointer->invisibleRootItem()->index()));
805
806     // Update the delete cookie button text.
807     if (deleteCookieButtonPointer->isEnabled())  // The button is enabled.
808     {
809         if (treeModelPointer->hasChildren(currentIndex))  // A domain is selected.
810         {
811             // Update the button text.
812             deleteCookieButtonPointer->setText(i18ncp("Delete cookies button.", "&Delete %1 cookie", "&Delete %1 cookies", treeModelPointer->rowCount(currentIndex)));
813         }
814         else  // A single cookie is selected.
815         {
816             // Update the button text.
817             deleteCookieButtonPointer->setText(i18nc("Delete cookies button.", "&Delete 1 cookie"));
818         }
819     }
820     else  // The button is disabled.
821     {
822         // Reset the button text.
823         deleteCookieButtonPointer->setText(i18nc("Delete cookie button.", "&Delete cookie"));
824     }
825
826     // Update the text of the durable cookies button.
827     durableCookiesButtonPointer->setText(i18nc("View the durable cookies button", "Durable cookies - %1", CookiesDatabase::cookieCount()));
828 }