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
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.