• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.9.5 API Reference
  • KDE Home
  • Contact Us
 

KDEWebKit

kwebwallet.cpp
Go to the documentation of this file.
00001 /*
00002  * This file is part of the KDE project.
00003  *
00004  * Copyright (C) 2009 Dawit Alemayehu <adawit@kde.org>
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Library General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Library General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Library General Public License
00017  * along with this library; see the file COPYING.LIB.  If not, write to
00018  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "kwebwallet.h"
00024 
00025 #include <kwallet.h>
00026 #include <kdebug.h>
00027 
00028 #include <QtCore/QSet>
00029 #include <QtCore/QHash>
00030 #include <QtCore/QFile>
00031 #include <QtCore/QWeakPointer>
00032 #include <QtCore/QScopedPointer>
00033 #include <QtWebKit/QWebPage>
00034 #include <QtWebKit/QWebFrame>
00035 #include <QtWebKit/QWebElement>
00036 #include <QtWebKit/QWebElementCollection>
00037 #include <qwindowdefs.h>
00038 
00039 #define QL1S(x)   QLatin1String(x)
00040 #define QL1C(x)   QLatin1Char(x)
00041 
00042 // Javascript used to extract/set data from <form> elements.
00043 #define FILLABLE_FORM_ELEMENT_EXTRACTOR_JS "(function (){ \
00044     var forms; \
00045     var formList = document.forms; \
00046     if (formList.length > 0) { \
00047         forms = new Array; \
00048         for (var i = 0; i < formList.length; ++i) { \
00049             var inputList = formList[i].elements; \
00050             if (inputList.length < 1) { \
00051                 continue; \
00052             } \
00053             var formObject = new Object; \
00054             formObject.name = formList[i].name; \
00055             if (typeof(formObject.name) != 'string') { \
00056                 formObject.name = String(formList[i].id); \
00057             } \
00058             formObject.index = i; \
00059             formObject.elements = new Array; \
00060             for (var j = 0; j < inputList.length; ++j) { \
00061                 if (inputList[j].type != 'text' && inputList[j].type != 'email' && inputList[j].type != 'password') { \
00062                     continue; \
00063                 } \
00064                 if (inputList[j].disabled || inputList[j].autocomplete == 'off') { \
00065                     continue; \
00066                 } \
00067                 var element = new Object; \
00068                 element.name = inputList[j].name; \
00069                 if (typeof(element.name) != 'string' ) { \
00070                     element.name = String(inputList[j].id); \
00071                 } \
00072                 element.value = String(inputList[j].value); \
00073                 element.type = String(inputList[j].type); \
00074                 element.readonly = Boolean(inputList[j].readOnly); \
00075                 formObject.elements.push(element); \
00076             } \
00077             if (formObject.elements.length > 0) { \
00078                 forms.push(formObject); \
00079             } \
00080         } \
00081     } \
00082     return forms; \
00083 }())"
00084 
00085 
00090 static QString walletKey(KWebWallet::WebForm form)
00091 {
00092     QString key = form.url.toString(QUrl::RemoveQuery|QUrl::RemoveFragment);
00093     key += QL1C('#');
00094     key += form.name;
00095     return key;
00096 }
00097 
00098 static void collectAllChildFrames(QWebFrame* frame, QList<QWebFrame*>& list)
00099 {
00100     list << frame->childFrames();
00101     QListIterator<QWebFrame*> it(frame->childFrames());
00102     while (it.hasNext()) {
00103         collectAllChildFrames(it.next(), list);
00104     }
00105 }
00106 
00107 static QUrl urlForFrame(QWebFrame* frame)
00108 {
00109     return (frame->url().isEmpty() ? frame->baseUrl().resolved(frame->url()) : frame->url());
00110 }
00111 
00112 /*
00113   Returns the top most window associated with widget.
00114 
00115   Unlike QWidget::window(), this function does its best to find and return the
00116   main application window associated with a given widget. It will not stop when
00117   it encounters a dialog which likely "has (or could have) a window-system frame".
00118 */
00119 static QWidget* topLevelWindow(QObject* obj)
00120 {
00121     QWebPage *page = qobject_cast<QWebPage*>(obj);
00122     QWidget* widget = (page ? page->view() : qobject_cast<QWidget*>(page));
00123     while (widget && widget->parentWidget()) {
00124         widget = widget->parentWidget();
00125     }
00126     return (widget ? widget->window() : 0);
00127 }
00128 
00129 class KWebWallet::KWebWalletPrivate
00130 {
00131 public:
00132     struct FormsData
00133     {
00134         QWeakPointer<QWebFrame> frame;
00135         KWebWallet::WebFormList forms;
00136     };
00137 
00138     KWebWalletPrivate(KWebWallet* parent);
00139     KWebWallet::WebFormList parseFormData(QWebFrame* frame, bool fillform = true, bool ignorepasswd = false);
00140     void fillDataFromCache(KWebWallet::WebFormList &formList);
00141     void saveDataToCache(const QString &key);
00142     void removeDataFromCache(const WebFormList &formList);
00143     void openWallet();
00144 
00145     // Private slots...
00146     void _k_openWalletDone(bool);
00147     void _k_walletClosed();
00148 
00149     WId wid;
00150     KWebWallet *q;
00151     QScopedPointer<KWallet::Wallet> wallet;
00152     KWebWallet::WebFormList pendingRemoveRequests;
00153     QHash<KUrl, FormsData> pendingFillRequests;
00154     QHash<QString, KWebWallet::WebFormList> pendingSaveRequests;
00155     QSet<KUrl> confirmSaveRequestOverwrites;
00156 };
00157 
00158 KWebWallet::KWebWalletPrivate::KWebWalletPrivate(KWebWallet *parent)
00159                               :wid (0), q(parent)
00160 {
00161 }
00162 
00163 KWebWallet::WebFormList KWebWallet::KWebWalletPrivate::parseFormData(QWebFrame *frame, bool fillform, bool ignorepasswd)
00164 {
00165     Q_ASSERT(frame);
00166 
00167     KWebWallet::WebFormList list;
00168 
00169     const QVariant result (frame->evaluateJavaScript(QL1S(FILLABLE_FORM_ELEMENT_EXTRACTOR_JS)));
00170     const QVariantList results (result.toList());
00171     Q_FOREACH (const QVariant &formVariant, results) {
00172         QVariantMap map = formVariant.toMap();
00173         KWebWallet::WebForm form;
00174         form.url = urlForFrame(frame);
00175         form.name = map[QL1S("name")].toString();
00176         form.index = map[QL1S("index")].toString();
00177         bool formHasPasswords = false;
00178         const QVariantList elements = map[QL1S("elements")].toList();
00179         QList<KWebWallet::WebForm::WebField> inputFields;
00180         Q_FOREACH (const QVariant &element, elements) {
00181             QVariantMap elementMap (element.toMap());
00182             const QString name (elementMap[QL1S("name")].toString());
00183             const QString value (ignorepasswd ? QString() : elementMap[QL1S("value")].toString());
00184             if (name.isEmpty()) {
00185                 continue;
00186             }
00187             if (fillform && elementMap[QL1S("readonly")].toBool()) {
00188                 continue;
00189             }
00190             if (elementMap[QL1S("type")].toString().compare(QL1S("password"), Qt::CaseInsensitive) == 0) {
00191                 if (!fillform && value.isEmpty())
00192                     continue;
00193                 formHasPasswords = true;
00194             }
00195             inputFields.append(qMakePair(name, value));
00196         }
00197 
00198         // Only add the input fields on form save requests...
00199         if (formHasPasswords || fillform) {
00200             form.fields = inputFields;
00201         }
00202 
00203         // Add the form to the list if we are saving it or it has cached data.
00204         if ((fillform && q->hasCachedFormData(form)) || (!fillform  && !form.fields.isEmpty()))
00205             list << form;
00206     }
00207 
00208     return list;
00209 }
00210 
00211 void KWebWallet::KWebWalletPrivate::fillDataFromCache(KWebWallet::WebFormList &formList)
00212 {
00213     if (!wallet) {
00214         kWarning(800) << "Unable to retrieve form data from wallet";
00215         return;
00216     }
00217 
00218     QString lastKey;
00219     QMap<QString, QString> cachedValues;
00220     QMutableListIterator <WebForm> formIt (formList);
00221 
00222     while (formIt.hasNext()) {
00223         KWebWallet::WebForm &form = formIt.next();
00224         const QString key (walletKey(form));
00225         if (key != lastKey && wallet->readMap(key, cachedValues) != 0) {
00226             kWarning(800) << "Unable to read form data for key:" << key;
00227             continue;
00228         }
00229 
00230         for (int i = 0, count = form.fields.count(); i < count; ++i) {
00231             form.fields[i].second = cachedValues.value(form.fields[i].first);
00232         }
00233         lastKey = key;
00234     }
00235 }
00236 
00237 void KWebWallet::KWebWalletPrivate::saveDataToCache(const QString &key)
00238 {
00239     // Make sure the specified keys exists before acting on it. See BR# 270209.
00240     if (!pendingSaveRequests.contains(key)) {
00241         return;
00242     }
00243 
00244     bool success = false;
00245     const QUrl url = pendingSaveRequests.value(key).first().url;
00246 
00247     if (wallet) {
00248         int count = 0;
00249         const KWebWallet::WebFormList list = pendingSaveRequests.value(key);
00250         QListIterator<KWebWallet::WebForm> formIt (list);
00251 
00252         while (formIt.hasNext()) {
00253             QMap<QString, QString> values, storedValues;
00254             const KWebWallet::WebForm form = formIt.next();
00255             const QString accessKey = walletKey(form);
00256             if (confirmSaveRequestOverwrites.contains(url)) {
00257                 confirmSaveRequestOverwrites.remove(url);
00258                 const int status = wallet->readMap(accessKey, storedValues);
00259                 if (status == 0 && storedValues.count()) {
00260                     QListIterator<KWebWallet::WebForm::WebField> fieldIt (form.fields);
00261                     while (fieldIt.hasNext()) {
00262                         const KWebWallet::WebForm::WebField field = fieldIt.next();
00263                         if (storedValues.contains(field.first) &&
00264                             storedValues.value(field.first) != field.second) {
00265                             emit q->saveFormDataRequested(key, url);
00266                             return;
00267                         }
00268                     }
00269                     // If we got here it means the new credential is exactly
00270                     // the same as the one already cached ; so skip the
00271                     // re-saving part...
00272                     success = true;
00273                     continue;
00274                 }
00275             }
00276             QListIterator<KWebWallet::WebForm::WebField> fieldIt (form.fields);
00277             while (fieldIt.hasNext()) {
00278                 const KWebWallet::WebForm::WebField field = fieldIt.next();
00279                 values.insert(field.first, field.second);
00280             }
00281 
00282             if (wallet->writeMap(accessKey, values) == 0)
00283                 count++;
00284             else
00285                 kWarning(800) << "Unable to write form data to wallet";
00286         }
00287 
00288         if (list.isEmpty() || count > 0)
00289           success = true;
00290 
00291         pendingSaveRequests.remove(key);
00292     } else {
00293         kWarning(800) << "NULL KWallet instance!";
00294     }
00295 
00296     emit q->saveFormDataCompleted(url, success);
00297 }
00298 
00299 void KWebWallet::KWebWalletPrivate::openWallet()
00300 {
00301     if (!wallet.isNull()) {
00302         return;
00303     }
00304 
00305     wallet.reset(KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(),
00306                                              wid, KWallet::Wallet::Asynchronous));
00307 
00308     if (wallet.isNull()) {
00309         return;
00310     }
00311 
00312     connect(wallet.data(), SIGNAL(walletOpened(bool)), q, SLOT(_k_openWalletDone(bool)));
00313     connect(wallet.data(), SIGNAL(walletClosed()), q, SLOT(_k_walletClosed()));
00314 }
00315 
00316 
00317 void KWebWallet::KWebWalletPrivate::removeDataFromCache(const WebFormList &formList)
00318 {
00319     if (!wallet) {
00320         kWarning(800) << "NULL KWallet instance!";
00321         return;
00322     }
00323 
00324     QListIterator<WebForm> formIt (formList);
00325     while (formIt.hasNext())
00326         wallet->removeEntry(walletKey(formIt.next()));
00327 }
00328 
00329 void KWebWallet::KWebWalletPrivate::_k_openWalletDone(bool ok)
00330 {
00331     Q_ASSERT (wallet);
00332 
00333     if (ok &&
00334         (wallet->hasFolder(KWallet::Wallet::FormDataFolder()) ||
00335          wallet->createFolder(KWallet::Wallet::FormDataFolder())) &&
00336          wallet->setFolder(KWallet::Wallet::FormDataFolder())) {
00337 
00338         // Do pending fill requests...
00339         if (!pendingFillRequests.isEmpty()) {
00340             KUrl::List urlList;
00341             QMutableHashIterator<KUrl, FormsData> requestIt (pendingFillRequests);
00342             while (requestIt.hasNext()) {
00343                requestIt.next();
00344                KWebWallet::WebFormList list = requestIt.value().forms;
00345                fillDataFromCache(list);
00346                q->fillWebForm(requestIt.key(), list);
00347             }
00348 
00349             pendingFillRequests.clear();
00350         }
00351 
00352          // Do pending save requests...
00353         if (!pendingSaveRequests.isEmpty()) {
00354             QListIterator<QString> keysIt (pendingSaveRequests.keys());
00355             while (keysIt.hasNext())
00356                 saveDataToCache(keysIt.next());
00357         }
00358 
00359         // Do pending remove requests...
00360         if (!pendingRemoveRequests.isEmpty()) {
00361             removeDataFromCache(pendingRemoveRequests);
00362             pendingRemoveRequests.clear();
00363         }
00364     } else {
00365         // Delete the wallet if opening the wallet failed or we were unable
00366         // to change to the folder we wanted to change to.
00367         delete wallet.take();
00368     }
00369 }
00370 
00371 void KWebWallet::KWebWalletPrivate::_k_walletClosed()
00372 {
00373     if (wallet)
00374       wallet.take()->deleteLater();
00375 
00376     emit q->walletClosed();
00377 }
00378 
00379 KWebWallet::KWebWallet(QObject *parent, WId wid)
00380            :QObject(parent), d(new KWebWalletPrivate(this))
00381 {
00382     if (!wid) {
00383         // If wid is 0, make a best effort attempt to discern it from our
00384         // parent object.
00385         QWidget* widget = topLevelWindow(parent);
00386         if (widget) {
00387             wid = widget->winId();
00388         }
00389     }
00390 
00391     d->wid = wid;
00392 }
00393 
00394 KWebWallet::~KWebWallet()
00395 {
00396     delete d;
00397 }
00398 
00399 KWebWallet::WebFormList KWebWallet::formsWithCachedData(QWebFrame* frame, bool recursive) const
00400 {
00401     WebFormList list;
00402 
00403     if (frame) {
00404         list << d->parseFormData(frame);
00405 
00406         if (recursive) {
00407             QList<QWebFrame*> childFrameList;
00408             collectAllChildFrames(frame, childFrameList);
00409             QListIterator <QWebFrame *> framesIt (childFrameList);
00410             while (framesIt.hasNext()) {
00411                 list << d->parseFormData(framesIt.next());
00412             }
00413         }
00414     }
00415 
00416     return list;
00417 }
00418 
00419 void KWebWallet::fillFormData(QWebFrame *frame, bool recursive)
00420 {
00421     if (!frame)
00422         return;
00423 
00424     KUrl::List urlList;
00425     WebFormList formsList = d->parseFormData(frame);
00426     if (!formsList.isEmpty()) {
00427         const QUrl url (urlForFrame(frame));
00428         if (d->pendingFillRequests.contains(url)) {
00429             kWarning(800) << "Duplicate request rejected!";
00430         } else {
00431             KWebWalletPrivate::FormsData data;
00432             data.frame = QWeakPointer<QWebFrame>(frame);
00433             data.forms << formsList;
00434             d->pendingFillRequests.insert(url, data);
00435             urlList << url;
00436         }
00437     }
00438 
00439     if (recursive) {
00440         QList<QWebFrame*> childFrameList;
00441         collectAllChildFrames(frame, childFrameList);
00442         QListIterator<QWebFrame*> frameIt (childFrameList);
00443         while (frameIt.hasNext()) {
00444             QWebFrame *childFrame = frameIt.next();
00445             formsList = d->parseFormData(childFrame);
00446             if (formsList.isEmpty())
00447                 continue;
00448             const QUrl url (childFrame->url());
00449             if (d->pendingFillRequests.contains(url)) {
00450                 kWarning(800) << "Duplicate request rejected!!!";
00451             } else {
00452                 KWebWalletPrivate::FormsData data;
00453                 data.frame = QWeakPointer<QWebFrame>(childFrame);
00454                 data.forms << formsList;
00455                 d->pendingFillRequests.insert(url, data);
00456                 urlList << url;
00457             }
00458         }
00459     }
00460 
00461     if (!urlList.isEmpty())
00462         fillFormDataFromCache(urlList);
00463 }
00464 
00465 static void createSaveKeyFor(QWebFrame* frame, QString* key)
00466 {
00467     QUrl frameUrl(urlForFrame(frame));
00468     frameUrl.setPassword(QString());
00469     frameUrl.setPassword(QString());
00470 
00471     QString keyStr = frameUrl.toString();
00472     if (!frame->frameName().isEmpty())
00473         keyStr += frame->frameName();
00474 
00475     *key = QString::number(qHash(keyStr), 16);
00476 }
00477 
00478 void KWebWallet::saveFormData(QWebFrame *frame, bool recursive, bool ignorePasswordFields)
00479 {
00480     if (!frame)
00481         return;
00482 
00483     QString key;
00484     createSaveKeyFor(frame, &key);
00485     if (d->pendingSaveRequests.contains(key))
00486         return;
00487 
00488     WebFormList list = d->parseFormData(frame, false, ignorePasswordFields);
00489     if (recursive) {
00490         QList<QWebFrame*> childFrameList;
00491         collectAllChildFrames(frame, childFrameList);
00492         QListIterator<QWebFrame*> frameIt (childFrameList);
00493         while (frameIt.hasNext())
00494             list << d->parseFormData(frameIt.next(), false, ignorePasswordFields);
00495     }
00496 
00497     if (list.isEmpty())
00498         return;
00499 
00500     d->pendingSaveRequests.insert(key, list);
00501 
00502     QMutableListIterator<WebForm> it (list);
00503     while (it.hasNext()) {
00504         const WebForm form (it.next());
00505         if (hasCachedFormData(form))
00506             it.remove();
00507     }
00508 
00509     if (list.isEmpty()) {
00510         d->confirmSaveRequestOverwrites.insert(urlForFrame(frame));
00511         saveFormDataToCache(key);
00512         return;
00513     }
00514 
00515     emit saveFormDataRequested(key, urlForFrame(frame));
00516 }
00517 
00518 void KWebWallet::removeFormData(QWebFrame *frame, bool recursive)
00519 {
00520     if (frame)
00521         removeFormDataFromCache(formsWithCachedData(frame, recursive));
00522 }
00523 
00524 void KWebWallet::removeFormData(const WebFormList &forms)
00525 {
00526     d->pendingRemoveRequests << forms;
00527     removeFormDataFromCache(forms);
00528 }
00529 
00530 void KWebWallet::acceptSaveFormDataRequest(const QString &key)
00531 {
00532     saveFormDataToCache(key);
00533 }
00534 
00535 void KWebWallet::rejectSaveFormDataRequest(const QString & key)
00536 {
00537     d->pendingSaveRequests.remove(key);
00538 }
00539 
00540 void KWebWallet::fillWebForm(const KUrl &url, const KWebWallet::WebFormList &forms)
00541 {
00542     QWeakPointer<QWebFrame> frame = d->pendingFillRequests.value(url).frame;
00543     if (!frame)
00544         return;
00545 
00546     QString script;
00547     bool wasFilled = false;
00548 
00549     Q_FOREACH (const KWebWallet::WebForm& form, forms) {
00550         Q_FOREACH(const KWebWallet::WebForm::WebField& field, form.fields) {
00551             QString value = field.second;
00552             value.replace(QL1C('\\'), QL1S("\\\\"));
00553             script += QString::fromLatin1("if (document.forms[\"%1\"].elements[\"%2\"]) document.forms[\"%1\"].elements[\"%2\"].value=\"%3\";\n")
00554                         .arg((form.name.isEmpty() ? form.index : form.name))
00555                         .arg(field.first).arg(value);
00556         }
00557     }
00558 
00559     if (!script.isEmpty()) {
00560         wasFilled = true;
00561         frame.data()->evaluateJavaScript(script);
00562     }
00563 
00564     emit fillFormRequestCompleted(wasFilled);
00565 }
00566 
00567 KWebWallet::WebFormList KWebWallet::formsToFill(const KUrl &url) const
00568 {
00569     return d->pendingFillRequests.value(url).forms;
00570 }
00571 
00572 KWebWallet::WebFormList KWebWallet::formsToSave(const QString &key) const
00573 {
00574     return d->pendingSaveRequests.value(key);
00575 }
00576 
00577 bool KWebWallet::hasCachedFormData(const WebForm &form) const
00578 {
00579     return !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
00580                                              KWallet::Wallet::FormDataFolder(),
00581                                              walletKey(form));
00582 }
00583 
00584 void KWebWallet::fillFormDataFromCache(const KUrl::List &urlList)
00585 {
00586     if (d->wallet) {
00587         QListIterator<KUrl> urlIt (urlList);
00588         while (urlIt.hasNext()) {
00589             const KUrl url = urlIt.next();
00590             WebFormList list = formsToFill(url);
00591             d->fillDataFromCache(list);
00592             fillWebForm(url, list);
00593         }
00594         d->pendingFillRequests.clear();
00595     }
00596     d->openWallet();
00597 }
00598 
00599 void KWebWallet::saveFormDataToCache(const QString &key)
00600 {
00601     if (d->wallet) {
00602         d->saveDataToCache(key);
00603         return;
00604     }
00605     d->openWallet();
00606 }
00607 
00608 void KWebWallet::removeFormDataFromCache(const WebFormList &forms)
00609 {
00610     if (d->wallet) {
00611         d->removeDataFromCache(forms);
00612         d->pendingRemoveRequests.clear();
00613         return;
00614     }
00615     d->openWallet();
00616 }
00617 
00618 #include "kwebwallet.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:37:47 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEWebKit

Skip menu "KDEWebKit"
  • Main Page
  • Namespace List
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.9.5 API Reference

Skip menu "kdelibs-4.9.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal