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

KDEWebKit

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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.3
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