File indexing completed on 2024-05-12 15:41:57

0001 /**
0002     kcookiespolicies.cpp - Cookies configuration
0003 
0004     Original Authors
0005     SPDX-FileCopyrightText: Waldo Bastian <bastian@kde.org>
0006     SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
0007     SPDX-FileCopyrightText: 2008 Urs Wolfer <uwolfer @ kde.org>
0008 
0009     Re-written by:
0010     SPDX-FileCopyrightText: 2000 Dawit Alemayehu <adawit@kde.org>
0011 
0012     SPDX-License-Identifier: GPL-2.0-or-later
0013 */
0014 
0015 // Own
0016 #include "kcookiespolicies.h"
0017 
0018 // Local
0019 #include "../ksaveioconfig.h"
0020 
0021 // Qt
0022 #include <QCheckBox>
0023 #include <QDBusInterface>
0024 #include <QDBusReply>
0025 #include <QPushButton>
0026 
0027 // KDE
0028 #include <KConfig>
0029 #include <KConfigGroup>
0030 #include <KLocalizedString>
0031 #include <KMessageBox>
0032 #include <QUrl>
0033 
0034 // QUrl::fromAce/toAce don't accept a domain that starts with a '.', like we do here.
0035 // So we use these wrappers.
0036 QString tolerantFromAce(const QByteArray &_domain)
0037 {
0038     QByteArray domain(_domain);
0039     const bool hasDot = domain.startsWith('.');
0040     if (hasDot) {
0041         domain.remove(0, 1);
0042     }
0043     QString ret = QUrl::fromAce(domain);
0044     if (hasDot) {
0045         ret.prepend(QLatin1Char('.'));
0046     }
0047     return ret;
0048 }
0049 
0050 static QByteArray tolerantToAce(const QString &_domain)
0051 {
0052     QString domain(_domain);
0053     const bool hasDot = domain.startsWith(QLatin1Char('.'));
0054     if (hasDot) {
0055         domain.remove(0, 1);
0056     }
0057     QByteArray ret = QUrl::toAce(domain);
0058     if (hasDot) {
0059         ret.prepend('.');
0060     }
0061     return ret;
0062 }
0063 
0064 KCookiesPolicies::KCookiesPolicies(QWidget *parent, const QVariantList &args)
0065     : KCModule(parent, args)
0066     , mSelectedItemsCount(0)
0067 {
0068     mUi.setupUi(this);
0069     mUi.kListViewSearchLine->setTreeWidget(mUi.policyTreeWidget);
0070     QList<int> columns;
0071     columns.append(0);
0072     mUi.kListViewSearchLine->setSearchColumns(columns);
0073 
0074     mUi.pbNew->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
0075     mUi.pbChange->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
0076     mUi.pbDelete->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
0077     mUi.pbDeleteAll->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0078 
0079     // Connect the main switch :) Enable/disable cookie support
0080     connect(mUi.cbEnableCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::cookiesEnabled);
0081     connect(mUi.cbEnableCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged);
0082 
0083     // Connect the preference check boxes...
0084     connect(mUi.cbRejectCrossDomainCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged);
0085     connect(mUi.cbAutoAcceptSessionCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged);
0086 
0087     connect(mUi.rbPolicyAsk, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged);
0088     connect(mUi.rbPolicyAccept, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged);
0089     connect(mUi.rbPolicyAcceptForSession, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged);
0090     connect(mUi.rbPolicyReject, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged);
0091     // Connect signals from the domain specific policy listview.
0092     connect(mUi.policyTreeWidget, &QTreeWidget::itemSelectionChanged, this, &KCookiesPolicies::selectionChanged);
0093     connect(mUi.policyTreeWidget, &QTreeWidget::itemDoubleClicked, this, qOverload<>(&KCookiesPolicies::changePressed));
0094 
0095     // Connect the buttons...
0096     connect(mUi.pbNew, &QAbstractButton::clicked, this, qOverload<>(&KCookiesPolicies::addPressed));
0097     connect(mUi.pbChange, &QAbstractButton::clicked, this, qOverload<>(&KCookiesPolicies::changePressed));
0098     connect(mUi.pbDelete, &QAbstractButton::clicked, this, &KCookiesPolicies::deletePressed);
0099     connect(mUi.pbDeleteAll, &QAbstractButton::clicked, this, &KCookiesPolicies::deleteAllPressed);
0100 }
0101 
0102 KCookiesPolicies::~KCookiesPolicies()
0103 {
0104 }
0105 
0106 void KCookiesPolicies::configChanged()
0107 {
0108     // kDebug() << "KCookiesPolicies::configChanged...";
0109     Q_EMIT changed(true);
0110 }
0111 
0112 void KCookiesPolicies::cookiesEnabled(bool enable)
0113 {
0114     mUi.bgDefault->setEnabled(enable);
0115     mUi.bgPreferences->setEnabled(enable);
0116     mUi.gbDomainSpecific->setEnabled(enable);
0117 }
0118 
0119 void KCookiesPolicies::setPolicy(const QString &domain)
0120 {
0121     QTreeWidgetItemIterator it(mUi.policyTreeWidget);
0122     bool hasExistingPolicy = false;
0123     while (*it) {
0124         if ((*it)->text(0) == domain) {
0125             hasExistingPolicy = true;
0126             break;
0127         }
0128         ++it;
0129     }
0130 
0131     if (hasExistingPolicy) {
0132         changePressed((*it), false);
0133     } else {
0134         addPressed(domain);
0135     }
0136 }
0137 
0138 void KCookiesPolicies::changePressed()
0139 {
0140     changePressed(mUi.policyTreeWidget->currentItem());
0141 }
0142 
0143 void KCookiesPolicies::addPressed()
0144 {
0145     addPressed(QString());
0146 }
0147 
0148 void KCookiesPolicies::changePressed(QTreeWidgetItem *item, bool state)
0149 {
0150     Q_ASSERT(item);
0151     const QString oldDomain(item->text(0));
0152 
0153     KCookiesPolicySelectionDlg pdlg(this);
0154     pdlg.setWindowTitle(i18nc("@title:window", "Change Cookie Policy"));
0155     pdlg.setPolicy(KCookieAdvice::strToAdvice(QString::fromLatin1(mDomainPolicyMap.value(oldDomain))));
0156     pdlg.setEnableHostEdit(state, oldDomain);
0157 
0158     if (pdlg.exec() && !pdlg.domain().isEmpty()) {
0159         const QString newDomain = tolerantFromAce(pdlg.domain().toLatin1());
0160         int advice = pdlg.advice();
0161         if (newDomain == oldDomain || !handleDuplicate(newDomain, advice)) {
0162             mDomainPolicyMap[newDomain] = KCookieAdvice::adviceToStr(advice);
0163             item->setText(0, newDomain);
0164             item->setText(1, i18n(mDomainPolicyMap.value(newDomain)));
0165             configChanged();
0166         }
0167     }
0168 }
0169 
0170 void KCookiesPolicies::addPressed(const QString &domain, bool state)
0171 {
0172     KCookiesPolicySelectionDlg pdlg(this);
0173     pdlg.setWindowTitle(i18nc("@title:window", "New Cookie Policy"));
0174     pdlg.setEnableHostEdit(state, domain);
0175 
0176     if (mUi.rbPolicyAccept->isChecked()) {
0177         pdlg.setPolicy(KCookieAdvice::Reject);
0178     } else {
0179         pdlg.setPolicy(KCookieAdvice::Accept);
0180     }
0181 
0182     if (pdlg.exec() && !pdlg.domain().isEmpty()) {
0183         const QString domain = tolerantFromAce(pdlg.domain().toLatin1());
0184         int advice = pdlg.advice();
0185 
0186         if (!handleDuplicate(domain, advice)) {
0187             const char *strAdvice = KCookieAdvice::adviceToStr(advice);
0188             const QStringList items{
0189                 domain,
0190                 i18n(strAdvice),
0191             };
0192             QTreeWidgetItem *item = new QTreeWidgetItem(mUi.policyTreeWidget, items);
0193             mDomainPolicyMap.insert(item->text(0), strAdvice);
0194             configChanged();
0195             updateButtons();
0196         }
0197     }
0198 }
0199 
0200 bool KCookiesPolicies::handleDuplicate(const QString &domain, int advice)
0201 {
0202     QTreeWidgetItem *item = mUi.policyTreeWidget->topLevelItem(0);
0203     while (item != nullptr) {
0204         if (item->text(0) == domain) {
0205             const int res = KMessageBox::warningContinueCancel(this,
0206                                                                i18n("<qt>A policy already exists for"
0207                                                                     "<center><b>%1</b></center>"
0208                                                                     "Do you want to replace it?</qt>",
0209                                                                     domain),
0210                                                                i18nc("@title:window", "Duplicate Policy"),
0211                                                                KGuiItem(i18n("Replace")));
0212             if (res == KMessageBox::Continue) {
0213                 mDomainPolicyMap[domain] = KCookieAdvice::adviceToStr(advice);
0214                 item->setText(0, domain);
0215                 item->setText(1, i18n(mDomainPolicyMap.value(domain)));
0216                 configChanged();
0217                 return true;
0218             } else {
0219                 return true; // User Cancelled!!
0220             }
0221         }
0222         item = mUi.policyTreeWidget->itemBelow(item);
0223     }
0224     return false;
0225 }
0226 
0227 void KCookiesPolicies::deletePressed()
0228 {
0229     QTreeWidgetItem *nextItem = nullptr;
0230 
0231     const QList<QTreeWidgetItem *> selectedItems = mUi.policyTreeWidget->selectedItems();
0232     for (const QTreeWidgetItem *item : selectedItems) {
0233         nextItem = mUi.policyTreeWidget->itemBelow(item);
0234         if (!nextItem) {
0235             nextItem = mUi.policyTreeWidget->itemAbove(item);
0236         }
0237 
0238         mDomainPolicyMap.remove(item->text(0));
0239         delete item;
0240     }
0241 
0242     if (nextItem) {
0243         nextItem->setSelected(true);
0244     }
0245 
0246     updateButtons();
0247     configChanged();
0248 }
0249 
0250 void KCookiesPolicies::deleteAllPressed()
0251 {
0252     mDomainPolicyMap.clear();
0253     mUi.policyTreeWidget->clear();
0254     updateButtons();
0255     configChanged();
0256 }
0257 
0258 void KCookiesPolicies::updateButtons()
0259 {
0260     bool hasItems = mUi.policyTreeWidget->topLevelItemCount() > 0;
0261 
0262     mUi.pbChange->setEnabled((hasItems && mSelectedItemsCount == 1));
0263     mUi.pbDelete->setEnabled((hasItems && mSelectedItemsCount > 0));
0264     mUi.pbDeleteAll->setEnabled(hasItems);
0265 }
0266 
0267 void KCookiesPolicies::updateDomainList(const QStringList &domainConfig)
0268 {
0269     mUi.policyTreeWidget->clear();
0270 
0271     QStringList::ConstIterator it = domainConfig.begin();
0272     for (; it != domainConfig.end(); ++it) {
0273         QString domain;
0274         KCookieAdvice::Value advice = KCookieAdvice::Dunno;
0275         splitDomainAdvice(*it, domain, advice);
0276         if (!domain.isEmpty()) {
0277             const QStringList items{
0278                 tolerantFromAce(domain.toLatin1()),
0279                 i18n(KCookieAdvice::adviceToStr(advice)),
0280             };
0281             QTreeWidgetItem *item = new QTreeWidgetItem(mUi.policyTreeWidget, items);
0282             mDomainPolicyMap[item->text(0)] = KCookieAdvice::adviceToStr(advice);
0283         }
0284     }
0285 
0286     mUi.policyTreeWidget->sortItems(0, Qt::AscendingOrder);
0287 }
0288 
0289 void KCookiesPolicies::selectionChanged()
0290 {
0291     mSelectedItemsCount = mUi.policyTreeWidget->selectedItems().count();
0292     updateButtons();
0293 }
0294 
0295 void KCookiesPolicies::load()
0296 {
0297     mSelectedItemsCount = 0;
0298 
0299     KConfig cfg(QStringLiteral("kcookiejarrc"));
0300     KConfigGroup group = cfg.group("Cookie Policy");
0301 
0302     bool enableCookies = group.readEntry("Cookies", true);
0303     mUi.cbEnableCookies->setChecked(enableCookies);
0304     cookiesEnabled(enableCookies);
0305 
0306     // Warning: the default values are duplicated in kcookiejar.cpp
0307     KCookieAdvice::Value advice = KCookieAdvice::strToAdvice(group.readEntry("CookieGlobalAdvice", "Accept"));
0308     switch (advice) {
0309     case KCookieAdvice::Accept:
0310         mUi.rbPolicyAccept->setChecked(true);
0311         break;
0312     case KCookieAdvice::AcceptForSession:
0313         mUi.rbPolicyAcceptForSession->setChecked(true);
0314         break;
0315     case KCookieAdvice::Reject:
0316         mUi.rbPolicyReject->setChecked(true);
0317         break;
0318     case KCookieAdvice::Ask:
0319     case KCookieAdvice::Dunno:
0320     default:
0321         mUi.rbPolicyAsk->setChecked(true);
0322     }
0323 
0324     bool enable = group.readEntry("RejectCrossDomainCookies", true);
0325     mUi.cbRejectCrossDomainCookies->setChecked(enable);
0326 
0327     bool sessionCookies = group.readEntry("AcceptSessionCookies", true);
0328     mUi.cbAutoAcceptSessionCookies->setChecked(sessionCookies);
0329     updateDomainList(group.readEntry("CookieDomainAdvice", QStringList()));
0330 
0331     if (enableCookies) {
0332         updateButtons();
0333     }
0334 }
0335 
0336 void KCookiesPolicies::save()
0337 {
0338     KConfig cfg(QStringLiteral("kcookiejarrc"));
0339     KConfigGroup group = cfg.group("Cookie Policy");
0340 
0341     bool state = mUi.cbEnableCookies->isChecked();
0342     group.writeEntry("Cookies", state);
0343     state = mUi.cbRejectCrossDomainCookies->isChecked();
0344     group.writeEntry("RejectCrossDomainCookies", state);
0345     state = mUi.cbAutoAcceptSessionCookies->isChecked();
0346     group.writeEntry("AcceptSessionCookies", state);
0347 
0348     QString advice;
0349     if (mUi.rbPolicyAccept->isChecked()) {
0350         advice = QString::fromLatin1(KCookieAdvice::adviceToStr(KCookieAdvice::Accept));
0351     } else if (mUi.rbPolicyAcceptForSession->isChecked()) {
0352         advice = QString::fromLatin1(KCookieAdvice::adviceToStr(KCookieAdvice::AcceptForSession));
0353     } else if (mUi.rbPolicyReject->isChecked()) {
0354         advice = QString::fromLatin1(KCookieAdvice::adviceToStr(KCookieAdvice::Reject));
0355     } else {
0356         advice = QString::fromLatin1(KCookieAdvice::adviceToStr(KCookieAdvice::Ask));
0357     }
0358 
0359     group.writeEntry("CookieGlobalAdvice", advice);
0360 
0361     QStringList domainConfig;
0362     QMapIterator<QString, const char *> it(mDomainPolicyMap);
0363     while (it.hasNext()) {
0364         it.next();
0365         const QString policy = QLatin1String(tolerantToAce(it.key())) + QLatin1Char(':') + QLatin1String(it.value());
0366         domainConfig << policy;
0367     }
0368 
0369     group.writeEntry("CookieDomainAdvice", domainConfig);
0370     group.sync();
0371 
0372     // Update the cookiejar...
0373     if (!mUi.cbEnableCookies->isChecked()) {
0374         QDBusInterface kded(QStringLiteral("org.kde.kcookiejar5"),
0375                             QStringLiteral("/modules/kcookiejar"),
0376                             QStringLiteral("org.kde.KCookieServer"),
0377                             QDBusConnection::sessionBus());
0378         kded.call(QStringLiteral("shutdown"));
0379     } else {
0380         QDBusInterface kded(QStringLiteral("org.kde.kcookiejar5"),
0381                             QStringLiteral("/modules/kcookiejar"),
0382                             QStringLiteral("org.kde.KCookieServer"),
0383                             QDBusConnection::sessionBus());
0384         QDBusReply<void> reply = kded.call(QStringLiteral("reloadPolicy"));
0385         if (!reply.isValid()) {
0386             KMessageBox::error(nullptr,
0387                                i18n("Unable to communicate with the cookie handler service.\n"
0388                                     "Any changes you made will not take effect until the service "
0389                                     "is restarted."));
0390         }
0391     }
0392 
0393     // Force running KIO workers to reload configurations...
0394     KSaveIOConfig::updateRunningWorkers(this);
0395     Q_EMIT changed(false);
0396 }
0397 
0398 void KCookiesPolicies::defaults()
0399 {
0400     mUi.cbEnableCookies->setChecked(true);
0401     mUi.rbPolicyAsk->setChecked(true);
0402     mUi.rbPolicyAccept->setChecked(false);
0403     mUi.rbPolicyAcceptForSession->setChecked(false);
0404     mUi.rbPolicyReject->setChecked(false);
0405     mUi.cbRejectCrossDomainCookies->setChecked(true);
0406     mUi.cbAutoAcceptSessionCookies->setChecked(false);
0407     mUi.policyTreeWidget->clear();
0408     mDomainPolicyMap.clear();
0409 
0410     cookiesEnabled(mUi.cbEnableCookies->isChecked());
0411     updateButtons();
0412 }
0413 
0414 void KCookiesPolicies::splitDomainAdvice(const QString &cfg, QString &domain, KCookieAdvice::Value &advice)
0415 {
0416     int sepPos = cfg.lastIndexOf(QLatin1Char(':'));
0417 
0418     // Ignore any policy that does not contain a domain...
0419     if (sepPos <= 0) {
0420         return;
0421     }
0422 
0423     domain = cfg.left(sepPos);
0424     advice = KCookieAdvice::strToAdvice(cfg.mid(sepPos + 1));
0425 }
0426 
0427 QString KCookiesPolicies::quickHelp() const
0428 {
0429     return i18n(
0430         "<h1>Cookies</h1><p>Cookies contain information that KDE"
0431         " application using the HTTP protocol (like Konqueror) stores"
0432         " on your computer from a remote Internet server. This means"
0433         " that a web server can store information about you and your"
0434         " browsing activities on your machine for later use. You might"
0435         " consider this an invasion of privacy.</p><p>However, cookies are"
0436         " useful in certain situations. For example, they are often used"
0437         " by Internet shops, so you can 'put things into a shopping"
0438         " basket'. Some sites require you have a browser that supports"
0439         " cookies.</p><p>Because most people want a compromise between privacy"
0440         " and the benefits cookies offer, KDE offers you the ability to"
0441         " customize the way it handles cookies. You might, for example"
0442         " want to set KDE's default policy to ask you whenever a server"
0443         " wants to set a cookie or simply reject or accept everything."
0444         " For example, you might choose to accept all cookies from your"
0445         " favorite shopping web site. For this all you have to do is"
0446         " either browse to that particular site and when you are presented"
0447         " with the cookie dialog box, click on <i> This domain </i> under"
0448         " the 'apply to' tab and choose accept or simply specify the name"
0449         " of the site in the <i> Domain Specific Policy </i> tab and set"
0450         " it to accept. This enables you to receive cookies from trusted"
0451         " web sites without being asked every time KDE receives a cookie.</p>");
0452 }
0453 
0454 #include "moc_kcookiespolicies.cpp"