1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2016 - 2019 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 * Copyright (C) 2016 Jan Bajer aka bajasoft <jbajer@gmail.com>
5 *
6 * This program 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.
10 *
11 * This program 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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **************************************************************************/
20 
21 #include "QtWebEngineUrlRequestInterceptor.h"
22 #include "QtWebEngineWebWidget.h"
23 #include "../../../../core/Console.h"
24 #include "../../../../core/ContentFiltersManager.h"
25 #include "../../../../core/SettingsManager.h"
26 #include "../../../../core/Utils.h"
27 
28 #include <QtCore/QCoreApplication>
29 
30 namespace Otter
31 {
32 
33 #if QTWEBENGINECORE_VERSION >= 0x050D00
QtWebEngineUrlRequestInterceptor(QtWebEngineWebWidget * parent)34 QtWebEngineUrlRequestInterceptor::QtWebEngineUrlRequestInterceptor(QtWebEngineWebWidget *parent) : QWebEngineUrlRequestInterceptor(parent),
35 	m_widget(parent),
36 	m_doNotTrackPolicy(NetworkManagerFactory::SkipTrackPolicy),
37 	m_areImagesEnabled(true),
38 	m_canSendReferrer(true)
39 {
40 }
41 
interceptRequest(QWebEngineUrlRequestInfo & request)42 void QtWebEngineUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &request)
43 {
44 	if (!m_areImagesEnabled && request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeImage)
45 	{
46 		request.block(true);
47 
48 		return;
49 	}
50 
51 	if (!m_contentBlockingProfiles.isEmpty() && (m_unblockedHosts.isEmpty() || !m_unblockedHosts.contains(Utils::extractHost(request.firstPartyUrl()))))
52 	{
53 		NetworkManager::ResourceType resourceType(NetworkManager::OtherType);
54 		bool storeBlockedUrl(true);
55 
56 		switch (request.resourceType())
57 		{
58 			case QWebEngineUrlRequestInfo::ResourceTypeMainFrame:
59 				resourceType = NetworkManager::MainFrameType;
60 
61 				break;
62 			case QWebEngineUrlRequestInfo::ResourceTypeSubFrame:
63 				resourceType = NetworkManager::SubFrameType;
64 
65 				break;
66 			case QWebEngineUrlRequestInfo::ResourceTypeStylesheet:
67 				resourceType = NetworkManager::StyleSheetType;
68 				storeBlockedUrl = false;
69 
70 				break;
71 			case QWebEngineUrlRequestInfo::ResourceTypeScript:
72 				resourceType = NetworkManager::ScriptType;
73 				storeBlockedUrl = false;
74 
75 				break;
76 			case QWebEngineUrlRequestInfo::ResourceTypeImage:
77 				resourceType = NetworkManager::ImageType;
78 
79 				break;
80 			case QWebEngineUrlRequestInfo::ResourceTypeObject:
81 			case QWebEngineUrlRequestInfo::ResourceTypeMedia:
82 				resourceType = NetworkManager::ObjectType;
83 
84 				break;
85 			case QWebEngineUrlRequestInfo::ResourceTypePluginResource:
86 				resourceType = NetworkManager::ObjectSubrequestType;
87 				storeBlockedUrl = false;
88 
89 				break;
90 			case QWebEngineUrlRequestInfo::ResourceTypeXhr:
91 				resourceType = NetworkManager::XmlHttpRequestType;
92 
93 				break;
94 			default:
95 				break;
96 		}
97 
98 		const ContentFiltersManager::CheckResult result(ContentFiltersManager::checkUrl(m_contentBlockingProfiles, request.firstPartyUrl(), request.requestUrl(), resourceType));
99 
100 		if (result.isBlocked)
101 		{
102 			const ContentFiltersProfile *profile(ContentFiltersManager::getProfile(result.profile));
103 
104 			Console::addMessage(QCoreApplication::translate("main", "Request blocked by rule from profile %1:\n%2").arg(profile ? profile->getTitle() : QCoreApplication::translate("main", "(Unknown)")).arg(result.rule), Console::NetworkCategory, Console::LogLevel, request.requestUrl().toString(), -1);
105 
106 			if (storeBlockedUrl && !m_blockedElements.contains(request.requestUrl().url()))
107 			{
108 				m_blockedElements.append(request.requestUrl().url());
109 			}
110 
111 			NetworkManager::ResourceInformation resource;
112 			resource.url = request.requestUrl();
113 			resource.resourceType = resourceType;
114 			resource.metaData[NetworkManager::ContentBlockingProfileMetaData] = result.profile;
115 			resource.metaData[NetworkManager::ContentBlockingRuleMetaData] = result.rule;
116 
117 			m_blockedRequests.append(resource);
118 
119 			emit requestBlocked(resource);
120 
121 			request.block(true);
122 
123 			return;
124 		}
125 	}
126 
127 	if (m_doNotTrackPolicy != NetworkManagerFactory::SkipTrackPolicy)
128 	{
129 		request.setHttpHeader(QStringLiteral("DNT").toLatin1(), ((m_doNotTrackPolicy == NetworkManagerFactory::DoNotAllowToTrackPolicy) ? QStringLiteral("1") : QStringLiteral("0")).toLatin1());
130 	}
131 
132 	if (!m_canSendReferrer)
133 	{
134 		request.setHttpHeader(QStringLiteral("Referer").toLatin1(), QByteArray());
135 	}
136 }
137 
updateOptions(const QUrl & url)138 void QtWebEngineUrlRequestInterceptor::updateOptions(const QUrl &url)
139 {
140 	m_blockedRequests.clear();
141 	m_blockedElements.clear();
142 
143 	if (getOption(SettingsManager::ContentBlocking_EnableContentBlockingOption, url).toBool())
144 	{
145 		m_contentBlockingProfiles = ContentFiltersManager::getProfileIdentifiers(getOption(SettingsManager::ContentBlocking_ProfilesOption, url).toStringList());
146 	}
147 	else
148 	{
149 		m_contentBlockingProfiles.clear();
150 	}
151 
152 	m_unblockedHosts = getOption(SettingsManager::ContentBlocking_IgnoreHostsOption, url).toStringList();
153 
154 	const QString doNotTrackPolicyValue(getOption(SettingsManager::Network_DoNotTrackPolicyOption, url).toString());
155 
156 	if (doNotTrackPolicyValue == QLatin1String("allow"))
157 	{
158 		m_doNotTrackPolicy = NetworkManagerFactory::AllowToTrackPolicy;
159 	}
160 	else if (doNotTrackPolicyValue == QLatin1String("doNotAllow"))
161 	{
162 		m_doNotTrackPolicy = NetworkManagerFactory::DoNotAllowToTrackPolicy;
163 	}
164 	else
165 	{
166 		m_doNotTrackPolicy = NetworkManagerFactory::SkipTrackPolicy;
167 	}
168 
169 	m_areImagesEnabled = (getOption(SettingsManager::Permissions_EnableImagesOption, url).toString() != QLatin1String("disabled"));
170 	m_canSendReferrer = getOption(SettingsManager::Network_EnableReferrerOption, url).toBool();
171 }
172 
getOption(int identifier,const QUrl & url) const173 QVariant QtWebEngineUrlRequestInterceptor::getOption(int identifier, const QUrl &url) const
174 {
175 	return (m_widget ? m_widget->getOption(identifier, url) : SettingsManager::getOption(identifier, Utils::extractHost(url)));
176 }
177 
getBlockedElements() const178 QStringList QtWebEngineUrlRequestInterceptor::getBlockedElements() const
179 {
180 	return m_blockedElements;
181 }
182 
getBlockedRequests() const183 QVector<NetworkManager::ResourceInformation> QtWebEngineUrlRequestInterceptor::getBlockedRequests() const
184 {
185 	return m_blockedRequests;
186 }
187 #else
188 QtWebEngineUrlRequestInterceptor::QtWebEngineUrlRequestInterceptor(QObject *parent) : QWebEngineUrlRequestInterceptor(parent),
189 	m_clearTimer(0),
190 	m_areImagesEnabled(SettingsManager::getOption(SettingsManager::Permissions_EnableImagesOption).toString() != QLatin1String("disabled"))
191 {
192 	m_clearTimer = startTimer(1800000);
193 
194 	connect(SettingsManager::getInstance(), &SettingsManager::optionChanged, this, &QtWebEngineUrlRequestInterceptor::handleOptionChanged);
195 	connect(SettingsManager::getInstance(), &SettingsManager::hostOptionChanged, this, &QtWebEngineUrlRequestInterceptor::handleOptionChanged);
196 }
197 
198 void QtWebEngineUrlRequestInterceptor::timerEvent(QTimerEvent *event)
199 {
200 	if (event->timerId() == m_clearTimer)
201 	{
202 		clearContentBlockingInformation();
203 	}
204 }
205 
206 void QtWebEngineUrlRequestInterceptor::clearContentBlockingInformation()
207 {
208 	m_blockedElements.clear();
209 	m_contentBlockingProfiles.clear();
210 }
211 
212 void QtWebEngineUrlRequestInterceptor::handleOptionChanged(int identifier)
213 {
214 	switch (identifier)
215 	{
216 		case SettingsManager::ContentBlocking_ProfilesOption:
217 			clearContentBlockingInformation();
218 
219 			break;
220 		case SettingsManager::Permissions_EnableImagesOption:
221 			m_areImagesEnabled = (SettingsManager::getOption(SettingsManager::Permissions_EnableImagesOption).toString() != QLatin1String("disabled"));
222 
223 			break;
224 		default:
225 			break;
226 	}
227 }
228 
229 QStringList QtWebEngineUrlRequestInterceptor::getBlockedElements(const QString &domain) const
230 {
231 	return m_blockedElements.value(domain);
232 }
233 
234 void QtWebEngineUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &request)
235 {
236 	if (!m_areImagesEnabled && request.resourceType() == QWebEngineUrlRequestInfo::ResourceTypeImage)
237 	{
238 		request.block(true);
239 
240 		return;
241 	}
242 
243 	if (!m_contentBlockingProfiles.contains(request.firstPartyUrl().host()))
244 	{
245 		const QString host(Utils::extractHost(request.firstPartyUrl()));
246 
247 		if (SettingsManager::getOption(SettingsManager::ContentBlocking_EnableContentBlockingOption, host).toBool())
248 		{
249 			m_contentBlockingProfiles[host] = ContentFiltersManager::getProfileIdentifiers(SettingsManager::getOption(SettingsManager::ContentBlocking_ProfilesOption, host).toStringList());
250 		}
251 		else
252 		{
253 			m_contentBlockingProfiles[host] = {};
254 		}
255 	}
256 
257 	const QVector<int> contentBlockingProfiles(m_contentBlockingProfiles.value(request.firstPartyUrl().host()));
258 
259 	if (!contentBlockingProfiles.isEmpty())
260 	{
261 		NetworkManager::ResourceType resourceType(NetworkManager::OtherType);
262 		bool storeBlockedUrl(true);
263 
264 		switch (request.resourceType())
265 		{
266 			case QWebEngineUrlRequestInfo::ResourceTypeMainFrame:
267 				resourceType = NetworkManager::MainFrameType;
268 
269 				break;
270 			case QWebEngineUrlRequestInfo::ResourceTypeSubFrame:
271 				resourceType = NetworkManager::SubFrameType;
272 
273 				break;
274 			case QWebEngineUrlRequestInfo::ResourceTypeStylesheet:
275 				resourceType = NetworkManager::StyleSheetType;
276 				storeBlockedUrl = false;
277 
278 				break;
279 			case QWebEngineUrlRequestInfo::ResourceTypeScript:
280 				resourceType = NetworkManager::ScriptType;
281 				storeBlockedUrl = false;
282 
283 				break;
284 			case QWebEngineUrlRequestInfo::ResourceTypeImage:
285 				resourceType = NetworkManager::ImageType;
286 
287 				break;
288 			case QWebEngineUrlRequestInfo::ResourceTypeObject:
289 			case QWebEngineUrlRequestInfo::ResourceTypeMedia:
290 				resourceType = NetworkManager::ObjectType;
291 
292 				break;
293 			case QWebEngineUrlRequestInfo::ResourceTypePluginResource:
294 				resourceType = NetworkManager::ObjectSubrequestType;
295 				storeBlockedUrl = false;
296 
297 				break;
298 			case QWebEngineUrlRequestInfo::ResourceTypeXhr:
299 				resourceType = NetworkManager::XmlHttpRequestType;
300 
301 				break;
302 			default:
303 				break;
304 		}
305 
306 		const ContentFiltersManager::CheckResult result(ContentFiltersManager::checkUrl(contentBlockingProfiles, request.firstPartyUrl(), request.requestUrl(), resourceType));
307 
308 		if (result.isBlocked)
309 		{
310 			const ContentFiltersProfile *profile(ContentFiltersManager::getProfile(result.profile));
311 
312 			Console::addMessage(QCoreApplication::translate("main", "Request blocked by rule from profile %1:\n%2").arg(profile ? profile->getTitle() : QCoreApplication::translate("main", "(Unknown)")).arg(result.rule), Console::NetworkCategory, Console::LogLevel, request.requestUrl().toString(), -1);
313 
314 			if (storeBlockedUrl && !m_blockedElements.value(request.firstPartyUrl().host()).contains(request.requestUrl().url()))
315 			{
316 				m_blockedElements[request.firstPartyUrl().host()].append(request.requestUrl().url());
317 			}
318 
319 			request.block(true);
320 
321 			return;
322 		}
323 	}
324 
325 	const NetworkManagerFactory::DoNotTrackPolicy doNotTrackPolicy(NetworkManagerFactory::getDoNotTrackPolicy());
326 
327 	if (doNotTrackPolicy != NetworkManagerFactory::SkipTrackPolicy)
328 	{
329 		request.setHttpHeader(QStringLiteral("DNT").toLatin1(), ((doNotTrackPolicy == NetworkManagerFactory::DoNotAllowToTrackPolicy) ? QStringLiteral("1") : QStringLiteral("0")).toLatin1());
330 	}
331 
332 	if (!SettingsManager::getOption(SettingsManager::Network_EnableReferrerOption).toBool())
333 	{
334 		request.setHttpHeader(QStringLiteral("Referer").toLatin1(), QByteArray());
335 	}
336 }
337 #endif
338 
339 }
340