File indexing completed on 2024-05-19 04:58:52
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "networkmanager.h" 0019 #include "autofill.h" 0020 #include "qztools.h" 0021 #include "settings.h" 0022 #include "cookiejar.h" 0023 #include "acceptlanguage.h" 0024 #include "mainapplication.h" 0025 #include "passwordmanager.h" 0026 #include "sslerrordialog.h" 0027 #include "networkurlinterceptor.h" 0028 #include "schemehandlers/falkonschemehandler.h" 0029 #include "schemehandlers/extensionschemehandler.h" 0030 #include "webpage.h" 0031 0032 #include <QLabel> 0033 #include <QDialog> 0034 #include <QLineEdit> 0035 #include <QCheckBox> 0036 #include <QFormLayout> 0037 #include <QAuthenticator> 0038 #include <QDialogButtonBox> 0039 #include <QNetworkReply> 0040 #include <QNetworkProxy> 0041 #include <QWebEngineProfile> 0042 #include <QtWebEngineWidgetsVersion> 0043 0044 #include <QWebEngineUrlScheme> 0045 0046 NetworkManager::NetworkManager(QObject *parent) 0047 : QNetworkAccessManager(parent) 0048 { 0049 // Create scheme handlers 0050 m_extensionScheme = new ExtensionSchemeManager(); 0051 0052 mApp->webProfile()->installUrlSchemeHandler(QByteArrayLiteral("falkon"), new FalkonSchemeHandler()); 0053 mApp->webProfile()->installUrlSchemeHandler(QByteArrayLiteral("extension"), m_extensionScheme); 0054 WebPage::addSupportedScheme(QSL("falkon")); 0055 WebPage::addSupportedScheme(QSL("extension")); 0056 0057 // Create url interceptor 0058 m_urlInterceptor = new NetworkUrlInterceptor(this); 0059 mApp->webProfile()->setUrlRequestInterceptor(m_urlInterceptor); 0060 0061 // Create cookie jar 0062 mApp->cookieJar(); 0063 0064 connect(this, &QNetworkAccessManager::authenticationRequired, this, [this](QNetworkReply *reply, QAuthenticator *auth) { 0065 authentication(reply->url(), auth); 0066 }); 0067 0068 connect(this, &QNetworkAccessManager::proxyAuthenticationRequired, this, [this](const QNetworkProxy &proxy, QAuthenticator *auth) { 0069 proxyAuthentication(proxy.hostName(), auth); 0070 }); 0071 } 0072 0073 bool NetworkManager::certificateError(QWebEngineCertificateError &error, QWidget *parent) 0074 { 0075 const QString &host = error.url().host(); 0076 const auto errorType = error.type(); 0077 0078 if (m_rejectedSslErrors.contains(host) && m_rejectedSslErrors.value(host) == errorType) { 0079 return false; 0080 } 0081 0082 if ((m_ignoredSslErrors.contains(host) && m_ignoredSslErrors.value(host) == errorType) 0083 || m_ignoredSslHosts.contains(host)) { 0084 return true; 0085 } 0086 0087 // Defer loading the URL until the user prompt has completed. 0088 if (error.isOverridable()) 0089 error.defer(); 0090 0091 QString title = tr("SSL Certificate Error!"); 0092 QString text1 = tr("The page you are trying to access has the following errors in the SSL certificate:"); 0093 QString text2 = tr("Would you like to make an exception for this certificate?"); 0094 0095 const auto errorDescription = error.description(); 0096 QString message = QSL("<b>%1</b><p>%2</p><ul><li>%3</li></ul><p>%4</p>").arg(title, text1, errorDescription, text2); 0097 0098 SslErrorDialog dialog(parent); 0099 dialog.setText(message); 0100 dialog.exec(); 0101 0102 switch (dialog.result()) { 0103 case SslErrorDialog::Yes: 0104 m_ignoredSslHosts.append(host); 0105 saveIgnoredSslHosts(); 0106 return true; 0107 0108 case SslErrorDialog::OnlyForThisSession: 0109 m_ignoredSslErrors[host] = errorType; 0110 return true; 0111 0112 case SslErrorDialog::NoForThisSession: 0113 m_rejectedSslErrors[host] = errorType; 0114 return false; 0115 0116 default: 0117 return false; 0118 } 0119 } 0120 0121 void NetworkManager::authentication(const QUrl &url, QAuthenticator *auth, QWidget *parent) 0122 { 0123 auto* dialog = new QDialog(parent); 0124 dialog->setWindowTitle(tr("Authorization required")); 0125 0126 auto* formLa = new QFormLayout(dialog); 0127 0128 auto* label = new QLabel(dialog); 0129 auto* userLab = new QLabel(dialog); 0130 auto* passLab = new QLabel(dialog); 0131 userLab->setText(tr("Username: ")); 0132 passLab->setText(tr("Password: ")); 0133 0134 auto* user = new QLineEdit(dialog); 0135 auto* pass = new QLineEdit(dialog); 0136 pass->setEchoMode(QLineEdit::Password); 0137 auto* save = new QCheckBox(dialog); 0138 save->setText(tr("Save username and password for this site")); 0139 0140 auto* box = new QDialogButtonBox(dialog); 0141 box->addButton(QDialogButtonBox::Ok); 0142 box->addButton(QDialogButtonBox::Cancel); 0143 connect(box, &QDialogButtonBox::rejected, dialog, &QDialog::reject); 0144 connect(box, &QDialogButtonBox::accepted, dialog, &QDialog::accept); 0145 0146 label->setText(tr("A username and password are being requested by %1. " 0147 "The site says: \"%2\"").arg(url.host(), auth->realm().toHtmlEscaped())); 0148 0149 formLa->addRow(label); 0150 formLa->addRow(userLab, user); 0151 formLa->addRow(passLab, pass); 0152 formLa->addRow(save); 0153 formLa->addWidget(box); 0154 0155 AutoFill* fill = mApp->autoFill(); 0156 QString storedUser; 0157 QString storedPassword; 0158 bool shouldUpdateEntry = false; 0159 0160 if (fill->isStored(url)) { 0161 const QVector<PasswordEntry> &data = fill->getFormData(url); 0162 if (!data.isEmpty()) { 0163 save->setChecked(true); 0164 shouldUpdateEntry = true; 0165 storedUser = data.at(0).username; 0166 storedPassword = data.at(0).password; 0167 user->setText(storedUser); 0168 pass->setText(storedPassword); 0169 } 0170 } 0171 0172 // Do not save when private browsing is enabled 0173 if (mApp->isPrivate()) { 0174 save->setVisible(false); 0175 } 0176 0177 if (dialog->exec() != QDialog::Accepted) { 0178 *auth = QAuthenticator(); 0179 delete dialog; 0180 return; 0181 } 0182 0183 auth->setUser(user->text()); 0184 auth->setPassword(pass->text()); 0185 0186 if (save->isChecked()) { 0187 if (shouldUpdateEntry) { 0188 if (storedUser != user->text() || storedPassword != pass->text()) { 0189 fill->updateEntry(url, user->text(), pass->text()); 0190 } 0191 } 0192 else { 0193 fill->addEntry(url, user->text(), pass->text()); 0194 } 0195 } 0196 0197 delete dialog; 0198 } 0199 0200 void NetworkManager::proxyAuthentication(const QString &proxyHost, QAuthenticator *auth, QWidget *parent) 0201 { 0202 const QNetworkProxy proxy = QNetworkProxy::applicationProxy(); 0203 if (!proxy.user().isEmpty() && !proxy.password().isEmpty()) { 0204 auth->setUser(proxy.user()); 0205 auth->setPassword(proxy.password()); 0206 return; 0207 } 0208 0209 auto* dialog = new QDialog(parent); 0210 dialog->setWindowTitle(tr("Proxy authorization required")); 0211 0212 auto* formLa = new QFormLayout(dialog); 0213 0214 auto* label = new QLabel(dialog); 0215 auto* userLab = new QLabel(dialog); 0216 auto* passLab = new QLabel(dialog); 0217 userLab->setText(tr("Username: ")); 0218 passLab->setText(tr("Password: ")); 0219 0220 auto* user = new QLineEdit(dialog); 0221 auto* pass = new QLineEdit(dialog); 0222 pass->setEchoMode(QLineEdit::Password); 0223 0224 auto* box = new QDialogButtonBox(dialog); 0225 box->addButton(QDialogButtonBox::Ok); 0226 box->addButton(QDialogButtonBox::Cancel); 0227 connect(box, &QDialogButtonBox::rejected, dialog, &QDialog::reject); 0228 connect(box, &QDialogButtonBox::accepted, dialog, &QDialog::accept); 0229 0230 label->setText(tr("A username and password are being requested by proxy %1. ").arg(proxyHost)); 0231 formLa->addRow(label); 0232 formLa->addRow(userLab, user); 0233 formLa->addRow(passLab, pass); 0234 formLa->addWidget(box); 0235 0236 if (dialog->exec() != QDialog::Accepted) { 0237 *auth = QAuthenticator(); 0238 delete dialog; 0239 return; 0240 } 0241 0242 auth->setUser(user->text()); 0243 auth->setPassword(pass->text()); 0244 0245 delete dialog; 0246 } 0247 0248 void NetworkManager::installUrlInterceptor(UrlInterceptor *interceptor) 0249 { 0250 m_urlInterceptor->installUrlInterceptor(interceptor); 0251 } 0252 0253 void NetworkManager::removeUrlInterceptor(UrlInterceptor *interceptor) 0254 { 0255 m_urlInterceptor->removeUrlInterceptor(interceptor); 0256 } 0257 0258 void NetworkManager::registerExtensionSchemeHandler(const QString &name, ExtensionSchemeHandler *handler) 0259 { 0260 m_extensionScheme->registerHandler(name, handler); 0261 } 0262 0263 void NetworkManager::unregisterExtensionSchemeHandler(ExtensionSchemeHandler *handler) 0264 { 0265 m_extensionScheme->unregisterHandler(handler); 0266 } 0267 0268 void NetworkManager::loadSettings() 0269 { 0270 Settings settings; 0271 settings.beginGroup(QSL("Language")); 0272 QStringList langs = settings.value(QSL("acceptLanguage"), AcceptLanguage::defaultLanguage()).toStringList(); 0273 settings.endGroup(); 0274 mApp->webProfile()->setHttpAcceptLanguage(QString::fromLatin1(AcceptLanguage::generateHeader(langs))); 0275 0276 QNetworkProxy proxy; 0277 settings.beginGroup(QSL("Web-Proxy")); 0278 const int proxyType = settings.value(QSL("ProxyType"), 2).toInt(); 0279 proxy.setHostName(settings.value(QSL("HostName"), QString()).toString()); 0280 proxy.setPort(settings.value(QSL("Port"), 8080).toInt()); 0281 proxy.setUser(settings.value(QSL("Username"), QString()).toString()); 0282 proxy.setPassword(settings.value(QSL("Password"), QString()).toString()); 0283 settings.endGroup(); 0284 0285 if (proxyType == 0) { 0286 proxy.setType(QNetworkProxy::NoProxy); 0287 } else if (proxyType == 3) { 0288 proxy.setType(QNetworkProxy::HttpProxy); 0289 } else if (proxyType == 4) { 0290 proxy.setType(QNetworkProxy::Socks5Proxy); 0291 } 0292 0293 if (proxyType == 2) { 0294 QNetworkProxy::setApplicationProxy(QNetworkProxy()); 0295 QNetworkProxyFactory::setUseSystemConfiguration(true); 0296 } else { 0297 QNetworkProxy::setApplicationProxy(proxy); 0298 QNetworkProxyFactory::setUseSystemConfiguration(false); 0299 } 0300 0301 m_urlInterceptor->loadSettings(); 0302 0303 settings.beginGroup(QSL("Web-Browser-Settings")); 0304 m_ignoredSslHosts = settings.value(QSL("IgnoredSslHosts"), QStringList()).toStringList(); 0305 settings.endGroup(); 0306 } 0307 0308 void NetworkManager::saveIgnoredSslHosts() 0309 { 0310 Settings settings; 0311 settings.beginGroup(QSL("Web-Browser-Settings")); 0312 settings.setValue(QSL("IgnoredSslHosts"), m_ignoredSslHosts); 0313 settings.endGroup(); 0314 } 0315 0316 void NetworkManager::shutdown() 0317 { 0318 mApp->webProfile()->setUrlRequestInterceptor(nullptr); 0319 saveIgnoredSslHosts(); 0320 } 0321 0322 // static 0323 void NetworkManager::registerSchemes() 0324 { 0325 QWebEngineUrlScheme falkonScheme("falkon"); 0326 falkonScheme.setFlags(QWebEngineUrlScheme::SecureScheme | QWebEngineUrlScheme::ContentSecurityPolicyIgnored); 0327 falkonScheme.setSyntax(QWebEngineUrlScheme::Syntax::Path); 0328 QWebEngineUrlScheme::registerScheme(falkonScheme); 0329 QWebEngineUrlScheme extensionScheme("extension"); 0330 extensionScheme.setFlags(QWebEngineUrlScheme::SecureScheme | QWebEngineUrlScheme::ContentSecurityPolicyIgnored); 0331 extensionScheme.setSyntax(QWebEngineUrlScheme::Syntax::Path); 0332 QWebEngineUrlScheme::registerScheme(extensionScheme); 0333 } 0334 0335 QNetworkReply *NetworkManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) 0336 { 0337 QNetworkRequest req = request; 0338 /* TODO Use HTTP2 */ 0339 0340 return QNetworkAccessManager::createRequest(op, req, outgoingData); 0341 }