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