File indexing completed on 2024-12-22 04:43:19
0001 /* 0002 This file is part of the KDE project. 0003 0004 SPDX-FileCopyrightText: 2018 Stefano Crocco <stefano.crocco@alice.it> 0005 0006 SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include "webenginepartcookiejar6.h" 0010 #include "settings/webenginesettings.h" 0011 #include <webenginepart_debug.h> 0012 #include "cookiealertdlg.h" 0013 #include "interfaces/browser.h" 0014 0015 #include <QWebEngineProfile> 0016 #include <QStringList> 0017 #include <QDBusReply> 0018 #include <QDebug> 0019 #include <QWidget> 0020 #include <QDateTime> 0021 #include <QTimeZone> 0022 #include <QApplication> 0023 #include <QJsonDocument> 0024 #include <QJsonObject> 0025 #include <QFile> 0026 #include <QDataStream> 0027 #include <QDir> 0028 #include <QNetworkCookie> 0029 0030 #include <kio_version.h> 0031 #include <KSharedConfig> 0032 #include <KConfigGroup> 0033 0034 #include <QTimer> 0035 0036 WebEnginePartCookieJar6::CookieIdentifier::CookieIdentifier(const QNetworkCookie& cookie): 0037 name(cookie.name()), domain(cookie.domain()), path(cookie.path()) 0038 { 0039 } 0040 0041 WebEnginePartCookieJar6::CookieIdentifier::CookieIdentifier(const QString& n, const QString& d, const QString& p): 0042 name(n), domain(d), path(p) 0043 { 0044 } 0045 0046 WebEnginePartCookieJar6::WebEnginePartCookieJar6(QWebEngineProfile *prof, QObject *parent): 0047 CookieJar(parent), m_cookieStore(prof->cookieStore()) 0048 { 0049 auto filter = [this](const QWebEngineCookieStore::FilterRequest &req){return filterCookie(req);}; 0050 m_cookieStore->setCookieFilter(filter); 0051 0052 connect(m_cookieStore, &QWebEngineCookieStore::cookieAdded, this, &WebEnginePartCookieJar6::handleCookieAdditionToStore); 0053 connect(m_cookieStore, &QWebEngineCookieStore::cookieRemoved, this, &WebEnginePartCookieJar6::removeCookieFromSet); 0054 connect(qApp, &QApplication::lastWindowClosed, this, &WebEnginePartCookieJar6::saveCookieAdvice); 0055 KonqInterfaces::Browser *br = KonqInterfaces::Browser::browser(qApp); 0056 if (br) { 0057 connect(br, &KonqInterfaces::Browser::configurationChanged, this, &WebEnginePartCookieJar6::applyConfiguration); 0058 } 0059 0060 //WARNING: call this *before* applyConfiguration(), otherwise, if the default policy is Ask, the user will be asked again for all cookies 0061 //already in the jar 0062 readCookieAdvice(); 0063 loadCookies(); 0064 m_cookieStore->loadAllCookies(); 0065 applyConfiguration(); 0066 } 0067 0068 WebEnginePartCookieJar6::~WebEnginePartCookieJar6() 0069 { 0070 QFile f(cookieDataPath()); 0071 if (!f.open(QFile::WriteOnly)) { 0072 return; 0073 } 0074 #if QT_VERSION_MAJOR > 5 0075 m_cookies.removeIf([](const QNetworkCookie &c){return !c.expirationDate().isValid();}); 0076 #else 0077 const QSet<QNetworkCookie> orig (m_cookies); 0078 for (const QNetworkCookie &c : orig) { 0079 if (!c.expirationDate().isValid()) { 0080 m_cookies.remove(c); 0081 } 0082 } 0083 #endif 0084 QDataStream ds(&f); 0085 ds << m_cookies; 0086 f.close(); 0087 } 0088 0089 QSet<QNetworkCookie> WebEnginePartCookieJar6::cookies() const 0090 { 0091 return m_cookies; 0092 } 0093 0094 void WebEnginePartCookieJar6::writeConfig() 0095 { 0096 KSharedConfig::Ptr cfg = KSharedConfig::openConfig(); 0097 KConfigGroup grp = cfg->group("Cookie Policy"); 0098 writeAdviceConfigEntry(grp, "CookieGlobalAdvice", m_policy.defaultPolicy); 0099 QJsonObject obj; 0100 for (auto it = m_policy.domainExceptions.constBegin(); it != m_policy.domainExceptions.constEnd(); ++it) { 0101 obj.insert(it.key(), adviceToInt(it.value())); 0102 } 0103 grp.writeEntry("CookieDomainAdvice", QJsonDocument(obj).toJson()); 0104 cfg->sync(); 0105 } 0106 0107 void WebEnginePartCookieJar6::applyConfiguration() 0108 { 0109 KConfigGroup grp = KSharedConfig::openConfig()->group("Cookie Policy"); 0110 m_policy.cookiesEnabled = grp.readEntry("Cookies", true); 0111 m_policy.rejectThirdPartyCookies = grp.readEntry("RejectCrossDomainCookies", true); 0112 m_policy.acceptSessionCookies = grp.readEntry("AcceptSessionCookies", true); 0113 m_policy.defaultPolicy = readAdviceConfigEntry(grp, "CookieGlobalAdvice", CookieAdvice::Accept); 0114 QJsonObject obj = QJsonDocument::fromJson(grp.readEntry("CookieDomainAdvice", QByteArray())).object(); 0115 for (auto it = obj.constBegin(); it != obj.constEnd(); ++it) { 0116 m_policy.domainExceptions.insert(it.key(), intToAdvice(it.value().toInt(0), CookieAdvice::Unknown)); 0117 } 0118 if (!m_policy.cookiesEnabled) { 0119 m_cookieStore->setCookieFilter([](const QWebEngineCookieStore::FilterRequest &){return false;}); 0120 m_cookieStore->deleteAllCookies(); 0121 } 0122 } 0123 0124 void WebEnginePartCookieJar6::removeAllCookies() 0125 { 0126 m_cookieStore->deleteAllCookies(); 0127 m_cookies.clear(); 0128 QFile::remove(cookieAdvicePath()); 0129 } 0130 0131 void WebEnginePartCookieJar6::removeCookiesWithDomain(const QString& domain) 0132 { 0133 QStringList possibleDomains{domain}; 0134 if (domain.startsWith('.')) { 0135 possibleDomains.append(domain.mid(1)); 0136 } else { 0137 possibleDomains.append(QString('.') + domain); 0138 } 0139 0140 bool exceptionsRemoved = false; 0141 QSet<QNetworkCookie> cookiesOrig(m_cookies); 0142 for (auto c : cookiesOrig) { 0143 if (possibleDomains.contains(c.domain())) { 0144 m_cookieStore->deleteCookie(c); 0145 if (m_policy.cookieExceptions.remove(CookieIdentifier{c}) ) { 0146 exceptionsRemoved = true; 0147 } 0148 } 0149 } 0150 if (exceptionsRemoved) { 0151 saveCookieAdvice(); 0152 } 0153 } 0154 0155 void WebEnginePartCookieJar6::removeCookies(const QVector<QNetworkCookie>& cookies) 0156 { 0157 bool exceptionsRemoved = false; 0158 for (const QNetworkCookie &c : cookies) { 0159 m_cookieStore->deleteCookie(c); 0160 if (m_policy.cookieExceptions.remove(CookieIdentifier{c}) > 0) { 0161 exceptionsRemoved = true; 0162 } 0163 } 0164 if (exceptionsRemoved) { 0165 saveCookieAdvice(); 0166 } 0167 } 0168 0169 void WebEnginePartCookieJar6::removeCookie(const QNetworkCookie& cookie, const QUrl &origin) 0170 { 0171 m_cookieStore->deleteCookie(cookie, origin); 0172 if (m_policy.cookieExceptions.remove(CookieIdentifier{cookie}) > 0) { 0173 saveCookieAdvice(); 0174 } 0175 } 0176 0177 bool WebEnginePartCookieJar6::filterCookie(const QWebEngineCookieStore::FilterRequest& req) 0178 { 0179 if (!m_policy.cookiesEnabled) { 0180 return false; 0181 } 0182 if (req.thirdParty && m_policy.rejectThirdPartyCookies) { 0183 return false; 0184 } 0185 return true; 0186 } 0187 0188 void WebEnginePartCookieJar6::removeSessionCookies() 0189 { 0190 for (const QNetworkCookie &c : m_cookies) { 0191 if (!c.expirationDate().isValid()) { 0192 m_cookieStore->deleteCookie(c); 0193 } 0194 } 0195 } 0196 0197 WebEnginePartCookieJar6::CookieAdvice WebEnginePartCookieJar6::decideCookieAction(const QNetworkCookie cookie) 0198 { 0199 CookieAdvice option = CookieAdvice::Unknown; 0200 auto cookiesExIt = m_policy.cookieExceptions.constFind(CookieIdentifier{cookie.name(), cookie.domain(), cookie.path()}); 0201 if (cookiesExIt != m_policy.cookieExceptions.constEnd()) { 0202 option = cookiesExIt.value(); 0203 } 0204 0205 if (option == CookieAdvice::Unknown && m_policy.acceptSessionCookies && !cookie.expirationDate().isValid()) { 0206 return CookieAdvice::Accept; 0207 } 0208 0209 auto domainExIt = m_policy.domainExceptions.constFind(cookie.domain()); 0210 if (domainExIt != m_policy.domainExceptions.constEnd()) { 0211 option = domainExIt.value(); 0212 } 0213 0214 if (option == CookieAdvice::Unknown) { 0215 option = m_policy.defaultPolicy != CookieAdvice::Unknown ? m_policy.defaultPolicy : CookieAdvice::Accept; 0216 } 0217 0218 if (option == CookieAdvice::Ask) { 0219 option = askCookieQuestion(cookie); 0220 } 0221 0222 return option; 0223 } 0224 0225 WebEnginePartCookieJar6::CookieAdvice WebEnginePartCookieJar6::askCookieQuestion(const QNetworkCookie cookie) 0226 { 0227 CookieAlertDlg dlg(cookie, qApp->activeWindow()); 0228 dlg.exec(); 0229 CookieAdvice option = dlg.choice(); 0230 switch (dlg.applyTo()) { 0231 case CookieAlertDlg::This: 0232 m_policy.cookieExceptions.insert(CookieIdentifier(cookie), option); 0233 break; 0234 case CookieAlertDlg::Domain: 0235 m_policy.domainExceptions.insert(cookie.domain(), option); 0236 break; 0237 case CookieAlertDlg::Cookies: 0238 m_policy.defaultPolicy = option; 0239 break; 0240 } 0241 writeConfig(); 0242 return option; 0243 } 0244 0245 void WebEnginePartCookieJar6::handleCookieAdditionToStore(const QNetworkCookie& cookie) 0246 { 0247 CookieAdvice action = decideCookieAction(cookie); 0248 if (action == CookieAdvice::Reject) { 0249 m_cookieStore->deleteCookie(cookie); 0250 return; 0251 } else if (action == CookieAdvice::AcceptForSession && cookie.expirationDate().isValid()) { 0252 QNetworkCookie sessionCookie(cookie); 0253 sessionCookie.setExpirationDate(QDateTime()); 0254 m_cookieStore->deleteCookie(cookie); 0255 m_cookieStore->setCookie(sessionCookie); 0256 return; 0257 } 0258 m_cookies.insert(cookie); 0259 } 0260 0261 QString WebEnginePartCookieJar6::cookieAdvicePath() 0262 { 0263 QString s_cookieAdvicePath; 0264 if (s_cookieAdvicePath.isEmpty()) { 0265 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); 0266 dir.mkpath(QStringLiteral(".")); 0267 s_cookieAdvicePath = dir.filePath(QStringLiteral("cookieadvice")); 0268 } 0269 return s_cookieAdvicePath; 0270 } 0271 0272 QString WebEnginePartCookieJar6::cookieDataPath() 0273 { 0274 QString s_cookieDataPath; 0275 if (s_cookieDataPath.isEmpty()) { 0276 QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); 0277 dir.mkpath(QStringLiteral(".")); 0278 s_cookieDataPath = dir.filePath(QStringLiteral("cookies")); 0279 } 0280 return s_cookieDataPath; 0281 } 0282 void WebEnginePartCookieJar6::saveCookieAdvice() 0283 { 0284 QFile f(cookieAdvicePath()); 0285 if (!f.open(QFile::WriteOnly)) { 0286 return; 0287 } 0288 QDataStream ds(&f); 0289 ds << m_policy.cookieExceptions; 0290 } 0291 0292 void WebEnginePartCookieJar6::readCookieAdvice() 0293 { 0294 QFile f(cookieAdvicePath()); 0295 if (!f.open(QFile::ReadOnly)) { 0296 return; 0297 } 0298 QDataStream ds(&f); 0299 ds >> m_policy.cookieExceptions; 0300 } 0301 0302 void WebEnginePartCookieJar6::removeCookieFromSet(const QNetworkCookie& cookie) 0303 { 0304 m_cookies.remove(cookie); 0305 } 0306 0307 void WebEnginePartCookieJar6::loadCookies() 0308 { 0309 QFile f(cookieDataPath()); 0310 if (!f.open(QFile::ReadOnly)) { 0311 return; 0312 } 0313 QDataStream ds(&f); 0314 ds >> m_cookies; 0315 f.close(); 0316 } 0317 0318 QDebug operator<<(QDebug deb, const WebEnginePartCookieJar6::CookieIdentifier& id) 0319 { 0320 QDebugStateSaver saver(deb); 0321 deb << "(" << id.name << "," << id.domain << "," << id.path << ")"; 0322 return deb; 0323 } 0324 0325 qHashReturnType qHash(const QNetworkCookie& cookie, uint seed) 0326 { 0327 return qHash(QStringList{cookie.name(), cookie.domain(), cookie.path()}, seed); 0328 } 0329 0330 QDataStream& operator>>(QDataStream& ds, WebEnginePartCookieJar6::CookieIdentifier& id) 0331 { 0332 ds >> id.name >> id.domain >> id.path; 0333 return ds; 0334 } 0335 0336 QDataStream& operator<<(QDataStream& ds, const WebEnginePartCookieJar6::CookieIdentifier& id) 0337 { 0338 ds << id.name << id.domain << id.path; 0339 return ds; 0340 } 0341 0342 QDataStream& operator>>(QDataStream& ds, QNetworkCookie& cookie) 0343 { 0344 QByteArray name, value; 0345 QString domain, path; 0346 QDateTime expirationDate; 0347 bool secure; 0348 ds >> name >> value >> domain >> path >> expirationDate >> secure; 0349 cookie.setName(name); 0350 cookie.setValue(value); 0351 cookie.setDomain(domain); 0352 cookie.setPath(path); 0353 cookie.setExpirationDate(expirationDate); 0354 cookie.setSecure(secure); 0355 return ds; 0356 } 0357 0358 QDataStream& operator<<(QDataStream& ds, const QNetworkCookie& cookie) 0359 { 0360 ds << cookie.name() << cookie.value() << cookie.domain() << cookie.path() << cookie.expirationDate() << cookie.isSecure(); 0361 return ds; 0362 } 0363