File indexing completed on 2024-05-19 05:00:54
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 // Qt 0019 #include <QCheckBox> 0020 #include <QDBusInterface> 0021 #include <QDBusReply> 0022 #include <QPushButton> 0023 #include <QJsonDocument> 0024 #include <QJsonObject> 0025 0026 // KDE 0027 #include <KConfig> 0028 #include <KConfigGroup> 0029 #include <KLocalizedString> 0030 #include <KMessageBox> 0031 #include <QUrl> 0032 #include <KSharedConfig> 0033 0034 using namespace KonqInterfaces; 0035 0036 0037 // QUrl::fromAce/toAce don't accept a domain that starts with a '.', like we do here. 0038 // So we use these wrappers. 0039 QString tolerantFromAce(const QByteArray &_domain) 0040 { 0041 QByteArray domain(_domain); 0042 const bool hasDot = domain.startsWith('.'); 0043 if (hasDot) { 0044 domain.remove(0, 1); 0045 } 0046 QString ret = QUrl::fromAce(domain); 0047 if (hasDot) { 0048 ret.prepend(QLatin1Char('.')); 0049 } 0050 return ret; 0051 } 0052 0053 static QByteArray tolerantToAce(const QString &_domain) 0054 { 0055 QString domain(_domain); 0056 const bool hasDot = domain.startsWith(QLatin1Char('.')); 0057 if (hasDot) { 0058 domain.remove(0, 1); 0059 } 0060 QByteArray ret = QUrl::toAce(domain); 0061 if (hasDot) { 0062 ret.prepend('.'); 0063 } 0064 return ret; 0065 } 0066 0067 KCookiesPolicies::KCookiesPolicies(QObject *parent, const KPluginMetaData &md, const QVariantList &) 0068 : KCModule(parent, md) 0069 , mSelectedItemsCount(0) 0070 { 0071 mUi.setupUi(widget()); 0072 mUi.kListViewSearchLine->setTreeWidget(mUi.policyTreeWidget); 0073 QList<int> columns; 0074 columns.append(0); 0075 mUi.kListViewSearchLine->setSearchColumns(columns); 0076 0077 mUi.pbNew->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0078 mUi.pbChange->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); 0079 mUi.pbDelete->setIcon(QIcon::fromTheme(QStringLiteral("list-remove"))); 0080 mUi.pbDeleteAll->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); 0081 0082 // Connect the main switch :) Enable/disable cookie support 0083 connect(mUi.cbEnableCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::cookiesEnabled); 0084 connect(mUi.cbEnableCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); 0085 0086 // Connect the preference check boxes... 0087 connect(mUi.cbRejectCrossDomainCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); 0088 connect(mUi.cbAutoAcceptSessionCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); 0089 0090 connect(mUi.rbPolicyAsk, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); 0091 connect(mUi.rbPolicyAccept, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); 0092 connect(mUi.rbPolicyAcceptForSession, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); 0093 connect(mUi.rbPolicyReject, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); 0094 // Connect signals from the domain specific policy listview. 0095 connect(mUi.policyTreeWidget, &QTreeWidget::itemSelectionChanged, this, &KCookiesPolicies::selectionChanged); 0096 connect(mUi.policyTreeWidget, &QTreeWidget::itemDoubleClicked, this, QOverload<>::of(&KCookiesPolicies::changePressed)); 0097 0098 // Connect the buttons... 0099 connect(mUi.pbNew, &QAbstractButton::clicked, this, QOverload<>::of(&KCookiesPolicies::addPressed)); 0100 connect(mUi.pbChange, &QAbstractButton::clicked, this, QOverload<>::of(&KCookiesPolicies::changePressed)); 0101 connect(mUi.pbDelete, &QAbstractButton::clicked, this, &KCookiesPolicies::deletePressed); 0102 connect(mUi.pbDeleteAll, &QAbstractButton::clicked, this, &KCookiesPolicies::deleteAllPressed); 0103 } 0104 0105 KCookiesPolicies::~KCookiesPolicies() 0106 { 0107 } 0108 0109 void KCookiesPolicies::configChanged() 0110 { 0111 // kDebug() << "KCookiesPolicies::configChanged..."; 0112 setNeedsSave(true); 0113 } 0114 0115 void KCookiesPolicies::cookiesEnabled(bool enable) 0116 { 0117 mUi.bgDefault->setEnabled(enable); 0118 mUi.bgPreferences->setEnabled(enable); 0119 mUi.gbDomainSpecific->setEnabled(enable); 0120 } 0121 0122 void KCookiesPolicies::setPolicy(const QString &domain) 0123 { 0124 QTreeWidgetItemIterator it(mUi.policyTreeWidget); 0125 bool hasExistingPolicy = false; 0126 while (*it) { 0127 if ((*it)->text(0) == domain) { 0128 hasExistingPolicy = true; 0129 break; 0130 } 0131 ++it; 0132 } 0133 0134 if (hasExistingPolicy) { 0135 changePressed((*it), false); 0136 } else { 0137 addPressed(domain); 0138 } 0139 } 0140 0141 void KCookiesPolicies::changePressed() 0142 { 0143 changePressed(mUi.policyTreeWidget->currentItem()); 0144 } 0145 0146 void KCookiesPolicies::addPressed() 0147 { 0148 addPressed(QString()); 0149 } 0150 0151 void KCookiesPolicies::changePressed(QTreeWidgetItem *item, bool state) 0152 { 0153 Q_ASSERT(item); 0154 const QString oldDomain(item->text(0)); 0155 0156 KCookiesPolicySelectionDlg pdlg(widget()); 0157 pdlg.setWindowTitle(i18nc("@title:window", "Change Cookie Policy")); 0158 pdlg.setPolicy(mDomainPolicyMap.value(oldDomain)); 0159 pdlg.setEnableHostEdit(state, oldDomain); 0160 0161 if (pdlg.exec() && !pdlg.domain().isEmpty()) { 0162 const QString newDomain = tolerantFromAce(pdlg.domain().toLatin1()); 0163 CookieJar::CookieAdvice advice = pdlg.advice(); 0164 if (newDomain == oldDomain || !handleDuplicate(newDomain, advice)) { 0165 mDomainPolicyMap[newDomain] = advice; 0166 item->setText(0, newDomain); 0167 item->setText(1, i18n(KCookieAdvice::adviceToStr(mDomainPolicyMap.value(newDomain)))); 0168 configChanged(); 0169 } 0170 } 0171 } 0172 0173 void KCookiesPolicies::addPressed(const QString &domain, bool state) 0174 { 0175 KCookiesPolicySelectionDlg pdlg(widget()); 0176 pdlg.setWindowTitle(i18nc("@title:window", "New Cookie Policy")); 0177 pdlg.setEnableHostEdit(state, domain); 0178 0179 if (mUi.rbPolicyAccept->isChecked()) { 0180 pdlg.setPolicy(CookieJar::CookieAdvice::Reject); 0181 } else { 0182 pdlg.setPolicy(CookieJar::CookieAdvice::Accept); 0183 } 0184 0185 if (pdlg.exec() && !pdlg.domain().isEmpty()) { 0186 const QString domain = tolerantFromAce(pdlg.domain().toLatin1()); 0187 CookieJar::CookieAdvice advice = pdlg.advice(); 0188 0189 if (!handleDuplicate(domain, advice)) { 0190 const char *strAdvice = KCookieAdvice::adviceToStr(advice); 0191 const QStringList items{ 0192 domain, 0193 i18n(strAdvice), 0194 }; 0195 QTreeWidgetItem *item = new QTreeWidgetItem(mUi.policyTreeWidget, items); 0196 mDomainPolicyMap.insert(item->text(0), advice); 0197 configChanged(); 0198 updateButtons(); 0199 } 0200 } 0201 } 0202 0203 bool KCookiesPolicies::handleDuplicate(const QString &domain, CookieJar::CookieAdvice advice) 0204 { 0205 QTreeWidgetItem *item = mUi.policyTreeWidget->topLevelItem(0); 0206 while (item != nullptr) { 0207 if (item->text(0) == domain) { 0208 const int res = KMessageBox::warningContinueCancel(widget(), 0209 i18n("<qt>A policy already exists for" 0210 "<center><b>%1</b></center>" 0211 "Do you want to replace it?</qt>", 0212 domain), 0213 i18nc("@title:window", "Duplicate Policy"), 0214 KGuiItem(i18n("Replace"))); 0215 if (res == KMessageBox::Continue) { 0216 mDomainPolicyMap[domain] = advice; 0217 item->setText(0, domain); 0218 item->setText(1, i18n(KCookieAdvice::adviceToStr(mDomainPolicyMap.value(domain)))); 0219 configChanged(); 0220 return true; 0221 } else { 0222 return true; // User Cancelled!! 0223 } 0224 } 0225 item = mUi.policyTreeWidget->itemBelow(item); 0226 } 0227 return false; 0228 } 0229 0230 void KCookiesPolicies::deletePressed() 0231 { 0232 QTreeWidgetItem *nextItem = nullptr; 0233 0234 const QList<QTreeWidgetItem *> selectedItems = mUi.policyTreeWidget->selectedItems(); 0235 for (const QTreeWidgetItem *item : selectedItems) { 0236 nextItem = mUi.policyTreeWidget->itemBelow(item); 0237 if (!nextItem) { 0238 nextItem = mUi.policyTreeWidget->itemAbove(item); 0239 } 0240 0241 mDomainPolicyMap.remove(item->text(0)); 0242 delete item; 0243 } 0244 0245 if (nextItem) { 0246 nextItem->setSelected(true); 0247 } 0248 0249 updateButtons(); 0250 configChanged(); 0251 } 0252 0253 void KCookiesPolicies::deleteAllPressed() 0254 { 0255 mDomainPolicyMap.clear(); 0256 mUi.policyTreeWidget->clear(); 0257 updateButtons(); 0258 configChanged(); 0259 } 0260 0261 void KCookiesPolicies::updateButtons() 0262 { 0263 bool hasItems = mUi.policyTreeWidget->topLevelItemCount() > 0; 0264 0265 mUi.pbChange->setEnabled((hasItems && mSelectedItemsCount == 1)); 0266 mUi.pbDelete->setEnabled((hasItems && mSelectedItemsCount > 0)); 0267 mUi.pbDeleteAll->setEnabled(hasItems); 0268 } 0269 0270 void KCookiesPolicies::updateDomainList(const QStringList &domainConfig) 0271 { 0272 mUi.policyTreeWidget->clear(); 0273 0274 QStringList::ConstIterator it = domainConfig.begin(); 0275 for (; it != domainConfig.end(); ++it) { 0276 QString domain; 0277 CookieJar::CookieAdvice advice = CookieJar::CookieAdvice::Unknown; 0278 splitDomainAdvice(*it, domain, advice); 0279 if (!domain.isEmpty()) { 0280 const QStringList items{ 0281 tolerantFromAce(domain.toLatin1()), 0282 i18n(KCookieAdvice::adviceToStr(advice)), 0283 }; 0284 QTreeWidgetItem *item = new QTreeWidgetItem(mUi.policyTreeWidget, items); 0285 mDomainPolicyMap[item->text(0)] = advice; 0286 } 0287 } 0288 0289 mUi.policyTreeWidget->sortItems(0, Qt::AscendingOrder); 0290 } 0291 0292 void KCookiesPolicies::selectionChanged() 0293 { 0294 mSelectedItemsCount = mUi.policyTreeWidget->selectedItems().count(); 0295 updateButtons(); 0296 } 0297 0298 void KCookiesPolicies::load() 0299 { 0300 mSelectedItemsCount = 0; 0301 0302 KSharedConfig::Ptr cfg = KSharedConfig::openConfig(); 0303 KConfigGroup group = cfg->group("Cookie Policy"); 0304 0305 bool enableCookies = group.readEntry("Cookies", true); 0306 mUi.cbEnableCookies->setChecked(enableCookies); 0307 cookiesEnabled(enableCookies); 0308 0309 CookieJar::CookieAdvice advice = CookieJar::readAdviceConfigEntry(group, "CookieGlobalAdvice"); 0310 switch (advice) { 0311 case CookieJar::CookieAdvice::Accept: 0312 mUi.rbPolicyAccept->setChecked(true); 0313 break; 0314 case CookieJar::CookieAdvice::AcceptForSession: 0315 mUi.rbPolicyAcceptForSession->setChecked(true); 0316 break; 0317 case CookieJar::CookieAdvice::Reject: 0318 mUi.rbPolicyReject->setChecked(true); 0319 break; 0320 case CookieJar::CookieAdvice::Ask: 0321 case CookieJar::CookieAdvice::Unknown: 0322 default: 0323 mUi.rbPolicyAsk->setChecked(true); 0324 } 0325 0326 bool enable = group.readEntry("RejectCrossDomainCookies", true); 0327 mUi.cbRejectCrossDomainCookies->setChecked(enable); 0328 0329 bool sessionCookies = group.readEntry("AcceptSessionCookies", true); 0330 mUi.cbAutoAcceptSessionCookies->setChecked(sessionCookies); 0331 updateDomainList(group.readEntry("CookieDomainAdvice", QStringList())); 0332 0333 if (enableCookies) { 0334 updateButtons(); 0335 } 0336 } 0337 0338 void KCookiesPolicies::save() 0339 { 0340 KSharedConfig::Ptr cfg = KSharedConfig::openConfig(); 0341 KConfigGroup group = cfg->group("Cookie Policy"); 0342 0343 bool state = mUi.cbEnableCookies->isChecked(); 0344 group.writeEntry("Cookies", state); 0345 state = mUi.cbRejectCrossDomainCookies->isChecked(); 0346 group.writeEntry("RejectCrossDomainCookies", state); 0347 state = mUi.cbAutoAcceptSessionCookies->isChecked(); 0348 group.writeEntry("AcceptSessionCookies", state); 0349 0350 CookieJar::CookieAdvice advice; 0351 if (mUi.rbPolicyAccept->isChecked()) { 0352 advice = CookieJar::CookieAdvice::Accept; 0353 } else if (mUi.rbPolicyAcceptForSession->isChecked()) { 0354 advice = CookieJar::CookieAdvice::AcceptForSession; 0355 } else if (mUi.rbPolicyReject->isChecked()) { 0356 advice = CookieJar::CookieAdvice::Reject; 0357 } else { 0358 advice = CookieJar::CookieAdvice::Ask; 0359 } 0360 0361 CookieJar::writeAdviceConfigEntry(group, "CookieGlobalAdvice", advice); 0362 0363 QStringList domainConfig; 0364 QJsonObject obj; 0365 for (auto it = mDomainPolicyMap.constBegin(); it != mDomainPolicyMap.constEnd(); ++it) { 0366 obj.insert(it.key(), CookieJar::adviceToInt(it.value())); 0367 } 0368 group.writeEntry("CookieDomainAdvice", QJsonDocument(obj).toJson()); 0369 0370 group.sync(); 0371 0372 QDBusMessage message = 0373 QDBusMessage::createSignal(QStringLiteral("/KonqMain"), QStringLiteral("org.kde.Konqueror.Main"), QStringLiteral("reparseConfiguration")); 0374 QDBusConnection::sessionBus().send(message); 0375 setNeedsSave(false); 0376 } 0377 0378 void KCookiesPolicies::defaults() 0379 { 0380 mUi.cbEnableCookies->setChecked(true); 0381 mUi.rbPolicyAsk->setChecked(true); 0382 mUi.rbPolicyAccept->setChecked(false); 0383 mUi.rbPolicyAcceptForSession->setChecked(false); 0384 mUi.rbPolicyReject->setChecked(false); 0385 mUi.cbRejectCrossDomainCookies->setChecked(true); 0386 mUi.cbAutoAcceptSessionCookies->setChecked(false); 0387 mUi.policyTreeWidget->clear(); 0388 mDomainPolicyMap.clear(); 0389 0390 cookiesEnabled(mUi.cbEnableCookies->isChecked()); 0391 updateButtons(); 0392 } 0393 0394 void KCookiesPolicies::splitDomainAdvice(const QString &cfg, QString &domain, CookieJar::CookieAdvice &advice) 0395 { 0396 int sepPos = cfg.lastIndexOf(QLatin1Char(':')); 0397 0398 // Ignore any policy that does not contain a domain... 0399 if (sepPos <= 0) { 0400 return; 0401 } 0402 0403 domain = cfg.left(sepPos); 0404 advice = KCookieAdvice::strToAdvice(cfg.mid(sepPos + 1)); 0405 }