nextcloud-desktop/src/gui/wizard/webview.cpp

226 lines
7.1 KiB
C++

#include "webview.h"
#include <QWebEnginePage>
#include <QWebEngineProfile>
#include <QWebEngineUrlRequestInterceptor>
#include <QWebEngineUrlRequestJob>
#include <QWebEngineUrlSchemeHandler>
#include <QWebEngineView>
#include <QDesktopServices>
#include <QProgressBar>
#include <QLoggingCategory>
#include <QLocale>
#include <QWebEngineCertificateError>
#include <QMessageBox>
#include "common/utility.h"
namespace OCC {
Q_LOGGING_CATEGORY(lcWizardWebiew, "gui.wizard.webview", QtInfoMsg)
class WebViewPageUrlRequestInterceptor : public QWebEngineUrlRequestInterceptor
{
Q_OBJECT
public:
WebViewPageUrlRequestInterceptor(QObject *parent = nullptr);
void interceptRequest(QWebEngineUrlRequestInfo &info) override;
};
class WebViewPageUrlSchemeHandler : public QWebEngineUrlSchemeHandler
{
Q_OBJECT
public:
WebViewPageUrlSchemeHandler(QObject *parent = nullptr);
void requestStarted(QWebEngineUrlRequestJob *request) override;
Q_SIGNALS:
void urlCatched(QString user, QString pass, QString host);
};
class WebEnginePage : public QWebEnginePage {
public:
WebEnginePage(QWebEngineProfile *profile, QObject* parent = nullptr);
QWebEnginePage * createWindow(QWebEnginePage::WebWindowType type) override;
void setUrl(const QUrl &url);
protected:
bool certificateError(const QWebEngineCertificateError &certificateError) override;
private:
QUrl _rootUrl;
};
// We need a separate class here, since we cannot simply return the same WebEnginePage object
// this leads to a strage segfault somewhere deep inside of the QWebEngine code
class ExternalWebEnginePage : public QWebEnginePage {
public:
ExternalWebEnginePage(QWebEngineProfile *profile, QObject* parent = nullptr);
bool acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame) override;
};
WebView::WebView(QWidget *parent)
: QWidget(parent),
_ui()
{
_ui.setupUi(this);
_webview = new QWebEngineView(this);
_profile = new QWebEngineProfile(this);
_page = new WebEnginePage(_profile);
_interceptor = new WebViewPageUrlRequestInterceptor(this);
_schemeHandler = new WebViewPageUrlSchemeHandler(this);
const QString userAgent(Utility::userAgentString());
_profile->setHttpUserAgent(userAgent);
QWebEngineProfile::defaultProfile()->setHttpUserAgent(userAgent);
_profile->setRequestInterceptor(_interceptor);
_profile->installUrlSchemeHandler("nc", _schemeHandler);
/*
* Set a proper accept langauge to the language of the client
* code from: http://code.qt.io/cgit/qt/qtbase.git/tree/src/network/access/qhttpnetworkconnection.cpp
*/
{
QString systemLocale = QLocale::system().name().replace(QChar::fromLatin1('_'),QChar::fromLatin1('-'));
QString acceptLanguage;
if (systemLocale == QLatin1String("C")) {
acceptLanguage = QString::fromLatin1("en,*");
} else if (systemLocale.startsWith(QLatin1String("en-"))) {
acceptLanguage = systemLocale + QLatin1String(",*");
} else {
acceptLanguage = systemLocale + QLatin1String(",en,*");
}
_profile->setHttpAcceptLanguage(acceptLanguage);
}
_webview->setPage(_page);
_ui.verticalLayout->addWidget(_webview);
connect(_webview, &QWebEngineView::loadProgress, _ui.progressBar, &QProgressBar::setValue);
connect(_schemeHandler, &WebViewPageUrlSchemeHandler::urlCatched, this, &WebView::urlCatched);
}
void WebView::setUrl(const QUrl &url) {
_page->setUrl(url);
}
WebView::~WebView() {
/*
* The Qt implmentation deletes children in the order they are added to the
* object tree, so in this case _page is deleted after _profile, which
* violates the assumption that _profile should exist longer than
* _page [1]. Here I delete _page manually so that _profile can be safely
* deleted later.
*
* [1] https://doc.qt.io/qt-5/qwebenginepage.html#QWebEnginePage-1
*/
delete _page;
}
WebViewPageUrlRequestInterceptor::WebViewPageUrlRequestInterceptor(QObject *parent)
: QWebEngineUrlRequestInterceptor(parent) {
}
void WebViewPageUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) {
info.setHttpHeader("OCS-APIREQUEST", "true");
}
WebViewPageUrlSchemeHandler::WebViewPageUrlSchemeHandler(QObject *parent)
: QWebEngineUrlSchemeHandler(parent) {
}
void WebViewPageUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *request) {
QUrl url = request->requestUrl();
QString path = url.path(0).mid(1); // get undecoded path
QStringList parts = path.split("&");
QString server;
QString user;
QString password;
for (QString part : parts) {
if (part.startsWith("server:")) {
server = part.mid(7);
} else if (part.startsWith("user:")) {
user = part.mid(5);
} else if (part.startsWith("password:")) {
password = part.mid(9);
}
}
qCDebug(lcWizardWebiew()) << "Got raw user from request path: " << user;
user = user.replace(QChar('+'), QChar(' '));
password = password.replace(QChar('+'), QChar(' '));
user = QUrl::fromPercentEncoding(user.toUtf8());
password = QUrl::fromPercentEncoding(password.toUtf8());
if (!server.startsWith("http://") && !server.startsWith("https://")) {
server = "https://" + server;
}
qCInfo(lcWizardWebiew()) << "Got user: " << user << ", server: " << server;
emit urlCatched(user, password, server);
}
WebEnginePage::WebEnginePage(QWebEngineProfile *profile, QObject* parent) : QWebEnginePage(profile, parent) {
}
QWebEnginePage * WebEnginePage::createWindow(QWebEnginePage::WebWindowType type) {
ExternalWebEnginePage *view = new ExternalWebEnginePage(this->profile());
return view;
}
void WebEnginePage::setUrl(const QUrl &url) {
QWebEnginePage::setUrl(url);
_rootUrl = url;
}
bool WebEnginePage::certificateError(const QWebEngineCertificateError &certificateError) {
if (certificateError.error() == QWebEngineCertificateError::CertificateAuthorityInvalid &&
certificateError.url().host() == _rootUrl.host()) {
return true;
}
/**
* TODO properly improve this.
* The certificate should be displayed.
*
* Or rather we should do a request with the QNAM and see if it works (then it is in the store).
* This is just a quick fix for now.
*/
QMessageBox messageBox;
messageBox.setText(tr("Invalid certificate detected"));
messageBox.setInformativeText(tr("The host \"%1\" provided an invalid certificate. Continue?").arg(certificateError.url().host()));
messageBox.setIcon(QMessageBox::Warning);
messageBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
messageBox.setDefaultButton(QMessageBox::No);
int ret = messageBox.exec();
return ret == QMessageBox::Yes;
}
ExternalWebEnginePage::ExternalWebEnginePage(QWebEngineProfile *profile, QObject* parent) : QWebEnginePage(profile, parent) {
}
bool ExternalWebEnginePage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType type, bool isMainFrame)
{
QDesktopServices::openUrl(url);
return false;
}
}
#include "webview.moc"