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