From bedfc8b344aa80c021740905f0ea9cebc1a3a761766adc87cc3527793d8d0da1 Mon Sep 17 00:00:00 2001 From: Luca Beltrame Date: Sat, 3 Mar 2018 23:46:03 +0000 Subject: [PATCH] Accepting request 582185 from home:wolfi323:branches:KDE:Applications - Add add-kwallet-support-to-webengine.patch to allow storing user names and passwords in kwallet when using the webenginepart OBS-URL: https://build.opensuse.org/request/show/582185 OBS-URL: https://build.opensuse.org/package/show/KDE:Applications/konqueror?expand=0&rev=59 --- add-kwallet-support-to-webengine.patch | 1256 ++++++++++++++++++++++++ konqueror.changes | 6 + konqueror.spec | 4 + 3 files changed, 1266 insertions(+) create mode 100644 add-kwallet-support-to-webengine.patch diff --git a/add-kwallet-support-to-webengine.patch b/add-kwallet-support-to-webengine.patch new file mode 100644 index 0000000..8bd4d84 --- /dev/null +++ b/add-kwallet-support-to-webengine.patch @@ -0,0 +1,1256 @@ +From 56ea2a32d2f1d9f4e6c97b27080cf8144a932ee3 Mon Sep 17 00:00:00 2001 +From: Stefano Crocco +Date: Sun, 14 Jan 2018 11:25:17 +0100 +Subject: Add back KWallet support to Konqueror + +Summary: +Added back the ability to store user names and passwords to KWallet and automatically fill them when loading a page. + +I copied the files `kwebwallet.cpp` and `kwebwallet.h` from the `KDEWebKit` framework and modified it to adapt to `QWebEngine`. In particular, I needed to modify the way form data is passed from javascript to C++ serializing it using JSON and read it back using `QJsonDocument`. Also, since `QWebEnginePage` doesn't allow to access frames from the C++ side, I needed to remove all the code dealing with recurisve searches and implement it in javascript. + +As the old code, this intentionally doesn't work with input elements with the `autocomplete` attribute set to `false`. + +I've not modified the copyright information in the files I copied from `KDEWebKit` as I don't know which is the correct way to do so: I must make clear my files are based on the work on other people but at the same time that I've changed them: how do I do this? + +Test Plan: I tried to access several sites requiring a login in, asking Konqueror to save the password, then logging out and visiting the page again. Except when the `autocomplete` attribute was `false` (checked by looking at the html code), Konqueror always saved and restored login information correctly. + +Reviewers: dfaure + +Reviewed By: dfaure + +Differential Revision: https://phabricator.kde.org/D10178 +--- + webenginepart/src/CMakeLists.txt | 4 + + webenginepart/src/webenginepage.cpp | 14 +- + webenginepart/src/webenginepage.h | 7 +- + webenginepart/src/webenginepart.cpp | 62 +-- + webenginepart/src/webenginepart.h | 5 + + webenginepart/src/webenginepartfactory.cpp | 1 + + webenginepart/src/webenginewallet.cpp | 599 +++++++++++++++++++++++++++++ + webenginepart/src/webenginewallet.h | 305 +++++++++++++++ + 8 files changed, 964 insertions(+), 33 deletions(-) + create mode 100644 webenginepart/src/webenginewallet.cpp + create mode 100644 webenginepart/src/webenginewallet.h + +diff --git a/webenginepart/src/CMakeLists.txt b/webenginepart/src/CMakeLists.txt +index bfac067..fffce88 100644 +--- a/webenginepart/src/CMakeLists.txt ++++ b/webenginepart/src/CMakeLists.txt +@@ -1,3 +1,5 @@ ++find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Wallet) ++ + include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}) + + set(kwebenginepartlib_LIB_SRCS +@@ -8,6 +10,7 @@ set(kwebenginepartlib_LIB_SRCS + websslinfo.cpp + webhistoryinterface.cpp + webenginepartdownloadmanager.cpp ++ webenginewallet.cpp + settings/webenginesettings.cpp + settings/webengine_filter.cpp + ui/searchbar.cpp +@@ -27,6 +30,7 @@ target_link_libraries(kwebenginepartlib Qt5::Core Qt5::DBus Qt5::Gui Qt5::Widget + KF5::Parts + KF5::SonnetCore + KF5::WindowSystem # for KUserTimestamp ++ KF5::Wallet + ) + + target_include_directories(kwebenginepartlib PUBLIC +diff --git a/webenginepart/src/webenginepage.cpp b/webenginepart/src/webenginepage.cpp +index 212e189..615e8f7 100644 +--- a/webenginepart/src/webenginepage.cpp ++++ b/webenginepart/src/webenginepage.cpp +@@ -27,6 +27,7 @@ + #include "webengineview.h" + #include "settings/webenginesettings.h" + #include "webenginepartdownloadmanager.h" ++#include "webenginewallet.h" + #include + #include + +@@ -72,7 +73,8 @@ WebEnginePage::WebEnginePage(WebEnginePart *part, QWidget *parent) + m_kioErrorCode(0), + m_ignoreError(false), + m_part(part), +- m_passwdServerClient(new KPasswdServerClient) ++ m_passwdServerClient(new KPasswdServerClient), ++ m_wallet(Q_NULLPTR) + { + if (view()) + WebEngineSettings::self()->computeFontSizes(view()->logicalDpiY()); +@@ -94,6 +96,7 @@ WebEnginePage::WebEnginePage(WebEnginePart *part, QWidget *parent) + this->profile()->setHttpUserAgent(this->profile()->httpUserAgent() + " Konqueror (WebEnginePart)"); + } + WebEnginePartDownloadManager::instance()->addPage(this); ++ m_wallet = new WebEngineWallet(this, parent ? parent->window()->winId() : 0); + } + + WebEnginePage::~WebEnginePage() +@@ -204,8 +207,9 @@ bool WebEnginePage::acceptNavigationRequest(const QUrl& url, NavigationType type + bool inPageRequest = true; + switch (type) { + case QWebEnginePage::NavigationTypeFormSubmitted: +- //if (!checkFormData(request)) +- // return false; ++ if (!checkFormData(url)) ++ return false; ++ m_wallet->saveFormData(this); + break; + #if 0 + case QWebEnginePage::NavigationTypeFormResubmitted: +@@ -548,9 +552,9 @@ bool WebEnginePage::checkLinkSecurity(const QNetworkRequest &req, NavigationType + return true; + } + +-bool WebEnginePage::checkFormData(const QNetworkRequest &req) const ++bool WebEnginePage::checkFormData(const QUrl &url) const + { +- const QString scheme (req.url().scheme()); ++ const QString scheme (url.scheme()); + + if (m_sslInfo.isValid() && + !scheme.compare(QL1S("https")) && !scheme.compare(QL1S("mailto")) && +diff --git a/webenginepart/src/webenginepage.h b/webenginepart/src/webenginepage.h +index 8fa0386..cb3d5ed 100644 +--- a/webenginepart/src/webenginepage.h ++++ b/webenginepart/src/webenginepage.h +@@ -40,6 +40,7 @@ class WebSslInfo; + class WebEnginePart; + class QWebEngineDownloadItem; + class KPasswdServerClient; ++class WebEngineWallet; + + class WebEnginePage : public QWebEnginePage + { +@@ -64,6 +65,8 @@ public: + + void download(const QUrl &url, bool newWindow = false); + ++ WebEngineWallet* wallet() const {return m_wallet;} ++ + Q_SIGNALS: + /** + * This signal is emitted whenever a user cancels/aborts a load resource +@@ -107,7 +110,7 @@ protected Q_SLOTS: + + private: + bool checkLinkSecurity(const QNetworkRequest& req, NavigationType type) const; +- bool checkFormData(const QNetworkRequest& req) const; ++ bool checkFormData(const QUrl& url) const; + bool handleMailToUrl (const QUrl& , NavigationType type) const; + void setPageJScriptPolicy(const QUrl& url); + +@@ -121,6 +124,7 @@ private: + QPointer m_part; + + QScopedPointer m_passwdServerClient; ++ WebEngineWallet *m_wallet; + }; + + +@@ -158,6 +162,7 @@ private: + KParts::WindowArgs m_windowArgs; + WebWindowType m_type; + bool m_createNewWindow; ++ WebEngineWallet* m_wallet; + }; + + #endif // WEBENGINEPAGE_H +diff --git a/webenginepart/src/webenginepart.cpp b/webenginepart/src/webenginepart.cpp +index 10bbffc..66edce7 100644 +--- a/webenginepart/src/webenginepart.cpp ++++ b/webenginepart/src/webenginepart.cpp +@@ -34,6 +34,7 @@ + #include "webenginepage.h" + #include "websslinfo.h" + #include "webhistoryinterface.h" ++#include "webenginewallet.h" + + #include "ui/searchbar.h" + #include "ui/passwordbar.h" +@@ -80,7 +81,8 @@ WebEnginePart::WebEnginePart(QWidget *parentWidget, QObject *parent, + m_statusBarWalletLabel(0), + m_searchBar(0), + m_passwordBar(0), +- m_featurePermissionBar(0) ++ m_featurePermissionBar(0), ++ m_wallet(Q_NULLPTR) + { + KAboutData about = KAboutData(QStringLiteral("webenginepart"), + i18nc("Program Name", "WebEnginePart"), +@@ -168,6 +170,7 @@ WebEnginePart::WebEnginePart(QWidget *parentWidget, QObject *parent, + + // Load plugins once we are fully ready + loadPlugins(); ++ setWallet(page()->wallet()); + } + + WebEnginePart::~WebEnginePart() +@@ -311,17 +314,25 @@ void WebEnginePart::connectWebEnginePageSignals(WebEnginePage* page) + m_browserExtension->setIconUrl(url); + } + }); ++} + +-#if 0 +- KWebWallet *wallet = page->wallet(); +- if (wallet) { +- connect(wallet, SIGNAL(saveFormDataRequested(QString,QUrl)), +- this, SLOT(slotSaveFormDataRequested(QString,QUrl))); +- connect(wallet, SIGNAL(fillFormRequestCompleted(bool)), +- this, SLOT(slotFillFormRequestCompleted(bool))); +- connect(wallet, SIGNAL(walletClosed()), this, SLOT(slotWalletClosed())); ++void WebEnginePart::setWallet(WebEngineWallet* wallet) ++{ ++ if(m_wallet){ ++ disconnect(m_wallet, &WebEngineWallet::saveFormDataRequested, ++ this, &WebEnginePart::slotSaveFormDataRequested); ++ disconnect(m_wallet, &WebEngineWallet::fillFormRequestCompleted, ++ this, &WebEnginePart::slotFillFormRequestCompleted); ++ disconnect(m_wallet, &WebEngineWallet::walletClosed, this, &WebEnginePart::slotWalletClosed); ++ } ++ m_wallet = wallet; ++ if (m_wallet) { ++ connect(m_wallet, &WebEngineWallet::saveFormDataRequested, ++ this, &WebEnginePart::slotSaveFormDataRequested); ++ connect(m_wallet, &WebEngineWallet::fillFormRequestCompleted, ++ this, &WebEnginePart::slotFillFormRequestCompleted); ++ connect(m_wallet, &WebEngineWallet::walletClosed, this, &WebEnginePart::slotWalletClosed); + } +-#endif + } + + bool WebEnginePart::openUrl(const QUrl &_u) +@@ -451,15 +462,15 @@ void WebEnginePart::slotLoadFinished (bool ok) + } + if (!Utils::isBlankUrl(url())) { + m_hasCachedFormData = false; +- + if (WebEngineSettings::self()->isNonPasswordStorableSite(url().host())) { + addWalletStatusBarIcon(); +- } else { ++ } ++ else { + // Attempt to fill the web form... +-// KWebWallet *webWallet = page() ? page()->wallet() : 0; +-// if (webWallet) { +-// webWallet->fillFormData(frame, false); +-// } ++ WebEngineWallet *wallet = page() ? page()->wallet() : 0; ++ if (wallet){ ++ wallet->fillFormData(page()); ++ } + } + } + +@@ -766,10 +777,10 @@ void WebEnginePart::slotDeleteNonPasswordStorableSite() + + void WebEnginePart::slotRemoveCachedPasswords() + { +- if (!page()) // || !page()->wallet()) ++ if (!page() || !page()->wallet()) + return; + +-// page()->wallet()->removeFormData(page()->mainFrame(), true); ++ page()->wallet()->removeFormData(page()); + m_hasCachedFormData = false; + } + +@@ -822,8 +833,8 @@ void WebEnginePart::slotShowFeaturePermissionBar(QWebEnginePage::Feature feature + this, SLOT(slotFeaturePermissionGranted(QWebEnginePage::Feature))); + connect(m_featurePermissionBar, SIGNAL(permissionDenied(QWebEnginePage::Feature)), + this, SLOT(slotFeaturePermissionDenied(QWebEnginePage::Feature))); +-// connect(m_passwordBar, SIGNAL(done()), +-// this, SLOT(slotSaveFormDataDone())); ++ connect(m_passwordBar, SIGNAL(done()), ++ this, SLOT(slotSaveFormDataDone())); + QBoxLayout* lay = qobject_cast(widget()->layout()); + if (lay) + lay->insertWidget(0, m_featurePermissionBar); +@@ -862,19 +873,16 @@ void WebEnginePart::slotSaveFormDataRequested (const QString& key, const QUrl& u + + if (!m_passwordBar) { + m_passwordBar = new PasswordBar(widget()); +-#if 0 +- KWebWallet* wallet = page()->wallet(); +- if (!wallet) { +- kWarning() << "No wallet instance found! This should never happen!"; ++ if (!m_wallet) { ++ qDebug() << "No m_wallet instance found! This should never happen!"; + return; + } + connect(m_passwordBar, SIGNAL(saveFormDataAccepted(QString)), +- wallet, SLOT(acceptSaveFormDataRequest(QString))); ++ m_wallet, SLOT(acceptSaveFormDataRequest(QString))); + connect(m_passwordBar, SIGNAL(saveFormDataRejected(QString)), +- wallet, SLOT(rejectSaveFormDataRequest(QString))); ++ m_wallet, SLOT(rejectSaveFormDataRequest(QString))); + connect(m_passwordBar, SIGNAL(done()), + this, SLOT(slotSaveFormDataDone())); +-#endif + } + + Q_ASSERT(m_passwordBar); +diff --git a/webenginepart/src/webenginepart.h b/webenginepart/src/webenginepart.h +index 6889e6d..91afa42 100644 +--- a/webenginepart/src/webenginepart.h ++++ b/webenginepart/src/webenginepart.h +@@ -42,6 +42,7 @@ class PasswordBar; + class FeaturePermissionBar; + class KUrlLabel; + class WebEngineBrowserExtension; ++class WebEngineWallet; + + /** + * A KPart wrapper for the QtWebEngine's browser rendering engine. +@@ -98,6 +99,9 @@ public: + void connectWebEnginePageSignals(WebEnginePage* page); + + void slotShowFeaturePermissionBar(QWebEnginePage::Feature); ++ ++ void setWallet(WebEngineWallet* wallet); ++ + protected: + /** + * Re-implemented for internal reasons. API remains unaffected. +@@ -160,6 +164,7 @@ private: + WebEngineBrowserExtension* m_browserExtension; + KParts::StatusBarExtension* m_statusBarExtension; + WebEngineView* m_webView; ++ WebEngineWallet* m_wallet; + }; + + #endif // WEBENGINEPART_H +diff --git a/webenginepart/src/webenginepartfactory.cpp b/webenginepart/src/webenginepartfactory.cpp +index 04853bd..7a877b4 100644 +--- a/webenginepart/src/webenginepartfactory.cpp ++++ b/webenginepart/src/webenginepartfactory.cpp +@@ -24,6 +24,7 @@ + + #include + ++ + WebEngineFactory::~WebEngineFactory() + { + // kDebug() << this; +diff --git a/webenginepart/src/webenginewallet.cpp b/webenginepart/src/webenginewallet.cpp +new file mode 100644 +index 0000000..f501b27 +--- /dev/null ++++ b/webenginepart/src/webenginewallet.cpp +@@ -0,0 +1,599 @@ ++/* ++ * This file is part of the KDE project. ++ * ++ * Copyright (C) 2009 Dawit Alemayehu ++ * Copyright (C) 2018 Stefano Crocco ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public License ++ * along with this library; see the file COPYING.LIB. If not, write to ++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ */ ++ ++#include "webenginewallet.h" ++#include "webenginepage.h" ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define QL1S(x) QLatin1String(x) ++#define QL1C(x) QLatin1Char(x) ++ ++// Javascript used to extract/set data from
elements. ++static const char s_fillableFormElementExtractorJs[] = "(function(){ " ++" function findFormsRecursive(wnd, existingList, path){" ++" findFormsInFrame(wnd, existingList, path);" ++" frameList = wnd.frames;" ++" for(var i = 0; i < frameList.length; ++i) {" ++" var newPath = path.concat(i);" ++" findFormsRecursive(frameList[i], existingList, newPath);" ++" }" ++" }" ++" function findFormsInFrame(frm, existingList, path){" ++" var url = frm.location;" ++" var formList;" ++" try{ formList = frm.document.forms; } " ++" catch(e){" ++" return;" ++" }" ++" if (formList.length > 0) { " ++" for (var i = 0; i < formList.length; ++i) { " ++" var inputList = formList[i].elements; " ++" if (inputList.length < 1) { " ++" continue; " ++" } " ++" var formObject = new Object; " ++" formObject.url = url;" ++" formObject.name = formList[i].name; " ++" if (typeof(formObject.name) != 'string') { " ++" formObject.name = String(formList[i].id); " ++" } " ++" formObject.index = i; " ++" formObject.elements = new Array; " ++" for (var j = 0; j < inputList.length; ++j) { " ++" if (inputList[j].type != 'text' && inputList[j].type != 'email' && inputList[j].type != 'password') { " ++" continue; " ++" } " ++" if (inputList[j].disabled || inputList[j].autocomplete == 'off') { " ++" continue; " ++" } " ++" var element = new Object; " ++" element.name = inputList[j].name; " ++" if (typeof(element.name) != 'string' ) { " ++" element.name = String(inputList[j].id); " ++" } " ++" element.value = String(inputList[j].value); " ++" element.type = String(inputList[j].type); " ++" element.readonly = Boolean(inputList[j].readOnly); " ++" formObject.elements.push(element); " ++" } " ++" if (formObject.elements.length > 0) { " ++" formObject.framePath = path;" ++" console.log(JSON.stringify(formObject));" ++" existingList.push(JSON.stringify(formObject)); " ++" } " ++" } " ++" } " ++" }" ++" var forms = new Array;" ++" findFormsRecursive(window, forms, []);" ++" return forms;" ++"})()"; ++ ++//javascript used to fill a single form element ++static const char s_javascriptFillInputFragment[] = "var frm = window;" ++" for(var i=0; i < [%1].length; ++i) frm=frm.frames[i];" ++" if (frm.document.forms['%2'] && frm.document.forms['%2'].elements['%3']){" ++" frm.document.forms['%2'].elements['%3'].value='%4';\n" ++" }"; ++ ++ ++/** ++ * Creates key used to store and retrieve form data. ++ * ++ */ ++static QString walletKey(WebEngineWallet::WebForm form) ++{ ++ QString key = form.url.toString(QUrl::RemoveQuery | QUrl::RemoveFragment); ++ key += QL1C('#'); ++ key += form.name; ++ return key; ++} ++ ++static QUrl urlForFrame(const QUrl &frameUrl, const QUrl &pageUrl) ++{ ++ return (frameUrl.isEmpty() || frameUrl.isRelative() ? pageUrl.resolved(frameUrl) : frameUrl); ++} ++ ++class WebEngineWallet::WebEngineWalletPrivate ++{ ++public: ++ struct FormsData { ++ QPointer page; ++ WebEngineWallet::WebFormList forms; ++ }; ++ ++ typedef std::function WebWalletCallback; ++ ++ WebEngineWalletPrivate(WebEngineWallet *parent); ++ ++ void withFormData(WebEnginePage *page, WebWalletCallback callback, bool fillform = true, bool ignorepasswd = false); ++ WebFormList parseFormData(const QVariant &result, const QUrl &url, bool fillform = true, bool ignorepasswd = false); ++ void performFormDataParsing(const QVariant &result, const QUrl &url, WebWalletCallback callback, bool fillform, bool ignorepasswd); ++ void fillDataFromCache(WebEngineWallet::WebFormList &formList); ++ void saveDataToCache(const QString &key); ++ void removeDataFromCache(const WebFormList &formList); ++ void openWallet(); ++ ++ // Private slots... ++ void _k_openWalletDone(bool); ++ void _k_walletClosed(); ++ ++ WId wid; ++ WebEngineWallet *q; ++ QScopedPointer wallet; ++ WebEngineWallet::WebFormList pendingRemoveRequests; ++ QHash pendingFillRequests; ++ QHash pendingSaveRequests; ++ QSet confirmSaveRequestOverwrites; ++}; ++ ++WebEngineWallet::WebEngineWalletPrivate::WebEngineWalletPrivate(WebEngineWallet *parent) ++ : wid(0), q(parent) ++{ ++} ++ ++WebEngineWallet::WebFormList WebEngineWallet::WebEngineWalletPrivate::parseFormData(const QVariant &result, const QUrl &url, bool fillform, bool ignorepasswd) ++{ ++ const QVariantList results(result.toList()); ++ WebEngineWallet::WebFormList list; ++ Q_FOREACH (const QVariant &formVariant, results) { ++ QJsonDocument doc = QJsonDocument::fromJson(formVariant.toString().toUtf8()); ++ const QVariantMap map = doc.toVariant().toMap(); ++ WebEngineWallet::WebForm form; ++ form.url = urlForFrame(QUrl(map[QL1S("url")].toString()), url); ++ form.name = map[QL1S("name")].toString(); ++ form.index = map[QL1S("index")].toString(); ++ form.framePath = map["framePath"].toStringList().join(","); ++ bool formHasPasswords = false; ++ const QVariantList elements = map[QL1S("elements")].toList(); ++ QVector inputFields; ++ Q_FOREACH (const QVariant &element, elements) { ++ QVariantMap elementMap(element.toMap()); ++ const QString name(elementMap[QL1S("name")].toString()); ++ const QString value(ignorepasswd ? QString() : elementMap[QL1S("value")].toString()); ++ if (name.isEmpty()) { ++ continue; ++ } ++ if (fillform && elementMap[QL1S("readonly")].toBool()) { ++ continue; ++ } ++ if (elementMap[QL1S("type")].toString().compare(QL1S("password"), Qt::CaseInsensitive) == 0) { ++ if (!fillform && value.isEmpty()) { ++ continue; ++ } ++ formHasPasswords = true; ++ } ++ inputFields.append(qMakePair(name, value)); ++ } ++ // Only add the input fields on form save requests... ++ if (formHasPasswords || fillform) { ++ form.fields = inputFields; ++ } ++ ++ // Add the form to the list if we are saving it or it has cached data. ++ if ((fillform && q->hasCachedFormData(form)) || (!fillform && !form.fields.isEmpty())) { ++ list << form; ++ } ++ } ++ return list; ++} ++ ++void WebEngineWallet::WebEngineWalletPrivate::withFormData(WebEnginePage* page, WebWalletCallback callback, bool fillform, bool ignorepasswd) ++{ ++ Q_ASSERT(page); ++ QUrl url = page->url(); ++ auto internalCallback = [this, url, fillform, ignorepasswd, callback](const QVariant &result){ ++ WebFormList res = parseFormData(result, url, fillform, ignorepasswd); ++ callback(res); ++ }; ++ page->runJavaScript(QL1S(s_fillableFormElementExtractorJs), internalCallback); ++} ++ ++void WebEngineWallet::WebEngineWalletPrivate::fillDataFromCache(WebEngineWallet::WebFormList &formList) ++{ ++ if (!wallet) { ++ qWarning() << "Unable to retrieve form data from wallet"; ++ return; ++ } ++ ++ QString lastKey; ++ QMap cachedValues; ++ QMutableVectorIterator formIt(formList); ++ ++ while (formIt.hasNext()) { ++ WebEngineWallet::WebForm &form = formIt.next(); ++ const QString key(walletKey(form)); ++ if (key != lastKey && wallet->readMap(key, cachedValues) != 0) { ++ qWarning() << "Unable to read form data for key:" << key; ++ continue; ++ } ++ ++ for (int i = 0, count = form.fields.count(); i < count; ++i) { ++ form.fields[i].second = cachedValues.value(form.fields[i].first); ++ } ++ lastKey = key; ++ } ++} ++ ++void WebEngineWallet::WebEngineWalletPrivate::saveDataToCache(const QString &key) ++{ ++ // Make sure the specified keys exists before acting on it. See BR# 270209. ++ if (!pendingSaveRequests.contains(key)) { ++ return; ++ } ++ ++ bool success = false; ++ const QUrl url = pendingSaveRequests.value(key).first().url; ++ ++ if (wallet) { ++ int count = 0; ++ const WebEngineWallet::WebFormList list = pendingSaveRequests.value(key); ++ QVectorIterator formIt(list); ++ ++ while (formIt.hasNext()) { ++ QMap values, storedValues; ++ const WebEngineWallet::WebForm form = formIt.next(); ++ const QString accessKey = walletKey(form); ++ if (confirmSaveRequestOverwrites.contains(url)) { ++ confirmSaveRequestOverwrites.remove(url); ++ const int status = wallet->readMap(accessKey, storedValues); ++ if (status == 0 && storedValues.count()) { ++ QVectorIterator fieldIt(form.fields); ++ while (fieldIt.hasNext()) { ++ const WebEngineWallet::WebForm::WebField field = fieldIt.next(); ++ if (storedValues.contains(field.first) && ++ storedValues.value(field.first) != field.second) { ++ emit q->saveFormDataRequested(key, url); ++ return; ++ } ++ } ++ // If we got here it means the new credential is exactly ++ // the same as the one already cached ; so skip the ++ // re-saving part... ++ success = true; ++ continue; ++ } ++ } ++ QVectorIterator fieldIt(form.fields); ++ while (fieldIt.hasNext()) { ++ const WebEngineWallet::WebForm::WebField field = fieldIt.next(); ++ values.insert(field.first, field.second); ++ } ++ ++ if (wallet->writeMap(accessKey, values) == 0) { ++ count++; ++ } else { ++ qWarning() << "Unable to write form data to wallet"; ++ } ++ } ++ ++ if (list.isEmpty() || count > 0) { ++ success = true; ++ } ++ ++ pendingSaveRequests.remove(key); ++ } else { ++ qWarning() << "NULL Wallet instance!"; ++ } ++ ++ emit q->saveFormDataCompleted(url, success); ++} ++ ++void WebEngineWallet::WebEngineWalletPrivate::openWallet() ++{ ++ if (!wallet.isNull()) { ++ return; ++ } ++ ++ wallet.reset(KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), ++ wid, KWallet::Wallet::Asynchronous)); ++ ++ if (wallet.isNull()) { ++ return; ++ } ++ ++ connect(wallet.data(), SIGNAL(walletOpened(bool)), q, SLOT(_k_openWalletDone(bool))); ++ connect(wallet.data(), SIGNAL(walletClosed()), q, SLOT(_k_walletClosed())); ++} ++ ++void WebEngineWallet::WebEngineWalletPrivate::removeDataFromCache(const WebFormList &formList) ++{ ++ if (!wallet) { ++ qWarning() << "NULL Wallet instance!"; ++ return; ++ } ++ ++ QVectorIterator formIt(formList); ++ while (formIt.hasNext()) { ++ wallet->removeEntry(walletKey(formIt.next())); ++ } ++} ++ ++void WebEngineWallet::WebEngineWalletPrivate::_k_openWalletDone(bool ok) ++{ ++ Q_ASSERT(wallet); ++ ++ if (ok && ++ (wallet->hasFolder(KWallet::Wallet::FormDataFolder()) || ++ wallet->createFolder(KWallet::Wallet::FormDataFolder())) && ++ wallet->setFolder(KWallet::Wallet::FormDataFolder())) { ++ ++ // Do pending fill requests... ++ if (!pendingFillRequests.isEmpty()) { ++ QList urlList; ++ QMutableHashIterator requestIt(pendingFillRequests); ++ while (requestIt.hasNext()) { ++ requestIt.next(); ++ WebEngineWallet::WebFormList list = requestIt.value().forms; ++ fillDataFromCache(list); ++ q->fillWebForm(requestIt.key(), list); ++ } ++ ++ pendingFillRequests.clear(); ++ } ++ ++ // Do pending save requests... ++ if (!pendingSaveRequests.isEmpty()) { ++ QListIterator keysIt(pendingSaveRequests.keys()); ++ while (keysIt.hasNext()) { ++ saveDataToCache(keysIt.next()); ++ } ++ } ++ ++ // Do pending remove requests... ++ if (!pendingRemoveRequests.isEmpty()) { ++ removeDataFromCache(pendingRemoveRequests); ++ pendingRemoveRequests.clear(); ++ } ++ } else { ++ // Delete the wallet if opening the wallet failed or we were unable ++ // to change to the folder we wanted to change to. ++ delete wallet.take(); ++ } ++} ++ ++void WebEngineWallet::WebEngineWalletPrivate::_k_walletClosed() ++{ ++ if (wallet) { ++ wallet.take()->deleteLater(); ++ } ++ ++ emit q->walletClosed(); ++} ++ ++WebEngineWallet::WebEngineWallet(QObject *parent, WId wid) ++ : QObject(parent), d(new WebEngineWalletPrivate(this)) ++{ ++ d->wid = wid; ++} ++ ++WebEngineWallet::~WebEngineWallet() ++{ ++ delete d; ++} ++ ++void WebEngineWallet::fillFormDataCallback(WebEnginePage* page, const WebEngineWallet::WebFormList& formsList) ++{ ++ QList urlList; ++ if (!formsList.isEmpty()) { ++ const QUrl url(page->url()); ++ if (d->pendingFillRequests.contains(url)) { ++ qWarning() << "Duplicate request rejected!"; ++ } else { ++ WebEngineWalletPrivate::FormsData data; ++ data.page = QPointer(page); ++ data.forms << formsList; ++ d->pendingFillRequests.insert(url, data); ++ urlList << url; ++ } ++ } ++ ++ if (!urlList.isEmpty()) { ++ fillFormDataFromCache(urlList); ++ } ++ ++} ++ ++void WebEngineWallet::fillFormData(WebEnginePage *page) ++{ ++ if (!page) return; ++ auto callback = [this, page](const WebFormList &forms){ ++ fillFormDataCallback(page, forms); ++ }; ++ d->withFormData(page, callback); ++} ++ ++static void createSaveKeyFor(WebEnginePage *page, QString *key) ++{ ++ QUrl pageUrl(page->url()); ++ pageUrl.setPassword(QString()); ++ ++ QString keyStr = pageUrl.toString(); ++ ++ *key = QString::number(qHash(keyStr), 16); ++} ++ ++void WebEngineWallet::saveFormData(WebEnginePage *page, bool ignorePasswordFields) ++{ ++ if (!page) { ++ return; ++ } ++ ++ QString key; ++ createSaveKeyFor(page, &key); ++ if (d->pendingSaveRequests.contains(key)) { ++ return; ++ } ++ ++ QUrl url = page->url(); ++ auto callback = [this, key, url](const WebFormList &list){saveFormDataCallback(key, url, list);}; ++ d->withFormData(page, callback, false, ignorePasswordFields); ++} ++ ++void WebEngineWallet::saveFormDataCallback(const QString &key, const QUrl& url, const WebEngineWallet::WebFormList& formsList) ++{ ++ ++ if (formsList.isEmpty()) { ++ return; ++ } ++ ++ WebFormList list(formsList); ++ ++ d->pendingSaveRequests.insert(key, list); ++ ++ QMutableVectorIterator it(list); ++ while (it.hasNext()) { ++ const WebForm form(it.next()); ++ if (hasCachedFormData(form)) { ++ it.remove(); ++ } ++ } ++ ++ if (list.isEmpty()) { ++ d->confirmSaveRequestOverwrites.insert(url); ++ saveFormDataToCache(key); ++ return; ++ } ++ ++ emit saveFormDataRequested(key, url); ++} ++ ++void WebEngineWallet::removeFormData(WebEnginePage *page) ++{ ++ if (page) { ++ auto callback = [this](const WebFormList &list){removeFormDataFromCache(list);}; ++ d->withFormData(page, callback); ++ } ++} ++ ++void WebEngineWallet::removeFormDataCallback(const WebFormList& list) ++{ ++ removeFormDataFromCache(list); ++} ++ ++ ++void WebEngineWallet::removeFormData(const WebFormList &forms) ++{ ++ d->pendingRemoveRequests << forms; ++ removeFormDataFromCache(forms); ++} ++ ++void WebEngineWallet::acceptSaveFormDataRequest(const QString &key) ++{ ++ saveFormDataToCache(key); ++} ++ ++void WebEngineWallet::rejectSaveFormDataRequest(const QString &key) ++{ ++ d->pendingSaveRequests.remove(key); ++} ++ ++void WebEngineWallet::fillWebForm(const QUrl &url, const WebEngineWallet::WebFormList &forms) ++{ ++ QPointer page = d->pendingFillRequests.value(url).page; ++ if (!page) { ++ return; ++ } ++ ++ QString script; ++ bool wasFilled = false; ++ ++ Q_FOREACH (const WebEngineWallet::WebForm &form, forms) { ++ Q_FOREACH (const WebEngineWallet::WebForm::WebField &field, form.fields) { ++ QString value = field.second; ++ value.replace(QL1C('\\'), QL1S("\\\\")); ++ script+= QString(s_javascriptFillInputFragment) ++ .arg(form.framePath) ++ .arg((form.name.isEmpty() ? form.index : form.name)) ++ .arg(field.first).arg(value); ++ } ++ } ++ if (!script.isEmpty()) { ++ wasFilled = true; ++ auto callback = [wasFilled, this](const QVariant &){emit fillFormRequestCompleted(wasFilled);}; ++ page.data()->runJavaScript(script, callback); ++ } ++} ++ ++WebEngineWallet::WebFormList WebEngineWallet::formsToFill(const QUrl &url) const ++{ ++ return d->pendingFillRequests.value(url).forms; ++} ++ ++WebEngineWallet::WebFormList WebEngineWallet::formsToSave(const QString &key) const ++{ ++ return d->pendingSaveRequests.value(key); ++} ++ ++bool WebEngineWallet::hasCachedFormData(const WebForm &form) const ++{ ++ return !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), ++ KWallet::Wallet::FormDataFolder(), ++ walletKey(form)); ++} ++ ++void WebEngineWallet::fillFormDataFromCache(const QList &urlList) ++{ ++ if (d->wallet) { ++ QListIterator urlIt(urlList); ++ while (urlIt.hasNext()) { ++ const QUrl url = urlIt.next(); ++ WebFormList list = formsToFill(url); ++ d->fillDataFromCache(list); ++ fillWebForm(url, list); ++ } ++ d->pendingFillRequests.clear(); ++ } ++ d->openWallet(); ++} ++ ++void WebEngineWallet::saveFormDataToCache(const QString &key) ++{ ++ if (d->wallet) { ++ d->saveDataToCache(key); ++ return; ++ } ++ d->openWallet(); ++} ++ ++void WebEngineWallet::removeFormDataFromCache(const WebFormList &forms) ++{ ++ if (d->wallet) { ++ d->removeDataFromCache(forms); ++ d->pendingRemoveRequests.clear(); ++ return; ++ } ++ d->openWallet(); ++} ++ ++#include "moc_webenginewallet.cpp" +diff --git a/webenginepart/src/webenginewallet.h b/webenginepart/src/webenginewallet.h +new file mode 100644 +index 0000000..2c3d65b +--- /dev/null ++++ b/webenginepart/src/webenginewallet.h +@@ -0,0 +1,305 @@ ++/* ++ * This file is part of the KDE project. ++ * ++ * Copyright (C) 2009 Dawit Alemayehu ++ * Copyright (C) 2018 Stefano Crocco ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Library General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Library General Public License for more details. ++ * ++ * You should have received a copy of the GNU Library General Public License ++ * along with this library; see the file COPYING.LIB. If not, write to ++ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ * ++ */ ++#ifndef WEBENGINEWALLET_H ++#define WEBENGINEWALLET_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++class WebEnginePage; ++ ++class WebEngineWallet : public QObject ++{ ++ Q_OBJECT ++ ++public: ++ ++ /** ++ * Holds data from a HTML <form> element. ++ */ ++ struct WebForm { ++ /** ++ * A typedef for storing the name and value attributes of HTML <input> ++ * elements. ++ */ ++ typedef QPair WebField; ++ ++ /** The URL the form was found at. */ ++ QUrl url; ++ /** The name attribute of the form. */ ++ QString name; ++ /** The position of the form on the web page, relative to other forms. */ ++ QString index; ++ /** The path of the frame the form belongs to relative to the toplevel window (in the javascript sense). ++ * ++ * This is stored as a string containing a javascript array (it is passed as is to javascript code, so no need to store it in C++ format ++ */ ++ QString framePath; ++ /** The name and value attributes of each input element in the form. */ ++ QVector fields; ++ }; ++ ++ /** ++ * A list of web forms ++ */ ++ typedef QVector WebFormList; ++ ++ /** ++ * Constructs a WebEngineWebWallet ++ * ++ * @p parent is usually the WebEnginePage this wallet is being used for. ++ * ++ * The @p wid parameter is used to tell the KWallet manager which window ++ * is requesting access to the wallet. ++ * ++ * @param parent the owner of this wallet ++ * @param wid the window ID of the window the web page will be ++ * embedded in ++ */ ++ explicit WebEngineWallet(QObject *parent = nullptr, WId wid = 0); ++ ++ /** ++ * Destructor ++ */ ++ virtual ~WebEngineWallet(); ++ ++ /** ++ * Attempts to save the form data from @p page and its children frames. ++ * ++ * You must connect to the @ref saveFormDataRequested signal and call either ++ * @ref rejectSaveFormDataRequest or @ref acceptSaveFormDataRequest signals ++ * in order to complete the save request. Otherwise, you request will simply ++ * be ignored. ++ * ++ * Note that this function is asynchronous, as it requires running javascript code ++ * on the page using @ref QWebEnginePage::runJavaScript. This function only requests ++ * for the form data to be saved when @ref QWebEnginePage::runJavaScript finishes. ++ * The actual saving is done by @ref saveFormDataCallback ++ */ ++ void saveFormData(WebEnginePage *page, bool ignorePasswordFields = false); ++ ++ /** ++ * Attempts to fill forms contained in @p page with cached data. ++ * ++ * Note that this function is asynchronous, as it requires running javascript code ++ * on the page using @ref QWebEnginePage::runJavaScript. This function only requests ++ * for the form data to be filled when @ref QWebEnginePage::runJavaScript finishes. ++ * The actual filling is done by @ref fillFormDataCallback ++ */ ++ void fillFormData(WebEnginePage *page); ++ ++ /** ++ * Removes the form data specified by @p forms from the persistent storage. ++ * ++ * Note that this function will remove all cached data for forms found in @p page. ++ * ++ * Note that this function is asynchronous, as it requires running javascript code ++ * on the page using @ref QWebEnginePage::runJavaScript. This function only requests ++ * for the form data to be removed when @ref QWebEnginePage::runJavaScript finishes. ++ * The actual removing is done by @ref removeFormDataCallback ++ */ ++ void removeFormData(WebEnginePage *page); ++ ++ /** ++ * Removes the form data specified by @p forms from the persistent storage. ++ * ++ * @see formsWithCachedData ++ */ ++ void removeFormData(const WebFormList &forms); ++ ++public Q_SLOTS: ++ /** ++ * Accepts the save form data request associated with @p key. ++ * ++ * The @p key parameter is the one sent through the @ref saveFormDataRequested ++ * signal. ++ * ++ * You must always call this function or @ref rejectSaveFormDataRequest in ++ * order to complete the save form data request. Otherwise, the request will ++ * simply be ignored. ++ * ++ * @see saveFormDataRequested. ++ */ ++ void acceptSaveFormDataRequest(const QString &key); ++ ++ /** ++ * Rejects the save form data request associated with @p key. ++ * ++ * The @p key parameter is the one sent through the @ref saveFormDataRequested ++ * signal. ++ * ++ * @see saveFormDataRequested. ++ */ ++ void rejectSaveFormDataRequest(const QString &key); ++ ++Q_SIGNALS: ++ /** ++ * This signal is emitted whenever a save form data request is received. ++ * ++ * Unless you connect to this signal and and call @ref acceptSaveFormDataRequest ++ * or @ref rejectSaveFormDataRequest slots, the save form data requested through ++ * @ref saveFormData will simply be ignored. ++ * ++ * @p key is a value that uniquely identifies the save request and @p url ++ * is the address for which the form data is being saved. ++ * ++ * @see acceptSaveFormDataRequest ++ * @see rejectSaveFormDataRequest ++ */ ++ void saveFormDataRequested(const QString &key, const QUrl &url); ++ ++ /** ++ * This signal is emitted whenever a save form data request is completed. ++ * ++ * @p ok will be set to true if the save form data request for @p url was ++ * completed successfully. ++ * ++ * @see saveFormDataRequested ++ */ ++ void saveFormDataCompleted(const QUrl &url, bool ok); ++ ++ /** ++ * This signal is emitted whenever a fill form data request is completed. ++ * ++ * @p ok will be set to true if any forms were successfully filled with ++ * cached data from the persistent storage. ++ * ++ * @see fillFormData ++ * @since 4.5 ++ */ ++ void fillFormRequestCompleted(bool ok); ++ ++ /** ++ * This signal is emitted whenever the current wallet is closed. ++ */ ++ void walletClosed(); ++ ++protected: ++ /** ++ * Returns a list of forms for @p url that are waiting to be filled. ++ * ++ * This function returns an empty list if there is no pending requests ++ * for filling forms associated with @p url. ++ */ ++ WebFormList formsToFill(const QUrl &url) const; ++ ++ /** ++ * Returns a list of for @p key that are waiting to be saved. ++ * ++ * This function returns an empty list if there are no pending requests ++ * for saving forms associated with @p key. ++ */ ++ WebFormList formsToSave(const QString &key) const; ++ ++ /** ++ * Returns forms to be removed from persistent storage. ++ */ ++ WebFormList formsToDelete() const; ++ ++ /** ++ * Returns true when there is data associated with @p form in the ++ * persistent storage. ++ */ ++ bool hasCachedFormData(const WebForm &form) const; ++ ++ /** ++ * Fills the web forms in frame that point to @p url with data from @p forms. ++ * ++ * @see fillFormDataFromCache. ++ */ ++ void fillWebForm(const QUrl &url, const WebFormList &forms); ++ ++ /** ++ * Fills form data from persistent storage. ++ * ++ * If you reimplement this function, call @ref formsToFill to obtain ++ * the list of forms pending to be filled. Once you fill the list with ++ * the cached data from the persistent storage, you must call @p fillWebForm ++ * to fill out the actual web forms. ++ * ++ * @see formsToFill ++ */ ++ void fillFormDataFromCache(const QList &list); ++ ++ /** ++ * Stores form data associated with @p key to a persistent storage. ++ * ++ * If you reimplement this function, call @ref formsToSave to obtain the ++ * list of form data pending to be saved to persistent storage. ++ * ++ *@see formsToSave ++ */ ++ void saveFormDataToCache(const QString &key); ++ ++ /** ++ * Removes all cached form data associated with @p forms from persistent storage. ++ * ++ * If you reimplement this function, call @ref formsToDelete to obtain the ++ * list of form data pending to be removed from persistent storage. ++ * ++ *@see formsToDelete ++ */ ++ void removeFormDataFromCache(const WebFormList &forms); ++ ++private: ++ ++ /** ++ * Callback used by @ref fillFormData to insert form data ++ * ++ * This function is called as a callback from @ref fillFormData after ++ * @ref QWebEnginePage::runJavaScript has finished ++ */ ++ void fillFormDataCallback(WebEnginePage *page, const WebFormList &formsList); ++ ++ /** ++ * Callback used by @ref saveFormData to save data ++ * ++ * This function is called as a callback from @ref saveFormData after ++ * @ref QWebEnginePage::runJavaScript has finished ++ */ ++ void saveFormDataCallback(const QString &key, const QUrl &url, const WebFormList &formslist); ++ ++ /** ++ * Callback used by @ref removeFormData to remove data ++ * ++ * This function is called as a callback from @ref removeFormData after ++ * @ref QWebEnginePage::runJavaScript has finished ++ */ ++ void removeFormDataCallback(const WebFormList &list); ++ ++private: ++ class WebEngineWalletPrivate; ++ friend class WebEngineWalletPrivate; ++ WebEngineWalletPrivate *const d; ++ ++ Q_PRIVATE_SLOT(d, void _k_openWalletDone(bool)) ++ Q_PRIVATE_SLOT(d, void _k_walletClosed()) ++}; ++ ++ ++#endif // WEBENGINEWALLET_H +-- +cgit v0.11.2 + diff --git a/konqueror.changes b/konqueror.changes index d4e206e..73246c2 100644 --- a/konqueror.changes +++ b/konqueror.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Sat Mar 3 10:19:24 UTC 2018 - wbauer@tmo.at + +- Add add-kwallet-support-to-webengine.patch to allow storing user + names and passwords in kwallet when using the webenginepart + ------------------------------------------------------------------- Wed Feb 07 08:48:34 CET 2018 - lbeltrame@kde.org diff --git a/konqueror.spec b/konqueror.spec index d956a8a..1bff9f2 100644 --- a/konqueror.spec +++ b/konqueror.spec @@ -33,6 +33,8 @@ License: GPL-2.0+ Group: System/GUI/KDE Url: http://www.kde.org/ Source: %{name}-%{version}.tar.xz +# PATCH-FEATURE-UPSTREAM +Patch: add-kwallet-support-to-webengine.patch BuildRequires: extra-cmake-modules BuildRequires: kactivities5-devel >= 5.7.0 BuildRequires: karchive-devel >= 5.7.0 @@ -45,6 +47,7 @@ BuildRequires: kdoctools-devel >= 5.7.0 BuildRequires: kf5-filesystem BuildRequires: khtml-devel >= 5.7.0 BuildRequires: kparts-devel >= 5.7.0 +BuildRequires: kwallet-devel BuildRequires: libtidy-devel BuildRequires: pkgconfig BuildRequires: update-desktop-files @@ -115,6 +118,7 @@ Development package for the konqueror libraries. %prep %setup -q +%patch -p1 sed -i 's|${CMAKE_CURRENT_SOURCE_DIR}/css|${CMAKE_CURRENT_SOURCE_DIR}/css ${CMAKE_BINARY_DIR}|g' settings/konqhtml/CMakeLists.txt %build