• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.3 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 // The following form parsing JS code was adapted from Arora project.
00043 // See https://github.com/Arora/arora/blob/master/src/data/parseForms.js
00044 #define FORM_PARSING_JS "(function (){ \
00045     var forms; \
00046     var doc = (this.contentDocument ? this.contentDocument : document); \
00047     var numForms = doc.forms.length; \
00048     if (numForms > 0 ) { \
00049         forms = new Array; \
00050         for (var i = 0; i < numForms; ++i) { \
00051             var form = document.forms[i]; \
00052             if (form.method.toLowerCase() != 'post') \
00053                 continue; \
00054             var formObject = new Object; \
00055             formObject.name = form.name; \
00056             formObject.index = i; \
00057             var elements = new Array; \
00058             var numElements = form.elements.length; \
00059             for (var j = 0; j < numElements; ++j) { \
00060                 var e = form.elements[j]; \
00061                 var element = new Object; \
00062                 element.name = e.name; \
00063                 element.value = e.value; \
00064                 element.type = e.type; \
00065                 element.readonly = e.hasAttribute('readonly'); \
00066                 element.disabled = e.hasAttribute('disabled'); \
00067                 if (element.autocomplete != null)  \
00068                     element.autocomplete = element.autocomplete.value; \
00069                 elements.push(element); \
00070             } \
00071             formObject.elements = elements; \
00072             forms.push(formObject); \
00073         } \
00074     } \
00075     return forms; \
00076 }())"
00077 
00078 
00083 static QString walletKey(KWebWallet::WebForm form)
00084 {
00085     QString key = form.url.toString(QUrl::RemoveQuery|QUrl::RemoveFragment);
00086     key += QL1C('#');
00087     key += form.name;
00088     return key;
00089 }
00090 
00091 static void collectAllChildFrames(QWebFrame* frame, QList<QWebFrame*>& list)
00092 {
00093     list << frame->childFrames();
00094     QListIterator<QWebFrame*> it(frame->childFrames());
00095     while (it.hasNext()) {
00096         collectAllChildFrames(it.next(), list);
00097     }
00098 }
00099 
00100 static QUrl urlForFrame(QWebFrame* frame)
00101 {
00102     return (frame->url().isEmpty() ? frame->baseUrl().resolved(frame->url()) : frame->url());
00103 }
00104 
00105 /*
00106   Returns the top most window associated with widget.
00107 
00108   Unlike QWidget::window(), this function does its best to find and return the
00109   main application window associated with a given widget. It will not stop when
00110   it encounters a dialog which likely "has (or could have) a window-system frame".
00111 */
00112 static QWidget* topLevelWindow(QObject* obj)
00113 {
00114     QWebPage *page = qobject_cast<QWebPage*>(obj);
00115     QWidget* widget = (page ? page->view() : qobject_cast<QWidget*>(page));
00116     while (widget && widget->parentWidget()) {
00117         widget = widget->parentWidget();
00118     }
00119     return (widget ? widget->window() : 0);
00120 }
00121 
00122 class KWebWallet::KWebWalletPrivate
00123 {
00124 public:
00125     struct FormsData
00126     {
00127         QWeakPointer<QWebFrame> frame;
00128         KWebWallet::WebFormList forms;
00129     };
00130 
00131     KWebWalletPrivate(KWebWallet* parent);
00132     KWebWallet::WebFormList parseFormData(QWebFrame* frame, bool fillform = true, bool ignorepasswd = false);
00133     void fillDataFromCache(KWebWallet::WebFormList &formList);
00134     void saveDataToCache(const QString &key);
00135     void removeDataFromCache(const WebFormList &formList);
00136     void openWallet();
00137 
00138     // Private slots...
00139     void _k_openWalletDone(bool);
00140     void _k_walletClosed();
00141 
00142     WId wid;
00143     KWebWallet *q;
00144     QScopedPointer<KWallet::Wallet> wallet;
00145     KWebWallet::WebFormList pendingRemoveRequests;
00146     QHash<KUrl, FormsData> pendingFillRequests;
00147     QHash<QString, KWebWallet::WebFormList> pendingSaveRequests;
00148     QSet<KUrl> confirmSaveRequestOverwrites;
00149 };
00150 
00151 KWebWallet::KWebWalletPrivate::KWebWalletPrivate(KWebWallet *parent)
00152                               :wid (0), q(parent)
00153 {
00154 }
00155 
00156 KWebWallet::WebFormList KWebWallet::KWebWalletPrivate::parseFormData(QWebFrame *frame, bool fillform, bool ignorepasswd)
00157 {
00158     Q_ASSERT(frame);
00159 
00160     KWebWallet::WebFormList list;
00161 
00162     // Execute the javscript to obtain the necessary fields...
00163     QVariantList results = frame->evaluateJavaScript(QL1S(FORM_PARSING_JS)).toList();
00164     Q_FOREACH (const QVariant &formVariant, results) {
00165         QVariantMap map = formVariant.toMap();
00166         KWebWallet::WebForm form;
00167         form.url = urlForFrame(frame);
00168         form.name = map[QL1S("name")].toString();
00169         form.index = map[QL1S("index")].toString();
00170         bool formHasPasswords = false;
00171         const QVariantList elements = map[QL1S("elements")].toList();
00172         QList<KWebWallet::WebForm::WebField> inputFields;
00173         Q_FOREACH (const QVariant &element, elements) {
00174             QVariantMap elementMap = element.toMap();
00175             const QString name = elementMap[QL1S("name")].toString();
00176             const QString value = (ignorepasswd ? QString() : elementMap[QL1S("value")].toString());
00177             const QString type = elementMap[QL1S("type")].toString();
00178             const bool isPasswdInput = (type.compare(QL1S("password"), Qt::CaseInsensitive) == 0);
00179             const bool isTextInput = (type.compare(QL1S("text"), Qt::CaseInsensitive) == 0);
00180             const bool autoCompleteOff = (elementMap[QL1S("autocomplete")].toString().compare(QL1S("off"), Qt::CaseInsensitive) == 0);
00181             if (name.isEmpty())
00182                 continue;
00183             if (!isPasswdInput && !isTextInput)
00184                 continue;
00185             if (autoCompleteOff)
00186                 continue;
00187             if (elementMap[QL1S("disabled")].toBool())
00188                 continue;
00189             if (fillform && elementMap[QL1S("readonly")].toBool())
00190                 continue;
00191             if (isPasswdInput && !fillform && value.isEmpty())
00192                 continue;
00193             if (isPasswdInput)
00194                 formHasPasswords = true;
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         // Add the form to the list if we are saving it or it has cached data.
00203         if ((fillform && q->hasCachedFormData(form)) || (!fillform  && !form.fields.isEmpty()))
00204             list << form;
00205     }
00206     return list;
00207 }
00208 
00209 void KWebWallet::KWebWalletPrivate::fillDataFromCache(KWebWallet::WebFormList &formList)
00210 {
00211     if (!wallet) {
00212         kWarning(800) << "Unable to retrieve form data from wallet";
00213         return;
00214     }
00215 
00216     QMap<QString, QString> cachedValues;
00217     QMutableListIterator <WebForm> formIt (formList);
00218 
00219     while (formIt.hasNext()) {
00220         KWebWallet::WebForm &form = formIt.next();
00221         const QString key (walletKey(form));
00222         if (wallet->readMap(key, cachedValues) != 0) {
00223             kWarning(800) << "Unable to read form data for key:" << key;
00224             continue;
00225         }
00226 
00227         QMapIterator<QString, QString> valuesIt (cachedValues);
00228         while (valuesIt.hasNext()) {
00229             valuesIt.next();
00230             //kDebug(800) << "wallet key:" << key << valuesIt.key() << valuesIt.value();
00231             form.fields << qMakePair(valuesIt.key(), valuesIt.value());
00232         }
00233     }
00234 }
00235 
00236 void KWebWallet::KWebWalletPrivate::saveDataToCache(const QString &key)
00237 {
00238     // Make sure the specified keys exists before acting on it. See BR# 270209.
00239     if (!pendingSaveRequests.contains(key)) {
00240         return;
00241     }
00242 
00243     bool success = false;
00244     const QUrl url = pendingSaveRequests.value(key).first().url;
00245 
00246     if (wallet) {
00247         int count = 0;
00248         const KWebWallet::WebFormList list = pendingSaveRequests.value(key);
00249         QListIterator<KWebWallet::WebForm> formIt (list);
00250 
00251         while (formIt.hasNext()) {
00252             QMap<QString, QString> values, storedValues;
00253             const KWebWallet::WebForm form = formIt.next();
00254             const QString accessKey = walletKey(form);
00255             if (confirmSaveRequestOverwrites.contains(url)) {
00256                 confirmSaveRequestOverwrites.remove(url);
00257                 const int status = wallet->readMap(accessKey, storedValues);
00258                 if (status == 0 && storedValues.count()) {
00259                     QListIterator<KWebWallet::WebForm::WebField> fieldIt (form.fields);
00260                     while (fieldIt.hasNext()) {
00261                         const KWebWallet::WebForm::WebField field = fieldIt.next();
00262                         if (storedValues.contains(field.first) &&
00263                             storedValues.value(field.first) != field.second) {
00264                             emit q->saveFormDataRequested(key, url);
00265                             return;
00266                         }
00267                     }
00268                     // If we got here it means the new credential is exactly
00269                     // the same as the one already cached ; so skip the
00270                     // re-saving part...
00271                     success = true;
00272                     continue;
00273                 }
00274             }
00275             QListIterator<KWebWallet::WebForm::WebField> fieldIt (form.fields);
00276             while (fieldIt.hasNext()) {
00277                 const KWebWallet::WebForm::WebField field = fieldIt.next();
00278                 values.insert(field.first, field.second);
00279             }
00280 
00281             if (wallet->writeMap(accessKey, values) == 0)
00282                 count++;
00283             else
00284                 kWarning(800) << "Unable to write form data to wallet";
00285         }
00286 
00287         if (list.isEmpty() || count > 0)
00288           success = true;
00289 
00290         pendingSaveRequests.remove(key);
00291     } else {
00292         kWarning(800) << "NULL KWallet instance!";
00293     }
00294 
00295     emit q->saveFormDataCompleted(url, success);
00296 }
00297 
00298 void KWebWallet::KWebWalletPrivate::openWallet()
00299 {
00300     if (!wallet.isNull()) {
00301         return;
00302     }
00303 
00304     wallet.reset(KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(),
00305                                              wid, KWallet::Wallet::Asynchronous));
00306 
00307     if (wallet.isNull()) {
00308         return;
00309     }
00310 
00311     connect(wallet.data(), SIGNAL(walletOpened(bool)), q, SLOT(_k_openWalletDone(bool)));
00312     connect(wallet.data(), SIGNAL(walletClosed()), q, SLOT(_k_walletClosed()));
00313 }
00314 
00315 
00316 void KWebWallet::KWebWalletPrivate::removeDataFromCache(const WebFormList &formList)
00317 {
00318     if (!wallet) {
00319         kWarning(800) << "NULL KWallet instance!";
00320         return;
00321     }
00322 
00323     QListIterator<WebForm> formIt (formList);
00324     while (formIt.hasNext())
00325         wallet->removeEntry(walletKey(formIt.next()));
00326 }
00327 
00328 void KWebWallet::KWebWalletPrivate::_k_openWalletDone(bool ok)
00329 {
00330     Q_ASSERT (wallet);
00331 
00332     if (ok &&
00333         (wallet->hasFolder(KWallet::Wallet::FormDataFolder()) ||
00334          wallet->createFolder(KWallet::Wallet::FormDataFolder())) &&
00335          wallet->setFolder(KWallet::Wallet::FormDataFolder())) {
00336 
00337         // Do pending fill requests...
00338         if (!pendingFillRequests.isEmpty()) {
00339             KUrl::List urlList;
00340             QMutableHashIterator<KUrl, FormsData> requestIt (pendingFillRequests);
00341             while (requestIt.hasNext()) {
00342                requestIt.next();
00343                KWebWallet::WebFormList list = requestIt.value().forms;
00344                fillDataFromCache(list);
00345                q->fillWebForm(requestIt.key(), list);
00346             }
00347 
00348             pendingFillRequests.clear();
00349         }
00350 
00351          // Do pending save requests...
00352         if (!pendingSaveRequests.isEmpty()) {
00353             QListIterator<QString> keysIt (pendingSaveRequests.keys());
00354             while (keysIt.hasNext())
00355                 saveDataToCache(keysIt.next());
00356         }
00357 
00358         // Do pending remove requests...
00359         if (!pendingRemoveRequests.isEmpty()) {
00360             removeDataFromCache(pendingRemoveRequests);
00361             pendingRemoveRequests.clear();
00362         }
00363     } else {
00364         // Delete the wallet if opening the wallet failed or we were unable
00365         // to change to the folder we wanted to change to.
00366         delete wallet.take();
00367     }
00368 }
00369 
00370 void KWebWallet::KWebWalletPrivate::_k_walletClosed()
00371 {
00372     if (wallet)
00373       wallet.take()->deleteLater();
00374 
00375     emit q->walletClosed();
00376 }
00377 
00378 KWebWallet::KWebWallet(QObject *parent, WId wid)
00379            :QObject(parent), d(new KWebWalletPrivate(this))
00380 {
00381     if (!wid) {
00382         // If wid is 0, make a best effort attempt to discern it from our
00383         // parent object.
00384         QWidget* widget = topLevelWindow(parent);
00385         if (widget) {
00386             wid = widget->winId();
00387         }
00388     }
00389 
00390     d->wid = wid;
00391 }
00392 
00393 KWebWallet::~KWebWallet()
00394 {
00395     delete d;
00396 }
00397 
00398 KWebWallet::WebFormList KWebWallet::formsWithCachedData(QWebFrame* frame, bool recursive) const
00399 {
00400     WebFormList list;
00401 
00402     if (frame) {
00403         list << d->parseFormData(frame);
00404 
00405         if (recursive) {
00406             QList<QWebFrame*> childFrameList;
00407             collectAllChildFrames(frame, childFrameList);
00408             QListIterator <QWebFrame *> framesIt (childFrameList);
00409             while (framesIt.hasNext()) {
00410                 list << d->parseFormData(framesIt.next());
00411             }
00412         }
00413     }
00414 
00415     return list;
00416 }
00417 
00418 void KWebWallet::fillFormData(QWebFrame *frame, bool recursive)
00419 {
00420     if (!frame)
00421         return;
00422 
00423     KUrl::List urlList;
00424     WebFormList formsList = d->parseFormData(frame);
00425     if (!formsList.isEmpty()) {
00426         const QUrl url (urlForFrame(frame));
00427         if (d->pendingFillRequests.contains(url)) {
00428             kWarning(800) << "Duplicate request rejected!";
00429         } else {
00430             KWebWalletPrivate::FormsData data;
00431             data.frame = QWeakPointer<QWebFrame>(frame);
00432             data.forms << formsList;
00433             d->pendingFillRequests.insert(url, data);
00434             urlList << url;
00435         }
00436     }
00437 
00438     if (recursive) {
00439         QList<QWebFrame*> childFrameList;
00440         collectAllChildFrames(frame, childFrameList);
00441         QListIterator<QWebFrame*> frameIt (childFrameList);
00442         while (frameIt.hasNext()) {
00443             QWebFrame *childFrame = frameIt.next();
00444             formsList = d->parseFormData(childFrame);
00445             if (formsList.isEmpty())
00446                 continue;
00447             const QUrl url (childFrame->url());
00448             if (d->pendingFillRequests.contains(url)) {
00449                 kWarning(800) << "Duplicate request rejected!!!";
00450             } else {
00451                 KWebWalletPrivate::FormsData data;
00452                 data.frame = QWeakPointer<QWebFrame>(childFrame);
00453                 data.forms << formsList;
00454                 d->pendingFillRequests.insert(url, data);
00455                 urlList << url;
00456             }
00457         }
00458     }
00459 
00460     if (!urlList.isEmpty())
00461         fillFormDataFromCache(urlList);
00462 }
00463 
00464 void KWebWallet::saveFormData(QWebFrame *frame, bool recursive, bool ignorePasswordFields)
00465 {
00466     if (!frame)
00467         return;
00468 
00469     WebFormList list = d->parseFormData(frame, false, ignorePasswordFields);
00470     if (recursive) {
00471         QList<QWebFrame*> childFrameList;
00472         collectAllChildFrames(frame, childFrameList);
00473         QListIterator<QWebFrame*> frameIt (childFrameList);
00474         while (frameIt.hasNext())
00475             list << d->parseFormData(frameIt.next(), false, ignorePasswordFields);
00476     }
00477 
00478     if (list.isEmpty())
00479         return;
00480 
00481     const QString key = QString::number(qHash(urlForFrame(frame).toString() + frame->frameName()), 16);
00482     const bool isAlreadyPending = d->pendingSaveRequests.contains(key);
00483     d->pendingSaveRequests.insert(key, list);
00484 
00485     if (isAlreadyPending)
00486         return;
00487 
00488     for (int i = 0 ; i < list.count(); ++i) {
00489         if (hasCachedFormData(list.at(i)))
00490             list.takeAt(i);
00491     }
00492 
00493     if (list.isEmpty()) {
00494         d->confirmSaveRequestOverwrites.insert(urlForFrame(frame));
00495         saveFormDataToCache(key);
00496         return;
00497     }
00498 
00499     emit saveFormDataRequested(key, urlForFrame(frame));
00500 }
00501 
00502 void KWebWallet::removeFormData(QWebFrame *frame, bool recursive)
00503 {
00504     if (frame)
00505         removeFormDataFromCache(formsWithCachedData(frame, recursive));
00506 }
00507 
00508 void KWebWallet::removeFormData(const WebFormList &forms)
00509 {
00510     d->pendingRemoveRequests << forms;
00511     removeFormDataFromCache(forms);
00512 }
00513 
00514 void KWebWallet::acceptSaveFormDataRequest(const QString &key)
00515 {
00516     saveFormDataToCache(key);
00517 }
00518 
00519 void KWebWallet::rejectSaveFormDataRequest(const QString & key)
00520 {
00521     d->pendingSaveRequests.remove(key);
00522 }
00523 
00524 void KWebWallet::fillWebForm(const KUrl &url, const KWebWallet::WebFormList &forms)
00525 {
00526     QWeakPointer<QWebFrame> frame = d->pendingFillRequests.value(url).frame;
00527     if (!frame)
00528         return;
00529 
00530     QString script;
00531     bool wasFilled = false;
00532 
00533     Q_FOREACH (const KWebWallet::WebForm& form, forms) {
00534         Q_FOREACH(const KWebWallet::WebForm::WebField& field, form.fields) {
00535             QString value = field.second;
00536             value.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
00537             script += QString::fromLatin1("document.forms[\"%1\"].elements[\"%2\"].value=\"%3\";\n")
00538                         .arg((form.name.isEmpty() ? form.index : form.name))
00539                         .arg(field.first).arg(value);
00540         }
00541     }
00542 
00543     if (!script.isEmpty()) {
00544         wasFilled = true;
00545         frame.data()->evaluateJavaScript(script);
00546     }
00547 
00548     emit fillFormRequestCompleted(wasFilled);
00549 }
00550 
00551 KWebWallet::WebFormList KWebWallet::formsToFill(const KUrl &url) const
00552 {
00553     return d->pendingFillRequests.value(url).forms;
00554 }
00555 
00556 KWebWallet::WebFormList KWebWallet::formsToSave(const QString &key) const
00557 {
00558     return d->pendingSaveRequests.value(key);
00559 }
00560 
00561 bool KWebWallet::hasCachedFormData(const WebForm &form) const
00562 {
00563     return !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
00564                                              KWallet::Wallet::FormDataFolder(),
00565                                              walletKey(form));
00566 }
00567 
00568 void KWebWallet::fillFormDataFromCache(const KUrl::List &urlList)
00569 {
00570     if (d->wallet) {
00571         QListIterator<KUrl> urlIt (urlList);
00572         while (urlIt.hasNext()) {
00573             const KUrl url = urlIt.next();
00574             WebFormList list = formsToFill(url);
00575             d->fillDataFromCache(list);
00576             fillWebForm(url, list);
00577         }
00578         d->pendingFillRequests.clear();
00579     }
00580     d->openWallet();
00581 }
00582 
00583 void KWebWallet::saveFormDataToCache(const QString &key)
00584 {
00585     if (d->wallet) {
00586         d->saveDataToCache(key);
00587         return;
00588     }
00589     d->openWallet();
00590 }
00591 
00592 void KWebWallet::removeFormDataFromCache(const WebFormList &forms)
00593 {
00594     if (d->wallet) {
00595         d->removeDataFromCache(forms);
00596         d->pendingRemoveRequests.clear();
00597         return;
00598     }
00599     d->openWallet();
00600 }
00601 
00602 #include "kwebwallet.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 18:47:57 by doxygen 1.8.0 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.8.3 API Reference

Skip menu "kdelibs-4.8.3 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