1 /**************************************************************************
2 * Otter Browser: Web browser controlled by the user, not vice-versa.
3 * Copyright (C) 2013 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
4 * Copyright (C) 2014 Piotr Wójcik <chocimier@tlen.pl>
5 * Copyright (C) 2015 - 2017 Jan Bajer aka bajasoft <jbajer@gmail.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 **************************************************************************/
21 
22 #include "QtWebKitNetworkManager.h"
23 #include "QtWebKitCookieJar.h"
24 #include "QtWebKitFtpListingNetworkReply.h"
25 #include "QtWebKitPage.h"
26 #include "../../../../core/AddonsManager.h"
27 #include "../../../../core/Console.h"
28 #include "../../../../core/CookieJar.h"
29 #include "../../../../core/ContentFiltersManager.h"
30 #include "../../../../core/LocalListingNetworkReply.h"
31 #include "../../../../core/NetworkCache.h"
32 #include "../../../../core/NetworkManagerFactory.h"
33 #include "../../../../core/NetworkProxyFactory.h"
34 #include "../../../../core/PasswordsManager.h"
35 #include "../../../../core/SettingsManager.h"
36 #include "../../../../core/ThemesManager.h"
37 #include "../../../../core/WebBackend.h"
38 #include "../../../../ui/AuthenticationDialog.h"
39 #include "../../../../ui/ContentsDialog.h"
40 
41 #include <QtCore/QCoreApplication>
42 #include <QtCore/QFileInfo>
43 #include <QtCore/QJsonArray>
44 #include <QtCore/QJsonDocument>
45 #include <QtCore/QJsonObject>
46 #include <QtCore/QMimeDatabase>
47 #include <QtNetwork/QNetworkProxy>
48 #include <QtNetwork/QNetworkReply>
49 
50 namespace Otter
51 {
52 
53 WebBackend* QtWebKitNetworkManager::m_backend(nullptr);
54 
QtWebKitNetworkManager(bool isPrivate,QtWebKitCookieJar * cookieJarProxy,QtWebKitWebWidget * parent)55 QtWebKitNetworkManager::QtWebKitNetworkManager(bool isPrivate, QtWebKitCookieJar *cookieJarProxy, QtWebKitWebWidget *parent) : QNetworkAccessManager(parent),
56 	m_widget(parent),
57 	m_cookieJar(nullptr),
58 	m_cookieJarProxy(cookieJarProxy),
59 	m_proxyFactory(nullptr),
60 	m_baseReply(nullptr),
61 	m_contentState(WebWidget::UnknownContentState),
62 	m_doNotTrackPolicy(NetworkManagerFactory::SkipTrackPolicy),
63 	m_isSecureValue(UnknownValue),
64 	m_bytesReceivedDifference(0),
65 	m_loadingSpeedTimer(0),
66 	m_areImagesEnabled(true),
67 	m_canSendReferrer(true)
68 {
69 	NetworkManagerFactory::initialize();
70 
71 	if (!isPrivate)
72 	{
73 		m_cookieJar = NetworkManagerFactory::getCookieJar();
74 		m_cookieJar->setParent(QCoreApplication::instance());
75 
76 		QNetworkDiskCache *cache(NetworkManagerFactory::getCache());
77 
78 		setCache(cache);
79 
80 		cache->setParent(QCoreApplication::instance());
81 	}
82 	else
83 	{
84 		m_cookieJar = new CookieJar(true, this);
85 	}
86 
87 	if (m_cookieJarProxy)
88 	{
89 		m_cookieJarProxy->setParent(this);
90 	}
91 	else
92 	{
93 		m_cookieJarProxy = new QtWebKitCookieJar(m_cookieJar, parent);
94 	}
95 
96 	setCookieJar(m_cookieJarProxy);
97 
98 	connect(this, &QtWebKitNetworkManager::finished, this, &QtWebKitNetworkManager::handleRequestFinished);
99 	connect(this, &QtWebKitNetworkManager::authenticationRequired, this, &QtWebKitNetworkManager::handleAuthenticationRequired);
100 	connect(this, &QtWebKitNetworkManager::proxyAuthenticationRequired, this, &QtWebKitNetworkManager::handleProxyAuthenticationRequired);
101 	connect(this, &QtWebKitNetworkManager::sslErrors, this, &QtWebKitNetworkManager::handleSslErrors);
102 	connect(NetworkManagerFactory::getInstance(), &NetworkManagerFactory::onlineStateChanged, this, &QtWebKitNetworkManager::handleOnlineStateChanged);
103 }
104 
timerEvent(QTimerEvent * event)105 void QtWebKitNetworkManager::timerEvent(QTimerEvent *event)
106 {
107 	if (event->timerId() == m_loadingSpeedTimer)
108 	{
109 		updateLoadingSpeed();
110 	}
111 }
112 
addContentBlockingException(const QUrl & url,NetworkManager::ResourceType resourceType)113 void QtWebKitNetworkManager::addContentBlockingException(const QUrl &url, NetworkManager::ResourceType resourceType)
114 {
115 	m_contentBlockingExceptions.insert(url);
116 
117 	if (m_widget && resourceType == NetworkManager::ImageType && m_widget->getOption(SettingsManager::Permissions_EnableImagesOption, m_widget->getUrl()).toString() == QLatin1String("onlyCached"))
118 	{
119 		m_areImagesEnabled = false;
120 	}
121 }
122 
resetStatistics()123 void QtWebKitNetworkManager::resetStatistics()
124 {
125 	killTimer(m_loadingSpeedTimer);
126 
127 	const QList<WebWidget::PageInformation> keys(m_pageInformation.keys());
128 
129 	m_sslInformation = WebWidget::SslInformation();
130 	m_loadingSpeedTimer = 0;
131 	m_blockedElements.clear();
132 	m_contentBlockingProfiles.clear();
133 	m_contentBlockingExceptions.clear();
134 	m_blockedRequests.clear();
135 	m_replies.clear();
136 	m_headers.clear();
137 	m_pageInformation.clear();
138 	m_pageInformation[WebWidget::DocumentBytesReceivedInformation] = quint64(0);
139 	m_pageInformation[WebWidget::DocumentBytesTotalInformation] = quint64(0);
140 	m_pageInformation[WebWidget::TotalBytesReceivedInformation] = quint64(0);
141 	m_pageInformation[WebWidget::TotalBytesTotalInformation] = quint64(0);
142 	m_pageInformation[WebWidget::RequestsFinishedInformation] = 0;
143 	m_pageInformation[WebWidget::RequestsStartedInformation] = 0;
144 	m_baseReply = nullptr;
145 	m_contentState = WebWidget::UnknownContentState;
146 	m_isSecureValue = UnknownValue;
147 	m_bytesReceivedDifference = 0;
148 
149 	updateLoadingSpeed();
150 
151 	for (int i = 0; i < keys.count(); ++i)
152 	{
153 		emit pageInformationChanged(keys.at(i), m_pageInformation.value(keys.at(i)));
154 	}
155 
156 	emit contentStateChanged(m_contentState);
157 }
158 
registerTransfer(QNetworkReply * reply)159 void QtWebKitNetworkManager::registerTransfer(QNetworkReply *reply)
160 {
161 	if (reply && !reply->isFinished())
162 	{
163 		m_transfers.append(reply);
164 
165 		setParent(nullptr);
166 
167 		connect(reply, &QNetworkReply::finished, this, &QtWebKitNetworkManager::handleTransferFinished);
168 	}
169 }
170 
handleDownloadProgress(qint64 bytesReceived,qint64 bytesTotal)171 void QtWebKitNetworkManager::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
172 {
173 	QNetworkReply *reply(qobject_cast<QNetworkReply*>(sender()));
174 
175 	if (reply && reply == m_baseReply)
176 	{
177 		if (bytesTotal == 0 || m_baseReply->hasRawHeader(QStringLiteral("Location").toLatin1()))
178 		{
179 			m_baseReply = nullptr;
180 		}
181 		else
182 		{
183 			setPageInformation(WebWidget::DocumentBytesReceivedInformation, bytesReceived);
184 			setPageInformation(WebWidget::DocumentBytesTotalInformation, bytesTotal);
185 			setPageInformation(WebWidget::DocumentLoadingProgressInformation, ((bytesTotal > 0) ? Utils::calculatePercent(bytesReceived, bytesTotal) : -1));
186 		}
187 	}
188 
189 	if (!reply || !m_replies.contains(reply))
190 	{
191 		return;
192 	}
193 
194 	const QUrl url(reply->url());
195 
196 	if (url.isValid() && url.scheme() != QLatin1String("data"))
197 	{
198 		setPageInformation(WebWidget::LoadingMessageInformation, tr("Receiving data from %1…").arg(Utils::extractHost(reply->url())));
199 	}
200 
201 	const qint64 difference(bytesReceived - m_replies[reply].first);
202 
203 	m_replies[reply].first = bytesReceived;
204 
205 	if (!m_replies[reply].second && bytesTotal > 0)
206 	{
207 		m_replies[reply].second = true;
208 
209 		m_pageInformation[WebWidget::TotalBytesTotalInformation] = (m_pageInformation[WebWidget::TotalBytesTotalInformation].toLongLong() + bytesTotal);
210 	}
211 
212 	if (difference <= 0)
213 	{
214 		return;
215 	}
216 
217 	m_bytesReceivedDifference += difference;
218 
219 	setPageInformation(WebWidget::TotalBytesReceivedInformation, (m_pageInformation[WebWidget::TotalBytesReceivedInformation].toLongLong() + difference));
220 }
221 
handleRequestFinished(QNetworkReply * reply)222 void QtWebKitNetworkManager::handleRequestFinished(QNetworkReply *reply)
223 {
224 	if (!reply || !m_replies.contains(reply))
225 	{
226 		return;
227 	}
228 
229 	const QUrl url(reply->url());
230 
231 	m_replies.remove(reply);
232 
233 	setPageInformation(WebWidget::RequestsFinishedInformation, (m_pageInformation[WebWidget::RequestsFinishedInformation].toInt() + 1));
234 
235 	if (reply == m_baseReply)
236 	{
237 		if (reply->sslConfiguration().isNull())
238 		{
239 			m_sslInformation.certificates = {};
240 			m_sslInformation.cipher = {};
241 		}
242 		else
243 		{
244 			m_sslInformation.certificates = reply->sslConfiguration().peerCertificateChain().toVector();
245 			m_sslInformation.cipher = reply->sslConfiguration().sessionCipher();
246 		}
247 
248 		const QList<QNetworkReply::RawHeaderPair> rawHeaders(m_baseReply->rawHeaderPairs());
249 
250 		for (int i = 0; i < rawHeaders.count(); ++i)
251 		{
252 			m_headers[rawHeaders.at(i).first] = rawHeaders.at(i).second;
253 		}
254 
255 		const QVariant mimeTypeHeader(reply->header(QNetworkRequest::ContentTypeHeader));
256 
257 		if (!mimeTypeHeader.isNull())
258 		{
259 			const QMimeType mimeType(QMimeDatabase().mimeTypeForName(mimeTypeHeader.toString().split(QLatin1Char(';')).first().trimmed()));
260 
261 			if (mimeType.isValid())
262 			{
263 				setPageInformation(WebWidget::DocumentMimeTypeInformation, mimeType.name());
264 			}
265 		}
266 	}
267 
268 	if (url.isValid() && url.scheme() != QLatin1String("data"))
269 	{
270 		setPageInformation(WebWidget::LoadingMessageInformation, tr("Completed request to %1").arg(Utils::extractHost(url)));
271 	}
272 
273 	disconnect(reply, &QNetworkReply::downloadProgress, this, &QtWebKitNetworkManager::handleDownloadProgress);
274 }
275 
handleTransferFinished()276 void QtWebKitNetworkManager::handleTransferFinished()
277 {
278 	QNetworkReply *reply(qobject_cast<QNetworkReply*>(sender()));
279 
280 	if (reply)
281 	{
282 		m_transfers.removeAll(reply);
283 
284 		reply->deleteLater();
285 
286 		if (m_transfers.isEmpty())
287 		{
288 			if (m_widget)
289 			{
290 				setParent(m_widget);
291 			}
292 			else
293 			{
294 				deleteLater();
295 			}
296 		}
297 	}
298 }
299 
handleAuthenticationRequired(QNetworkReply * reply,QAuthenticator * authenticator)300 void QtWebKitNetworkManager::handleAuthenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
301 {
302 	if (!m_widget)
303 	{
304 		return;
305 	}
306 
307 	setPageInformation(WebWidget::LoadingMessageInformation, tr("Waiting for authentication…"));
308 
309 	AuthenticationDialog *authenticationDialog(new AuthenticationDialog(reply->url(), authenticator, AuthenticationDialog::HttpAuthentication, m_widget));
310 	authenticationDialog->setButtonsVisible(false);
311 
312 	ContentsDialog dialog(ThemesManager::createIcon(QLatin1String("dialog-password")), authenticationDialog->windowTitle(), {}, {}, (QDialogButtonBox::Ok | QDialogButtonBox::Cancel), authenticationDialog, m_widget);
313 
314 	connect(&dialog, &ContentsDialog::accepted, authenticationDialog, &AuthenticationDialog::accept);
315 	connect(m_widget, &QtWebKitWebWidget::aboutToReload, &dialog, &ContentsDialog::close);
316 
317 	m_widget->showDialog(&dialog);
318 
319 	NetworkManagerFactory::notifyAuthenticated(authenticator, dialog.isAccepted());
320 }
321 
handleProxyAuthenticationRequired(const QNetworkProxy & proxy,QAuthenticator * authenticator)322 void QtWebKitNetworkManager::handleProxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
323 {
324 	if (!m_widget)
325 	{
326 		return;
327 	}
328 
329 	if ((m_proxyFactory && m_proxyFactory->usesSystemAuthentication()) || (!m_proxyFactory && NetworkManagerFactory::usesSystemProxyAuthentication()))
330 	{
331 		authenticator->setUser({});
332 
333 		return;
334 	}
335 
336 	setPageInformation(WebWidget::LoadingMessageInformation, tr("Waiting for authentication…"));
337 
338 	AuthenticationDialog *authenticationDialog(new AuthenticationDialog(proxy.hostName(), authenticator, AuthenticationDialog::ProxyAuthentication, m_widget));
339 	authenticationDialog->setButtonsVisible(false);
340 
341 	ContentsDialog dialog(ThemesManager::createIcon(QLatin1String("dialog-password")), authenticationDialog->windowTitle(), {}, {}, (QDialogButtonBox::Ok | QDialogButtonBox::Cancel), authenticationDialog, m_widget);
342 
343 	connect(&dialog, &ContentsDialog::accepted, authenticationDialog, &AuthenticationDialog::accept);
344 	connect(m_widget, &QtWebKitWebWidget::aboutToReload, &dialog, &ContentsDialog::close);
345 
346 	m_widget->showDialog(&dialog);
347 
348 	NetworkManagerFactory::notifyAuthenticated(authenticator, dialog.isAccepted());
349 }
350 
handleSslErrors(QNetworkReply * reply,const QList<QSslError> & errors)351 void QtWebKitNetworkManager::handleSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
352 {
353 	if (!m_widget)
354 	{
355 		return;
356 	}
357 
358 	if (errors.isEmpty())
359 	{
360 		reply->ignoreSslErrors(errors);
361 
362 		return;
363 	}
364 
365 	const QString firstPartyUrl(m_widget->getUrl().toString());
366 	const QString thirdPartyUrl(reply->request().url().toString());
367 	const QStringList exceptions(m_widget->getOption(SettingsManager::Security_IgnoreSslErrorsOption, m_widget->getUrl()).toStringList());
368 	QList<QSslError> errorsToIgnore;
369 
370 	for (int i = 0; i < errors.count(); ++i)
371 	{
372 		if (errors.at(i).error() != QSslError::NoError)
373 		{
374 			m_sslInformation.errors.append({errors.at(i), reply->url()});
375 
376 			if (exceptions.contains(errors.at(i).certificate().digest().toBase64()))
377 			{
378 				Console::addMessage(QStringLiteral("[accepted] The page at %1 was allowed to display insecure content from %2").arg(firstPartyUrl).arg(thirdPartyUrl), Console::SecurityCategory, Console::WarningLevel, thirdPartyUrl, -1, m_widget->getWindowIdentifier());
379 
380 				errorsToIgnore.append(errors.at(i));
381 			}
382 			else
383 			{
384 				Console::addMessage(QStringLiteral("[blocked] The page at %1 was not allowed to display insecure content from %2").arg(firstPartyUrl).arg(thirdPartyUrl), Console::SecurityCategory, Console::WarningLevel, thirdPartyUrl, -1, m_widget->getWindowIdentifier());
385 			}
386 		}
387 	}
388 
389 	if (!errorsToIgnore.isEmpty())
390 	{
391 		reply->ignoreSslErrors(errorsToIgnore);
392 	}
393 
394 	if (reply == m_baseReply)
395 	{
396 		m_isSecureValue = FalseValue;
397 
398 		if (m_contentState.testFlag(WebWidget::SecureContentState))
399 		{
400 			m_contentState = WebWidget::RemoteContentState;
401 
402 			emit contentStateChanged(m_contentState);
403 		}
404 	}
405 }
406 
handleOnlineStateChanged(bool isOnline)407 void QtWebKitNetworkManager::handleOnlineStateChanged(bool isOnline)
408 {
409 	if (isOnline)
410 	{
411 		setNetworkAccessible(QNetworkAccessManager::Accessible);
412 	}
413 }
414 
handleLoadFinished(bool result)415 void QtWebKitNetworkManager::handleLoadFinished(bool result)
416 {
417 	setPageInformation(WebWidget::LoadingFinishedInformation, QDateTime::currentDateTime());
418 	setPageInformation(WebWidget::LoadingMessageInformation, tr("Loading finished"));
419 	setPageInformation(WebWidget::LoadingSpeedInformation, 0);
420 	killTimer(m_loadingSpeedTimer);
421 
422 	m_loadingSpeedTimer = 0;
423 
424 	if (result && (m_isSecureValue == TrueValue || (m_isSecureValue == UnknownValue && m_contentState.testFlag(WebWidget::SecureContentState))) && m_sslInformation.errors.isEmpty())
425 	{
426 		m_contentState = WebWidget::SecureContentState;
427 	}
428 
429 	emit contentStateChanged(m_contentState);
430 }
431 
updateLoadingSpeed()432 void QtWebKitNetworkManager::updateLoadingSpeed()
433 {
434 	setPageInformation(WebWidget::LoadingSpeedInformation, (m_bytesReceivedDifference * 2));
435 
436 	m_bytesReceivedDifference = 0;
437 }
438 
updateOptions(const QUrl & url)439 void QtWebKitNetworkManager::updateOptions(const QUrl &url)
440 {
441 	if (!m_backend)
442 	{
443 		m_backend = AddonsManager::getWebBackend(QLatin1String("qtwebkit"));
444 	}
445 
446 	if (getOption(SettingsManager::ContentBlocking_EnableContentBlockingOption, url).toBool())
447 	{
448 		m_contentBlockingProfiles = ContentFiltersManager::getProfileIdentifiers(getOption(SettingsManager::ContentBlocking_ProfilesOption, url).toStringList());
449 	}
450 	else
451 	{
452 		m_contentBlockingProfiles.clear();
453 	}
454 
455 	QString acceptLanguage(getOption(SettingsManager::Network_AcceptLanguageOption, url).toString());
456 	acceptLanguage = ((acceptLanguage.isEmpty()) ? QLatin1String(" ") : acceptLanguage.replace(QLatin1String("system"), QLocale::system().bcp47Name()));
457 
458 	m_acceptLanguage = ((acceptLanguage == NetworkManagerFactory::getAcceptLanguage()) ? QString() : acceptLanguage);
459 	m_userAgent = m_backend->getUserAgent(NetworkManagerFactory::getUserAgent(getOption(SettingsManager::Network_UserAgentOption, url).toString()).value);
460 	m_unblockedHosts = getOption(SettingsManager::ContentBlocking_IgnoreHostsOption, url).toStringList();
461 
462 	const QString doNotTrackPolicyValue(getOption(SettingsManager::Network_DoNotTrackPolicyOption, url).toString());
463 
464 	if (doNotTrackPolicyValue == QLatin1String("allow"))
465 	{
466 		m_doNotTrackPolicy = NetworkManagerFactory::AllowToTrackPolicy;
467 	}
468 	else if (doNotTrackPolicyValue == QLatin1String("doNotAllow"))
469 	{
470 		m_doNotTrackPolicy = NetworkManagerFactory::DoNotAllowToTrackPolicy;
471 	}
472 	else
473 	{
474 		m_doNotTrackPolicy = NetworkManagerFactory::SkipTrackPolicy;
475 	}
476 
477 	m_areImagesEnabled = (getOption(SettingsManager::Permissions_EnableImagesOption, url).toString() != QLatin1String("disabled"));
478 	m_canSendReferrer = getOption(SettingsManager::Network_EnableReferrerOption, url).toBool();
479 
480 	const QString generalCookiesPolicyValue(getOption(SettingsManager::Network_CookiesPolicyOption, url).toString());
481 	CookieJar::CookiesPolicy generalCookiesPolicy(CookieJar::AcceptAllCookies);
482 
483 	if (generalCookiesPolicyValue == QLatin1String("ignore"))
484 	{
485 		generalCookiesPolicy = CookieJar::IgnoreCookies;
486 	}
487 	else if (generalCookiesPolicyValue == QLatin1String("readOnly"))
488 	{
489 		generalCookiesPolicy = CookieJar::ReadOnlyCookies;
490 	}
491 	else if (generalCookiesPolicyValue == QLatin1String("acceptExisting"))
492 	{
493 		generalCookiesPolicy = CookieJar::AcceptExistingCookies;
494 	}
495 
496 	const QString thirdPartyCookiesPolicyValue(getOption(SettingsManager::Network_ThirdPartyCookiesPolicyOption, url).toString());
497 	CookieJar::CookiesPolicy thirdPartyCookiesPolicy(CookieJar::AcceptAllCookies);
498 
499 	if (thirdPartyCookiesPolicyValue == QLatin1String("ignore"))
500 	{
501 		thirdPartyCookiesPolicy = CookieJar::IgnoreCookies;
502 	}
503 	else if (thirdPartyCookiesPolicyValue == QLatin1String("readOnly"))
504 	{
505 		thirdPartyCookiesPolicy = CookieJar::ReadOnlyCookies;
506 	}
507 	else if (thirdPartyCookiesPolicyValue == QLatin1String("acceptExisting"))
508 	{
509 		thirdPartyCookiesPolicy = CookieJar::AcceptExistingCookies;
510 	}
511 
512 	const QString keepCookiesModeValue(getOption(SettingsManager::Network_CookiesKeepModeOption, url).toString());
513 	CookieJar::KeepMode keepCookiesMode(CookieJar::KeepUntilExpiresMode);
514 
515 	if (keepCookiesModeValue == QLatin1String("keepUntilExit"))
516 	{
517 		keepCookiesMode = CookieJar::KeepUntilExitMode;
518 	}
519 	else if (keepCookiesModeValue == QLatin1String("ask"))
520 	{
521 		keepCookiesMode = CookieJar::AskIfKeepMode;
522 	}
523 
524 	m_cookieJarProxy->setup(getOption(SettingsManager::Network_ThirdPartyCookiesAcceptedHostsOption, url).toStringList(), getOption(SettingsManager::Network_ThirdPartyCookiesRejectedHostsOption, url).toStringList(), generalCookiesPolicy, thirdPartyCookiesPolicy, keepCookiesMode);
525 
526 	if (!m_proxyFactory && ((m_widget && m_widget->hasOption(SettingsManager::Network_ProxyOption)) || SettingsManager::hasOverride(Utils::extractHost(url), SettingsManager::Network_ProxyOption)))
527 	{
528 		m_proxyFactory = new NetworkProxyFactory(this);
529 
530 		setProxyFactory(m_proxyFactory);
531 	}
532 
533 	if (m_proxyFactory)
534 	{
535 		m_proxyFactory->setProxy(getOption(SettingsManager::Network_ProxyOption, url).toString());
536 	}
537 }
538 
setPageInformation(WebWidget::PageInformation key,const QVariant & value)539 void QtWebKitNetworkManager::setPageInformation(WebWidget::PageInformation key, const QVariant &value)
540 {
541 	if (m_loadingSpeedTimer != 0 || key != WebWidget::LoadingMessageInformation)
542 	{
543 		m_pageInformation[key] = value;
544 
545 		emit pageInformationChanged(key, value);
546 	}
547 }
548 
setFormRequest(const QUrl & url)549 void QtWebKitNetworkManager::setFormRequest(const QUrl &url)
550 {
551 	m_formRequestUrl = url;
552 }
553 
setMainRequest(const QUrl & url)554 void QtWebKitNetworkManager::setMainRequest(const QUrl &url)
555 {
556 	m_mainRequestUrl = url;
557 	m_baseReply = nullptr;
558 	m_contentState = WebWidget::UnknownContentState;
559 	m_isSecureValue = UnknownValue;
560 }
561 
setWidget(QtWebKitWebWidget * widget)562 void QtWebKitNetworkManager::setWidget(QtWebKitWebWidget *widget)
563 {
564 	setParent(widget);
565 
566 	m_widget = widget;
567 
568 	m_cookieJarProxy->setWidget(widget);
569 }
570 
clone() const571 QtWebKitNetworkManager* QtWebKitNetworkManager::clone() const
572 {
573 	return new QtWebKitNetworkManager((cache() == nullptr), m_cookieJarProxy->clone(nullptr), nullptr);
574 }
575 
createRequest(QNetworkAccessManager::Operation operation,const QNetworkRequest & request,QIODevice * outgoingData)576 QNetworkReply* QtWebKitNetworkManager::createRequest(QNetworkAccessManager::Operation operation, const QNetworkRequest &request, QIODevice *outgoingData)
577 {
578 	if (m_widget && request.url() == m_formRequestUrl)
579 	{
580 		m_formRequestUrl = QUrl();
581 
582 		m_widget->openFormRequest(request, operation, outgoingData);
583 
584 		return QNetworkAccessManager::createRequest(QNetworkAccessManager::GetOperation, QNetworkRequest());
585 	}
586 
587 	if (m_widget && request.url().path() == QLatin1String("/otter-message") && request.hasRawHeader(QByteArrayLiteral("X-Otter-Token")) && request.hasRawHeader(QByteArrayLiteral("X-Otter-Data")))
588 	{
589 		if (QString(request.rawHeader(QByteArrayLiteral("X-Otter-Token"))) == m_widget->getMessageToken())
590 		{
591 			const QString type(request.rawHeader(QByteArrayLiteral("X-Otter-Type")));
592 			const QJsonObject payloadObject(QJsonDocument::fromJson(QByteArray::fromBase64(request.rawHeader(QByteArrayLiteral("X-Otter-Data")))).object());
593 
594 			if (type == QLatin1String("add-ssl-error-exception"))
595 			{
596 				const QString digest(payloadObject.value(QLatin1String("digest")).toString());
597 				const QUrl url(m_widget->getUrl());
598 				QStringList exceptions(getOption(SettingsManager::Security_IgnoreSslErrorsOption, url).toStringList());
599 
600 				if (!digest.isEmpty() && !exceptions.contains(digest))
601 				{
602 					exceptions.append(digest);
603 
604 					SettingsManager::setOption(SettingsManager::Security_IgnoreSslErrorsOption, exceptions, Utils::extractHost(url));
605 				}
606 			}
607 			else if (type == QLatin1String("add-content-blocking-exception"))
608 			{
609 				const QUrl url(m_widget->getUrl());
610 				const QString host(Utils::extractHost(url));
611 				QStringList ignoredHosts;
612 
613 				if (m_widget->hasOption(SettingsManager::ContentBlocking_IgnoreHostsOption))
614 				{
615 					ignoredHosts = m_widget->getOption(SettingsManager::ContentBlocking_IgnoreHostsOption).toStringList();
616 				}
617 
618 				if (!ignoredHosts.contains(host))
619 				{
620 					ignoredHosts.append(host);
621 
622 					m_widget->setOption(SettingsManager::ContentBlocking_IgnoreHostsOption, ignoredHosts);
623 				}
624 			}
625 			else if (type == QLatin1String("save-password"))
626 			{
627 				const QJsonArray fieldsArray(payloadObject.value(QLatin1String("fields")).toArray());
628 				PasswordsManager::PasswordInformation password;
629 				password.url = QUrl(payloadObject.value(QLatin1String("url")).toString());
630 				password.timeAdded = QDateTime::currentDateTimeUtc();
631 				password.fields.reserve(fieldsArray.count());
632 				password.type = PasswordsManager::FormPassword;
633 
634 				for (int i = 0; i < fieldsArray.count(); ++i)
635 				{
636 					const QJsonObject fieldObject(fieldsArray.at(i).toObject());
637 					PasswordsManager::PasswordInformation::Field field;
638 					field.name = fieldObject.value(QLatin1String("name")).toString();
639 					field.value = fieldObject.value(QLatin1String("value")).toString();
640 					field.type = ((fieldObject.value(QLatin1String("type")).toString() == QLatin1String("password")) ? PasswordsManager::PasswordField : PasswordsManager::TextField);
641 
642 					password.fields.append(field);
643 				}
644 
645 				const PasswordsManager::PasswordMatch match(PasswordsManager::hasPassword(password));
646 
647 				if (match != PasswordsManager::FullMatch)
648 				{
649 					m_widget->notifySavePasswordRequested(password, (match == PasswordsManager::PartialMatch));
650 				}
651 			}
652 		}
653 
654 		return QNetworkAccessManager::createRequest(QNetworkAccessManager::GetOperation, QNetworkRequest(QUrl()));
655 	}
656 
657 	if (m_widget && (m_contentBlockingExceptions.isEmpty() || !m_contentBlockingExceptions.contains(request.url())))
658 	{
659 		const QUrl baseUrl(m_widget->isNavigating() ? request.url() : m_widget->getUrl());
660 		const bool needsContentBlockingCheck(!m_contentBlockingProfiles.isEmpty() && (m_unblockedHosts.isEmpty() || !m_unblockedHosts.contains(Utils::extractHost(baseUrl))));
661 		const NetworkManager::ResourceType resourceType((needsContentBlockingCheck || !m_areImagesEnabled) ? NetworkManager::getResourceType(request, m_mainRequestUrl) : NetworkManager::OtherType);
662 
663 		if (!m_areImagesEnabled && request.url() != m_mainRequestUrl && resourceType == NetworkManager::ImageType)
664 		{
665 			return QNetworkAccessManager::createRequest(QNetworkAccessManager::GetOperation, QNetworkRequest(QUrl()));
666 		}
667 
668 		if (needsContentBlockingCheck)
669 		{
670 			const ContentFiltersManager::CheckResult result(ContentFiltersManager::checkUrl(m_contentBlockingProfiles, baseUrl, request.url(), resourceType));
671 
672 			if (result.isBlocked)
673 			{
674 				const ContentFiltersProfile *profile(ContentFiltersManager::getProfile(result.profile));
675 
676 				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.url().toString(), -1, (m_widget ? m_widget->getWindowIdentifier() : 0));
677 
678 				if (resourceType != NetworkManager::ScriptType && resourceType != NetworkManager::StyleSheetType)
679 				{
680 					m_blockedElements.append(request.url().url());
681 				}
682 
683 				NetworkManager::ResourceInformation resource;
684 				resource.url = request.url();
685 				resource.resourceType = resourceType;
686 				resource.metaData[NetworkManager::ContentBlockingProfileMetaData] = result.profile;
687 				resource.metaData[NetworkManager::ContentBlockingRuleMetaData] = result.rule;
688 
689 				m_blockedRequests.append(resource);
690 
691 				emit requestBlocked(resource);
692 
693 				return QNetworkAccessManager::createRequest(QNetworkAccessManager::GetOperation, QNetworkRequest());
694 			}
695 		}
696 	}
697 
698 	setPageInformation(WebWidget::RequestsStartedInformation, (m_pageInformation[WebWidget::RequestsStartedInformation].toULongLong() + 1));
699 
700 	QNetworkRequest mutableRequest(request);
701 
702 	if (!m_canSendReferrer)
703 	{
704 		mutableRequest.setRawHeader(QStringLiteral("Referer").toLatin1(), QByteArray());
705 	}
706 
707 	if (operation == PostOperation && mutableRequest.header(QNetworkRequest::ContentTypeHeader).isNull())
708 	{
709 		mutableRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded"));
710 	}
711 
712 	if (NetworkManagerFactory::isWorkingOffline())
713 	{
714 		mutableRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
715 	}
716 	else if (m_doNotTrackPolicy != NetworkManagerFactory::SkipTrackPolicy)
717 	{
718 		mutableRequest.setRawHeader(QStringLiteral("DNT").toLatin1(), ((m_doNotTrackPolicy == NetworkManagerFactory::DoNotAllowToTrackPolicy) ? QStringLiteral("1") : QStringLiteral("0")).toLatin1());
719 	}
720 
721 	mutableRequest.setRawHeader(QStringLiteral("Accept-Language").toLatin1(), (m_acceptLanguage.isEmpty() ? NetworkManagerFactory::getAcceptLanguage().toLatin1() : m_acceptLanguage.toLatin1()));
722 	mutableRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent);
723 #if QT_VERSION >= 0x050900
724 	mutableRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, false);
725 #endif
726 
727 	setPageInformation(WebWidget::LoadingMessageInformation, tr("Sending request to %1…").arg(request.url().host()));
728 
729 	QNetworkReply *reply(nullptr);
730 
731 	if (operation == GetOperation && request.url().isLocalFile() && QFileInfo(request.url().toLocalFile()).isDir())
732 	{
733 		LocalListingNetworkReply *localListingReply(new LocalListingNetworkReply(request, this));
734 
735 		reply = localListingReply;
736 
737 		if (m_widget)
738 		{
739 			if (reply->error() == QNetworkReply::NoError)
740 			{
741 				connect(localListingReply, &LocalListingNetworkReply::listingError, m_widget->getPage(), &QtWebKitPage::markAsDisplayingErrorPage);
742 			}
743 			else
744 			{
745 				m_widget->getPage()->markAsDisplayingErrorPage();
746 			}
747 		}
748 	}
749 	else if (operation == GetOperation && request.url().scheme() == QLatin1String("ftp"))
750 	{
751 		QtWebKitFtpListingNetworkReply *ftpListingReply(new QtWebKitFtpListingNetworkReply(request, this));
752 
753 		reply = ftpListingReply;
754 
755 		if (m_widget)
756 		{
757 			if (reply->error() == QNetworkReply::NoError)
758 			{
759 				connect(ftpListingReply, &QtWebKitFtpListingNetworkReply::listingError, m_widget->getPage(), &QtWebKitPage::markAsDisplayingErrorPage);
760 			}
761 			else
762 			{
763 				m_widget->getPage()->markAsDisplayingErrorPage();
764 			}
765 		}
766 	}
767 	else
768 	{
769 		reply = QNetworkAccessManager::createRequest(operation, mutableRequest, outgoingData);
770 	}
771 
772 	if (!m_baseReply && request.url() == m_mainRequestUrl)
773 	{
774 		m_baseReply = reply;
775 	}
776 
777 	if (m_baseReply && m_isSecureValue != FalseValue)
778 	{
779 		const QString scheme(reply->url().scheme());
780 
781 		if (scheme == QLatin1String("https"))
782 		{
783 			m_isSecureValue = TrueValue;
784 		}
785 		else if (scheme == QLatin1String("http"))
786 		{
787 			m_isSecureValue = FalseValue;
788 
789 			if (m_contentState.testFlag(WebWidget::SecureContentState))
790 			{
791 				m_contentState = WebWidget::MixedContentState;
792 
793 				emit contentStateChanged(m_contentState);
794 			}
795 		}
796 	}
797 
798 	m_replies[reply] = {0, false};
799 
800 	connect(reply, &QNetworkReply::downloadProgress, this, &QtWebKitNetworkManager::handleDownloadProgress);
801 
802 	if (m_loadingSpeedTimer == 0)
803 	{
804 		m_loadingSpeedTimer = startTimer(500);
805 	}
806 
807 	return reply;
808 }
809 
getCookieJar() const810 CookieJar* QtWebKitNetworkManager::getCookieJar() const
811 {
812 	return m_cookieJar;
813 }
814 
getPageInformation(WebWidget::PageInformation key) const815 QVariant QtWebKitNetworkManager::getPageInformation(WebWidget::PageInformation key) const
816 {
817 	if (key == WebWidget::RequestsBlockedInformation)
818 	{
819 		return m_blockedRequests.count();
820 	}
821 
822 	return m_pageInformation.value(key);
823 }
824 
getSslInformation() const825 WebWidget::SslInformation QtWebKitNetworkManager::getSslInformation() const
826 {
827 	return m_sslInformation;
828 }
829 
getUserAgent() const830 QString QtWebKitNetworkManager::getUserAgent() const
831 {
832 	return m_userAgent;
833 }
834 
getOption(int identifier,const QUrl & url) const835 QVariant QtWebKitNetworkManager::getOption(int identifier, const QUrl &url) const
836 {
837 	return (m_widget ? m_widget->getOption(identifier, url) : SettingsManager::getOption(identifier, Utils::extractHost(url)));
838 }
839 
getBlockedElements() const840 QStringList QtWebKitNetworkManager::getBlockedElements() const
841 {
842 	return m_blockedElements;
843 }
844 
getBlockedRequests() const845 QVector<NetworkManager::ResourceInformation> QtWebKitNetworkManager::getBlockedRequests() const
846 {
847 	return m_blockedRequests;
848 }
849 
getHeaders() const850 QMap<QByteArray, QByteArray> QtWebKitNetworkManager::getHeaders() const
851 {
852 	return m_headers;
853 }
854 
getContentState() const855 WebWidget::ContentStates QtWebKitNetworkManager::getContentState() const
856 {
857 	return m_contentState;
858 }
859 
860 }
861