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

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