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