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