1 /* ============================================================
2 * Falkon - Qt web browser
3 * Copyright (C) 2010-2017 David Rosca <nowrep@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 * ============================================================ */
18 #include "cookiejar.h"
19 #include "mainapplication.h"
20 #include "datapaths.h"
21 #include "autosaver.h"
22 #include "settings.h"
23 #include "qztools.h"
24 
25 #include <QNetworkCookie>
26 #include <QWebEngineProfile>
27 #include <QWebEngineSettings>
28 #include <QDateTime>
29 
30 //#define COOKIE_DEBUG
31 
CookieJar(QObject * parent)32 CookieJar::CookieJar(QObject* parent)
33     : QObject(parent)
34     , m_client(mApp->webProfile()->cookieStore())
35 {
36     loadSettings();
37     m_client->loadAllCookies();
38 
39 #if QTWEBENGINEWIDGETS_VERSION >= QT_VERSION_CHECK(5, 11, 0)
40     m_client->setCookieFilter(std::bind(&CookieJar::cookieFilter, this, std::placeholders::_1));
41 #endif
42 
43     connect(m_client, &QWebEngineCookieStore::cookieAdded, this, &CookieJar::slotCookieAdded);
44     connect(m_client, &QWebEngineCookieStore::cookieRemoved, this, &CookieJar::slotCookieRemoved);
45 }
46 
~CookieJar()47 CookieJar::~CookieJar()
48 {
49 #if QTWEBENGINEWIDGETS_VERSION >= QT_VERSION_CHECK(5, 11, 0)
50     m_client->setCookieFilter(nullptr);
51 #endif
52 }
53 
loadSettings()54 void CookieJar::loadSettings()
55 {
56     Settings settings;
57     settings.beginGroup("Cookie-Settings");
58     m_allowCookies = settings.value("allowCookies", true).toBool();
59     m_filterThirdParty = settings.value("filterThirdPartyCookies", false).toBool();
60     m_filterTrackingCookie = settings.value("filterTrackingCookie", false).toBool();
61     m_whitelist = settings.value("whitelist", QStringList()).toStringList();
62     m_blacklist = settings.value("blacklist", QStringList()).toStringList();
63     settings.endGroup();
64 }
65 
setAllowCookies(bool allow)66 void CookieJar::setAllowCookies(bool allow)
67 {
68     m_allowCookies = allow;
69 }
70 
deleteCookie(const QNetworkCookie & cookie)71 void CookieJar::deleteCookie(const QNetworkCookie &cookie)
72 {
73     m_client->deleteCookie(cookie);
74 }
75 
getAllCookies() const76 QVector<QNetworkCookie> CookieJar::getAllCookies() const
77 {
78     return m_cookies;
79 }
80 
deleteAllCookies(bool deleteAll)81 void CookieJar::deleteAllCookies(bool deleteAll)
82 {
83     if (deleteAll || m_whitelist.isEmpty()) {
84         m_client->deleteAllCookies();
85         return;
86     }
87 
88     for (const QNetworkCookie &cookie : qAsConst(m_cookies)) {
89         if (!listMatchesDomain(m_whitelist, cookie.domain())) {
90             m_client->deleteCookie(cookie);
91         }
92     }
93 }
94 
matchDomain(QString cookieDomain,QString siteDomain) const95 bool CookieJar::matchDomain(QString cookieDomain, QString siteDomain) const
96 {
97     // According to RFC 6265
98 
99     // Remove leading dot
100     if (cookieDomain.startsWith(QLatin1Char('.'))) {
101         cookieDomain.remove(0, 1);
102     }
103 
104     if (siteDomain.startsWith(QLatin1Char('.'))) {
105         siteDomain.remove(0, 1);
106     }
107 
108     return QzTools::matchDomain(cookieDomain, siteDomain);
109 }
110 
listMatchesDomain(const QStringList & list,const QString & cookieDomain) const111 bool CookieJar::listMatchesDomain(const QStringList &list, const QString &cookieDomain) const
112 {
113     for (const QString &d : list) {
114         if (matchDomain(d, cookieDomain)) {
115             return true;
116         }
117     }
118 
119     return false;
120 }
121 
slotCookieAdded(const QNetworkCookie & cookie)122 void CookieJar::slotCookieAdded(const QNetworkCookie &cookie)
123 {
124     if (rejectCookie(QString(), cookie, cookie.domain())) {
125         m_client->deleteCookie(cookie);
126         return;
127     }
128 
129     m_cookies.append(cookie);
130     emit cookieAdded(cookie);
131 }
132 
slotCookieRemoved(const QNetworkCookie & cookie)133 void CookieJar::slotCookieRemoved(const QNetworkCookie &cookie)
134 {
135     if (m_cookies.removeOne(cookie))
136         emit cookieRemoved(cookie);
137 }
138 
139 #if QTWEBENGINEWIDGETS_VERSION >= QT_VERSION_CHECK(5, 11, 0)
cookieFilter(const QWebEngineCookieStore::FilterRequest & request) const140 bool CookieJar::cookieFilter(const QWebEngineCookieStore::FilterRequest &request) const
141 {
142     if (!m_allowCookies) {
143         bool result = listMatchesDomain(m_whitelist, request.origin.host());
144         if (!result) {
145 #ifdef COOKIE_DEBUG
146             qDebug() << "not in whitelist" << request.origin;
147 #endif
148             return false;
149         }
150     }
151 
152     if (m_allowCookies) {
153         bool result = listMatchesDomain(m_blacklist, request.origin.host());
154         if (result) {
155 #ifdef COOKIE_DEBUG
156             qDebug() << "found in blacklist" << request.origin.host();
157 #endif
158             return false;
159         }
160     }
161 
162     if (m_filterThirdParty && request.thirdParty) {
163 #ifdef COOKIE_DEBUG
164         qDebug() << "thirdParty" << request.firstPartyUrl << request.origin;
165 #endif
166         return false;
167     }
168 
169     return true;
170 }
171 #endif
172 
rejectCookie(const QString & domain,const QNetworkCookie & cookie,const QString & cookieDomain) const173 bool CookieJar::rejectCookie(const QString &domain, const QNetworkCookie &cookie, const QString &cookieDomain) const
174 {
175     Q_UNUSED(domain)
176 
177     if (!m_allowCookies) {
178         bool result = listMatchesDomain(m_whitelist, cookieDomain);
179         if (!result) {
180 #ifdef COOKIE_DEBUG
181             qDebug() << "not in whitelist" << cookie;
182 #endif
183             return true;
184         }
185     }
186 
187     if (m_allowCookies) {
188         bool result = listMatchesDomain(m_blacklist, cookieDomain);
189         if (result) {
190 #ifdef COOKIE_DEBUG
191             qDebug() << "found in blacklist" << cookie;
192 #endif
193             return true;
194         }
195     }
196 
197 #ifdef QTWEBENGINE_DISABLED
198     if (m_filterThirdParty) {
199         bool result = matchDomain(cookieDomain, domain);
200         if (!result) {
201 #ifdef COOKIE_DEBUG
202             qDebug() << "purged for domain mismatch" << cookie << cookieDomain << domain;
203 #endif
204             return true;
205         }
206     }
207 #endif
208 
209     if (m_filterTrackingCookie && cookie.name().startsWith("__utm")) {
210 #ifdef COOKIE_DEBUG
211         qDebug() << "purged as tracking " << cookie;
212 #endif
213         return true;
214     }
215 
216     return false;
217 }
218