2 * Copyright © 2022-2023 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
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.
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.
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/>.
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"
27 // KDE Frameworks headers.
28 #include <KLocalizedString>
30 // Qt toolkit headers.
32 #include <QMessageBox>
36 // Define the cookie sort predicate.
37 bool cookieSortPredicate(const QNetworkCookie &leftHandCookie, const QNetworkCookie &rightHandCookie)
39 // Check to see if the domains are identical.
40 if (leftHandCookie.domain() == rightHandCookie.domain())
42 // Check to see if the names are identical.
43 if (leftHandCookie.name() == rightHandCookie.name())
45 // Sort the cookies by the path.
46 return (leftHandCookie.path() < rightHandCookie.path());
48 else // The name are not identical.
50 // Sort the cookies by the name.
51 return (leftHandCookie.name() < rightHandCookie.name());
54 else // The domains are not identical.
56 // Get copies of the domains.
57 QString leftHandDomain = leftHandCookie.domain();
58 QString rightHandDomain = rightHandCookie.domain();
60 // Create the strings.
61 QString leftHandTopLevelDomain;
62 QString rightHandTopLevelDomain;
63 QString leftHandSecondLevelDomain;
64 QString rightHandSecondLevelDomain;
65 QString leftHandThirdLevelDomain;
66 QString rightHandThirdLevelDomain;
68 // Get the number of dots in the strings.
69 int leftHandDots = leftHandDomain.count(QLatin1Char('.'));
70 int rightHandDots = rightHandDomain.count(QLatin1Char('.'));
72 // Get the top level domains.
73 leftHandTopLevelDomain = leftHandDomain.section('.', -1);
74 rightHandTopLevelDomain = rightHandDomain.section('.', -1);
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);
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);
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)
91 // Check to see if the second level domains are the same.
92 if (leftHandSecondLevelDomain == rightHandSecondLevelDomain)
94 // Check to see if the third level domains are the same.
95 if (leftHandThirdLevelDomain == rightHandThirdLevelDomain)
97 // Sort the cookies by the full domain because they share the same third level domain.
98 return (leftHandDomain < rightHandDomain);
100 else // The second level domains are the same, but the third level domains are different.
102 // Sort the cookies by the third level domains.
103 return (leftHandThirdLevelDomain < rightHandThirdLevelDomain);
106 else // The top level domains are the same, but the second level domains are diferent.
108 // Sort the cookies by the second level domain.
109 return (leftHandSecondLevelDomain < rightHandSecondLevelDomain);
112 else // The top level domains are different.
114 // Sort the cookies by the top level domain.
115 return (leftHandTopLevelDomain < rightHandTopLevelDomain);
120 // Construct the class.
121 CookiesDialog::CookiesDialog(std::list<QNetworkCookie> *originalCookieListPointer) : QDialog(nullptr), cookieListPointer(originalCookieListPointer)
123 // Set the dialog window title.
124 setWindowTitle(i18nc("The cookies dialog window title", "Cookies"));
126 // Set the window modality.
127 setWindowModality(Qt::WindowModality::ApplicationModal);
129 // Instantiate the cookie settings dialog UI.
130 Ui::CookiesDialog cookiesDialogUi;
133 cookiesDialogUi.setupUi(this);
135 // Get a handle for the tree view.
136 treeViewPointer = cookiesDialogUi.treeView;
138 // Initialize the tree model.
139 treeModelPointer = new QStandardItemModel();
141 // Set the column count.
142 treeModelPointer->setColumnCount(7);
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"));
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."));
166 // Sort the cookie list.
167 cookieListPointer->sort(cookieSortPredicate);
169 // Create the current domain string.
170 QString currentDomainString = "";
172 // Create the current domain item pointer.
173 QStandardItem *currentDomainItemPointer;
175 // Populate the cookie tree view.
176 for (QNetworkCookie cookie : *cookieListPointer)
178 // Get the cookie domain.
179 QString cookieDomain = cookie.domain();
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.
184 // Create the domain name item.
185 QStandardItem *domainNameItemPointer = new QStandardItem(cookieDomain);
187 // Add the domain to the tree.
188 treeModelPointer->invisibleRootItem()->appendRow(domainNameItemPointer);
190 // Update the current domain string.
191 currentDomainString = cookieDomain;
193 // Update the current domain item pointer.
194 currentDomainItemPointer = domainNameItemPointer;
197 // Check to see if the cookie is durable.
198 bool isDurable = CookiesDatabase::isDurable(cookie);
200 // Create a list for the cookie items.
201 QList<QStandardItem*> cookieItemList;
203 // Create the cookie items.
204 QStandardItem *nameItemPointer = new QStandardItem(QString(cookie.name()));
205 QStandardItem *durableItemPointer = new QStandardItem(QString(isDurable ? i18n("yes") : i18n("no")));
206 QStandardItem *pathItemPointer = new QStandardItem(QString(cookie.path()));
207 QStandardItem *expirationDateItemPointer = new QStandardItem(QString(cookie.expirationDate().toString()));
208 QStandardItem *isHttpOnlyItemPointer = new QStandardItem(QString(cookie.isHttpOnly() ? i18n("yes") : i18n("no")));
209 QStandardItem *isSecureItemPointer = new QStandardItem(QString(cookie.isSecure() ? i18n("yes") : i18n("no")));
210 QStandardItem *valueItemPointer = new QStandardItem(QString(cookie.value()));
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);
221 // Add the cookie to the tree.
222 currentDomainItemPointer->appendRow(cookieItemList);
225 // Auto resize the headers.
226 treeViewPointer->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
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);
231 // Don't elide the Value field (or any other field).
232 treeViewPointer->setTextElideMode(Qt::ElideNone);
234 // Indicate that all the rows are the same height, wich improves performance.
235 treeViewPointer->setUniformRowHeights(true);
237 // Disable editing in the tree view.
238 treeViewPointer->setEditTriggers(QAbstractItemView::NoEditTriggers);
240 // Set the tree model.
241 treeViewPointer->setModel(treeModelPointer);
243 // Get a handle for the tree selection model.
244 treeSelectionModelPointer = treeViewPointer->selectionModel();
246 // Listen for selection changes.
247 connect(treeSelectionModelPointer, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(updateUi()));
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);
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);
261 // Set the button icons.
262 durableCookiesButtonPointer->setIcon(QIcon::fromTheme("view-visible"));
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()));
272 // Set the close button to be the default.
273 closeButtonPointer->setDefault(true);
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);
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()));
293 // Edit a cookie when it is double clicked.
294 connect(treeViewPointer, SIGNAL(doubleClicked(QModelIndex)), editCookieButtonPointer, SLOT(click()));
300 void CookiesDialog::addCookieFromDialog(const QNetworkCookie &cookie, const bool &isDurable) const
302 // Add the cookie to the cookie list and the cookie store.
303 emit addCookie(cookie);
305 // Get the new domain string.
306 QString newDomain = cookie.domain();
308 // Check to see if the domain already exists in the model.
309 QList<QStandardItem*> currentDomainItemList = treeModelPointer->findItems(newDomain);
311 // Create a domain item pointer.
312 QStandardItem *domainNameItemPointer;
314 // Prepare the domain item pointer.
315 if (currentDomainItemList.isEmpty()) // The domain doesn't currently exist in the tree.
317 // Create the domain name item.
318 domainNameItemPointer = new QStandardItem(newDomain);
320 // Get the number of domains in the tree.
321 int numberOfDomains = treeModelPointer->invisibleRootItem()->rowCount();
323 // Create the insert domain row number and initially set it to be at the end of the list.
324 int insertDomainRowNumber = numberOfDomains;
326 // Create the new domain strings.
327 QString newDomainTopLevelDomain;
328 QString newDomainSecondLevelDomain;
329 QString newDomainThirdLevelDomain;
331 // Get the number of dots in the new domain string.
332 int newDomainDots = newDomain.count(QLatin1Char('.'));
334 // Get the new top level domain.
335 newDomainTopLevelDomain = newDomain.section('.', -1);
337 // Get the new second level domain if it contains at least one dot.
338 if (newDomainDots >= 1)
339 newDomainSecondLevelDomain = newDomain.section('.', -2);
341 // Get the new third level domain if it contains at least two dots.
342 if (newDomainDots >= 2)
343 newDomainThirdLevelDomain = newDomain.section('.', -3);
345 // Create while loop trackers.
346 bool locationFound = false;
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();
354 // Create the current domain strings.
355 QString currentDomainTopLevelDomain;
356 QString currentDomainSecondLevelDomain;
357 QString currentDomainThirdLevelDomain;
359 // Get the number of dots in the current domain string.
360 int currentDomainDots = currentDomain.count(QLatin1Char('.'));
362 // Get the current top level domain.
363 currentDomainTopLevelDomain = currentDomain.section('.', -1);
365 // Get the current second level domain if it contains at least one dot.
366 if (currentDomainDots >= 1)
367 currentDomainSecondLevelDomain = currentDomain.section('.', -2);
369 // Get the current third level domain if it contains at least two dots.
370 if (currentDomainDots >= 2)
371 currentDomainThirdLevelDomain = currentDomain.section('.', -3);
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`.
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;
383 // Mark the location as found.
384 locationFound = true;
387 // Move to the next row.
391 // Add the domain to the tree.
392 treeModelPointer->invisibleRootItem()->insertRow(insertDomainRowNumber, domainNameItemPointer);
394 else // The domain already exists in the tree.
396 // Use the current domain standard item.
397 domainNameItemPointer = currentDomainItemList[0];
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());
404 // Create a cookie item list.
405 QList<QStandardItem*> cookieItemList;
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()));
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);
425 // Get the number of cookies in the domain.
426 int numberOfCookies = domainNameItemPointer->rowCount();
428 // Create the insert cookie row number and initially set it to be at the end of the list.
429 int insertCookieRowNumber = numberOfCookies;
431 // Create the trackers.
432 bool removeExistingRow = false;
433 bool rowFound = false;
436 while (!rowFound && currentRow < numberOfCookies)
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();
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.
447 // Remove the existing cookie if the core attributes are the same.
448 if ((newCookieName == currentCookieName) && (newCookiePath == currentCookiePath))
449 removeExistingRow = true;
451 // Insert the new cookie at this row.
452 insertCookieRowNumber = currentRow;
454 // Mark the row as found.
458 // Move to the next row.
462 // Remove the existing row if it is being edited.
463 if (removeExistingRow)
464 domainNameItemPointer->removeRow(insertCookieRowNumber);
466 // Add the cookie to the tree model.
467 domainNameItemPointer->insertRow(insertCookieRowNumber, cookieItemList);
469 // Get the new cookie model index.
470 QModelIndex newCookieIndex = nameItemPointer->index();
472 // Set the new cookie to be the current index.
473 treeViewPointer->setCurrentIndex(newCookieIndex);
475 // Expand the parent of the new cookie.
476 treeViewPointer->expand(newCookieIndex.parent());
479 void CookiesDialog::deleteCookie(const QModelIndex &modelIndex, const bool &deleteDurableCookies) const
481 // Create a partial cookie.
482 QNetworkCookie partialCookie;
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());
489 // Create a cookie to delete.
490 QNetworkCookie cookieToDelete;
492 // Check if the cookie is durable.
493 bool isDurable = CookiesDatabase::isDurable(partialCookie);
495 // Only delete durable cookies if directed.
496 if (deleteDurableCookies || !isDurable)
498 // Search for the partial cookie in the cookie list.
499 for (QNetworkCookie cookie : *cookieListPointer)
501 // Store the cookie to delete if it has the same identifier as the partial cookie.
502 if (cookie.hasSameIdentifier(partialCookie))
503 cookieToDelete = cookie;
506 // Remove the cookie from the tree model.
507 treeModelPointer->removeRow(modelIndex.row(), modelIndex.parent());
509 // Delete the cookie from the cookie list and cookie store.
510 emit deleteCookie(cookieToDelete);
512 // Delete the cookie from the durable cookies database.
514 CookiesDatabase::deleteCookie(cookieToDelete);
518 void CookiesDialog::deleteDomain(const QModelIndex &modelIndex, const bool &deleteDurableCookies) const
520 // Get the parent index.
521 QModelIndex parentIndex = modelIndex.parent();
523 // Get the number of cookies in the domain.
524 int numberOfCookies = treeModelPointer->rowCount(modelIndex);
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);
530 // Remove the domain if all the cookies have been deleted.
531 if (treeModelPointer->rowCount(modelIndex) == 0)
532 treeModelPointer->removeRow(modelIndex.row(), parentIndex);
535 void CookiesDialog::deleteCookieFromDatabase(const QNetworkCookie &cookie) const
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());
540 // Find any matching cookies.
541 for (QStandardItem *domainItemPointer : domainList)
543 // Get the number of cookies in the domain.
544 int numberOfCookies = domainItemPointer->rowCount();
546 // Initialize the tracking variables.
547 bool cookieFound = false;
550 // Find the cookie in the tree model.
551 while (!cookieFound && (currentRow < numberOfCookies))
553 // Get the name item.
554 QStandardItem *nameItemPointer = domainItemPointer->child(currentRow);
556 // Get the name model index.
557 QModelIndex nameModelIndex = nameItemPointer->index();
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()))
562 // Set the current index.
563 treeSelectionModelPointer->setCurrentIndex(nameModelIndex, QItemSelectionModel::ClearAndSelect);
565 // Delete the cookie.
566 deleteCookieFromDialog(cookie);
568 // Mark the cookie as found.
572 // Move to the next row.
578 void CookiesDialog::deleteCookieFromDialog(const QNetworkCookie &cookie) const
580 // Get the current model index.
581 QModelIndex currentIndex = treeSelectionModelPointer->currentIndex();
583 // Get the parent index.
584 QModelIndex parentIndex = currentIndex.parent();
586 // Remove the cookie from the tree model.
587 treeModelPointer->removeRow(currentIndex.row(), parentIndex);
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());
593 // Delete the cookie from the cookie list and cookie store.
594 emit deleteCookie(cookie);
597 void CookiesDialog::showAddCookieDialog() const
599 // Instantiate an add cookie dialog.
600 QDialog *addCookieDialogPointer = new AddOrEditCookieDialog(AddOrEditCookieDialog::AddCookie);
603 addCookieDialogPointer->show();
605 // Add the cookie if directed.
606 connect(addCookieDialogPointer, SIGNAL(addCookie(QNetworkCookie, bool)), this, SLOT(addCookieFromDialog(QNetworkCookie, bool)));
609 void CookiesDialog::showDeleteAllMessageBox() const
611 // Instantiate a delete all message box.
612 QMessageBox deleteAllCookiesMessageBox;
615 deleteAllCookiesMessageBox.setIcon(QMessageBox::Warning);
617 // Set the window title.
618 deleteAllCookiesMessageBox.setWindowTitle(i18nc("Delete all cookies dialog title", "Delete All Cookies"));
621 deleteAllCookiesMessageBox.setText(i18nc("Delete all cookies dialog text", "Delete all cookies?"));
623 // Create a delete durable cookies check box.
624 QCheckBox deleteDurableCookiesCheckBox(i18nc("Delete durable cookies check box", "Delete even if durable"));
626 // Add the check box to the dialog.
627 deleteAllCookiesMessageBox.setCheckBox(&deleteDurableCookiesCheckBox);
629 // Set the standard buttons.
630 deleteAllCookiesMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
632 // Set the default button.
633 deleteAllCookiesMessageBox.setDefaultButton(QMessageBox::No);
635 // Display the dialog and capture the return value.
636 int returnValue = deleteAllCookiesMessageBox.exec();
638 // Delete all cookies if instructed.
639 if (returnValue == QMessageBox::Yes)
641 // Only delete durable cookies if requested.
642 if (deleteDurableCookiesCheckBox.isChecked()) // Delete everything.
644 // Delete all the cookies.
645 emit deleteAllCookies();
647 // Clear the tree model.
648 treeModelPointer->clear();
650 // Delete the durable cookies from the database.
651 CookiesDatabase::deleteAllCookies();
656 else // Only delete cookies that are not durable.
658 // Get the root model index.
659 QModelIndex rootIndex = treeModelPointer->invisibleRootItem()->index();
661 // Get the number of domains.
662 int numberOfDomains = treeModelPointer->rowCount(rootIndex);
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());
671 void CookiesDialog::showDeleteCookieMessageBox() const
673 // Get the current model index.
674 QModelIndex currentIndex = treeSelectionModelPointer->currentIndex();
676 // Determine if a domain is selected.
677 bool isDomain = treeModelPointer->hasChildren(currentIndex);
679 // Instantiate a delete cookie message box.
680 QMessageBox deleteCookieMessageBox;
683 deleteCookieMessageBox.setIcon(QMessageBox::Warning);
685 // Create a delete durable cookies check box.
686 QCheckBox deleteDurableCookiesCheckBox(i18nc("Delete durable cookies check box", "Delete even if durable"));
688 if (isDomain) // A domain is selected.
690 // Get the number of cookies.
691 int numberOfCookiesToDelete = treeModelPointer->rowCount(currentIndex);
693 // Set the window title.
694 deleteCookieMessageBox.setWindowTitle(i18ncp("Delete cookies dialog title", "Delete %1 Cookie", "Delete 1% Cookies", numberOfCookiesToDelete));
697 deleteCookieMessageBox.setText(i18ncp("Delete cookies dialog text", "Delete %1 cookie?", "Delete %1 cookies?", numberOfCookiesToDelete));
699 else // A single cookie is selected.
701 // Set the window title.
702 deleteCookieMessageBox.setWindowTitle(i18nc("Delete cookie dialog title", "Delete 1 Cookie"));
705 deleteCookieMessageBox.setText(i18nc("Delete cookie dialog text", "Delete 1 cookie?"));
708 deleteDurableCookiesCheckBox.setChecked(true);
711 // Add the check box to the dialog.
712 deleteCookieMessageBox.setCheckBox(&deleteDurableCookiesCheckBox);
714 // Set the standard buttons.
715 deleteCookieMessageBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
717 // Set the default button.
718 deleteCookieMessageBox.setDefaultButton(QMessageBox::No);
720 // Display the dialog and capture the return value.
721 int returnValue = deleteCookieMessageBox.exec();
723 // Delete the cookie if instructed.
724 if (returnValue == QMessageBox::Yes)
726 // Delete the cookies according to the selection.
727 if (isDomain) // A domain is selected.
729 // Delete the domain.
730 deleteDomain(currentIndex, deleteDurableCookiesCheckBox.isChecked());
732 else // A single cookie is selected.
734 // Get the parent model index.
735 QModelIndex parentIndex = currentIndex.parent();
737 // Delete the cookie.
738 deleteCookie(currentIndex, deleteDurableCookiesCheckBox.isChecked());
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());
747 void CookiesDialog::showDurableCookiesDialog() const
749 // Instantiate a durable cookies dialog.
750 QDialog *durableCookiesDialogPointer = new DurableCookiesDialog();
753 durableCookiesDialogPointer->show();
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()));
761 void CookiesDialog::showEditCookieDialog() const
763 // Get the current model index.
764 QModelIndex currentIndex = treeSelectionModelPointer->currentIndex();
766 // Create a partial cookie.
767 QNetworkCookie partialCookie;
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());
774 // Create a cookie to edit.
775 QNetworkCookie cookieToEdit;
777 // Search for the partial cookie in the cookie list.
778 for (QNetworkCookie cookie : *cookieListPointer)
780 // Store the cookie to edit if it has the same identifier as the partial cookie.
781 if (cookie.hasSameIdentifier(partialCookie))
782 cookieToEdit = cookie;
785 // Instantiate an edit cookie dialog.
786 QDialog *editCookieDialogPointer = new AddOrEditCookieDialog(AddOrEditCookieDialog::EditCookie, &cookieToEdit, currentIndex.siblingAtColumn(1).data().toString() == i18n("yes"));
789 editCookieDialogPointer->show();
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)));
796 void CookiesDialog::updateUi() const
798 // Get the current index of the first column.
799 QModelIndex currentIndex = treeSelectionModelPointer->currentIndex().siblingAtColumn(0);
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()));
806 // Update the delete cookie button text.
807 if (deleteCookieButtonPointer->isEnabled()) // The button is enabled.
809 if (treeModelPointer->hasChildren(currentIndex)) // A domain is selected.
811 // Update the button text.
812 deleteCookieButtonPointer->setText(i18ncp("Delete cookies button.", "&Delete %1 cookie", "&Delete %1 cookies", treeModelPointer->rowCount(currentIndex)));
814 else // A single cookie is selected.
816 // Update the button text.
817 deleteCookieButtonPointer->setText(i18nc("Delete cookies button.", "&Delete 1 cookie"));
820 else // The button is disabled.
822 // Reset the button text.
823 deleteCookieButtonPointer->setText(i18nc("Delete cookie button.", "&Delete cookie"));
826 // Update the text of the durable cookies button.
827 durableCookiesButtonPointer->setText(i18nc("View the durable cookies button", "Durable cookies - %1", CookiesDatabase::cookieCount()));