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

KDEWebKit

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

kdelibs

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal