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