• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.4 API Reference
  • KDE Home
  • Contact Us
 

KDEWebKit

  • kdewebkit
kwebwallet.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the KDE project.
3  *
4  * Copyright (C) 2009 Dawit Alemayehu <adawit@kde.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB. If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "kwebwallet.h"
24 
25 #include <kwallet.h>
26 #include <kdebug.h>
27 
28 #include <QtCore/QSet>
29 #include <QtCore/QHash>
30 #include <QtCore/QFile>
31 #include <QtCore/QWeakPointer>
32 #include <QtCore/QScopedPointer>
33 #include <QtWebKit/QWebPage>
34 #include <QtWebKit/QWebFrame>
35 #include <QtWebKit/QWebElement>
36 #include <QtWebKit/QWebElementCollection>
37 #include <qwindowdefs.h>
38 
39 #define QL1S(x) QLatin1String(x)
40 #define QL1C(x) QLatin1Char(x)
41 
42 // The following form parsing JS code was adapted from Arora project.
43 // See https://github.com/Arora/arora/blob/master/src/data/parseForms.js
44 #define FORM_PARSING_JS "(function (){ \
45  var forms; \
46  var doc = (this.contentDocument ? this.contentDocument : document); \
47  var numForms = doc.forms.length; \
48  if (numForms > 0 ) { \
49  forms = new Array; \
50  for (var i = 0; i < numForms; ++i) { \
51  var form = document.forms[i]; \
52  if (form.method.toLowerCase() != 'post') \
53  continue; \
54  var formObject = new Object; \
55  formObject.name = form.name; \
56  formObject.index = i; \
57  var elements = new Array; \
58  var numElements = form.elements.length; \
59  for (var j = 0; j < numElements; ++j) { \
60  var e = form.elements[j]; \
61  var element = new Object; \
62  element.name = e.name; \
63  element.value = e.value; \
64  element.type = e.type; \
65  element.readonly = e.hasAttribute('readonly'); \
66  element.disabled = e.hasAttribute('disabled'); \
67  if (element.autocomplete != null) \
68  element.autocomplete = element.autocomplete.value; \
69  elements.push(element); \
70  } \
71  formObject.elements = elements; \
72  forms.push(formObject); \
73  } \
74  } \
75  return forms; \
76 }())"
77 
78 
83 static QString walletKey(KWebWallet::WebForm form)
84 {
85  QString key = form.url.toString(QUrl::RemoveQuery|QUrl::RemoveFragment);
86  key += QL1C('#');
87  key += form.name;
88  return key;
89 }
90 
91 static void collectAllChildFrames(QWebFrame* frame, QList<QWebFrame*>& list)
92 {
93  list << frame->childFrames();
94  QListIterator<QWebFrame*> it(frame->childFrames());
95  while (it.hasNext()) {
96  collectAllChildFrames(it.next(), list);
97  }
98 }
99 
100 static QUrl urlForFrame(QWebFrame* frame)
101 {
102  return (frame->url().isEmpty() ? frame->baseUrl().resolved(frame->url()) : frame->url());
103 }
104 
105 /*
106  Returns the top most window associated with widget.
107 
108  Unlike QWidget::window(), this function does its best to find and return the
109  main application window associated with a given widget. It will not stop when
110  it encounters a dialog which likely "has (or could have) a window-system frame".
111 */
112 static QWidget* topLevelWindow(QObject* obj)
113 {
114  QWebPage *page = qobject_cast<QWebPage*>(obj);
115  QWidget* widget = (page ? page->view() : qobject_cast<QWidget*>(page));
116  while (widget && widget->parentWidget()) {
117  widget = widget->parentWidget();
118  }
119  return (widget ? widget->window() : 0);
120 }
121 
122 class KWebWallet::KWebWalletPrivate
123 {
124 public:
125  struct FormsData
126  {
127  QWeakPointer<QWebFrame> frame;
128  KWebWallet::WebFormList forms;
129  };
130 
131  KWebWalletPrivate(KWebWallet* parent);
132  KWebWallet::WebFormList parseFormData(QWebFrame* frame, bool fillform = true, bool ignorepasswd = false);
133  void fillDataFromCache(KWebWallet::WebFormList &formList);
134  void saveDataToCache(const QString &key);
135  void removeDataFromCache(const WebFormList &formList);
136  void openWallet();
137 
138  // Private slots...
139  void _k_openWalletDone(bool);
140  void _k_walletClosed();
141 
142  WId wid;
143  KWebWallet *q;
144  QScopedPointer<KWallet::Wallet> wallet;
145  KWebWallet::WebFormList pendingRemoveRequests;
146  QHash<KUrl, FormsData> pendingFillRequests;
147  QHash<QString, KWebWallet::WebFormList> pendingSaveRequests;
148  QSet<KUrl> confirmSaveRequestOverwrites;
149 };
150 
151 KWebWallet::KWebWalletPrivate::KWebWalletPrivate(KWebWallet *parent)
152  :wid (0), q(parent)
153 {
154 }
155 
156 KWebWallet::WebFormList KWebWallet::KWebWalletPrivate::parseFormData(QWebFrame *frame, bool fillform, bool ignorepasswd)
157 {
158  Q_ASSERT(frame);
159 
160  KWebWallet::WebFormList list;
161 
162  // Execute the javscript to obtain the necessary fields...
163  QVariantList results = frame->evaluateJavaScript(QL1S(FORM_PARSING_JS)).toList();
164  Q_FOREACH (const QVariant &formVariant, results) {
165  QVariantMap map = formVariant.toMap();
166  KWebWallet::WebForm form;
167  form.url = urlForFrame(frame);
168  form.name = map[QL1S("name")].toString();
169  form.index = map[QL1S("index")].toString();
170  bool formHasPasswords = false;
171  const QVariantList elements = map[QL1S("elements")].toList();
172  QList<KWebWallet::WebForm::WebField> inputFields;
173  Q_FOREACH (const QVariant &element, elements) {
174  QVariantMap elementMap = element.toMap();
175  const QString name = elementMap[QL1S("name")].toString();
176  const QString value = (ignorepasswd ? QString() : elementMap[QL1S("value")].toString());
177  const QString type = elementMap[QL1S("type")].toString();
178  const bool isPasswdInput = (type.compare(QL1S("password"), Qt::CaseInsensitive) == 0);
179  const bool isTextInput = (type.compare(QL1S("text"), Qt::CaseInsensitive) == 0);
180  const bool autoCompleteOff = (elementMap[QL1S("autocomplete")].toString().compare(QL1S("off"), Qt::CaseInsensitive) == 0);
181  if (name.isEmpty())
182  continue;
183  if (!isPasswdInput && !isTextInput)
184  continue;
185  if (autoCompleteOff)
186  continue;
187  if (elementMap[QL1S("disabled")].toBool())
188  continue;
189  if (fillform && elementMap[QL1S("readonly")].toBool())
190  continue;
191  if (isPasswdInput && !fillform && value.isEmpty())
192  continue;
193  if (isPasswdInput)
194  formHasPasswords = true;
195  inputFields.append(qMakePair(name, value));
196  }
197 
198  // Only add the input fields on form save requests...
199  if (formHasPasswords && !fillform)
200  form.fields = inputFields;
201 
202  // Add the form to the list if we are saving it or it has cached data.
203  if ((fillform && q->hasCachedFormData(form)) || (!fillform && !form.fields.isEmpty()))
204  list << form;
205  }
206  return list;
207 }
208 
209 void KWebWallet::KWebWalletPrivate::fillDataFromCache(KWebWallet::WebFormList &formList)
210 {
211  if (!wallet) {
212  kWarning(800) << "Unable to retrieve form data from wallet";
213  return;
214  }
215 
216  QMap<QString, QString> cachedValues;
217  QMutableListIterator <WebForm> formIt (formList);
218 
219  while (formIt.hasNext()) {
220  KWebWallet::WebForm &form = formIt.next();
221  const QString key (walletKey(form));
222  if (wallet->readMap(key, cachedValues) != 0) {
223  kWarning(800) << "Unable to read form data for key:" << key;
224  continue;
225  }
226 
227  QMapIterator<QString, QString> valuesIt (cachedValues);
228  while (valuesIt.hasNext()) {
229  valuesIt.next();
230  //kDebug(800) << "wallet key:" << key << valuesIt.key() << valuesIt.value();
231  form.fields << qMakePair(valuesIt.key(), valuesIt.value());
232  }
233  }
234 }
235 
236 void KWebWallet::KWebWalletPrivate::saveDataToCache(const QString &key)
237 {
238  // Make sure the specified keys exists before acting on it. See BR# 270209.
239  if (!pendingSaveRequests.contains(key)) {
240  return;
241  }
242 
243  bool success = false;
244  const QUrl url = pendingSaveRequests.value(key).first().url;
245 
246  if (wallet) {
247  int count = 0;
248  const KWebWallet::WebFormList list = pendingSaveRequests.value(key);
249  QListIterator<KWebWallet::WebForm> formIt (list);
250 
251  while (formIt.hasNext()) {
252  QMap<QString, QString> values, storedValues;
253  const KWebWallet::WebForm form = formIt.next();
254  const QString accessKey = walletKey(form);
255  if (confirmSaveRequestOverwrites.contains(url)) {
256  confirmSaveRequestOverwrites.remove(url);
257  const int status = wallet->readMap(accessKey, storedValues);
258  if (status == 0 && storedValues.count()) {
259  QListIterator<KWebWallet::WebForm::WebField> fieldIt (form.fields);
260  while (fieldIt.hasNext()) {
261  const KWebWallet::WebForm::WebField field = fieldIt.next();
262  if (storedValues.contains(field.first) &&
263  storedValues.value(field.first) != field.second) {
264  emit q->saveFormDataRequested(key, url);
265  return;
266  }
267  }
268  // If we got here it means the new credential is exactly
269  // the same as the one already cached ; so skip the
270  // re-saving part...
271  success = true;
272  continue;
273  }
274  }
275  QListIterator<KWebWallet::WebForm::WebField> fieldIt (form.fields);
276  while (fieldIt.hasNext()) {
277  const KWebWallet::WebForm::WebField field = fieldIt.next();
278  values.insert(field.first, field.second);
279  }
280 
281  if (wallet->writeMap(accessKey, values) == 0)
282  count++;
283  else
284  kWarning(800) << "Unable to write form data to wallet";
285  }
286 
287  if (list.isEmpty() || count > 0)
288  success = true;
289 
290  pendingSaveRequests.remove(key);
291  } else {
292  kWarning(800) << "NULL KWallet instance!";
293  }
294 
295  emit q->saveFormDataCompleted(url, success);
296 }
297 
298 void KWebWallet::KWebWalletPrivate::openWallet()
299 {
300  if (!wallet.isNull()) {
301  return;
302  }
303 
304  wallet.reset(KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(),
305  wid, KWallet::Wallet::Asynchronous));
306 
307  if (wallet.isNull()) {
308  return;
309  }
310 
311  connect(wallet.data(), SIGNAL(walletOpened(bool)), q, SLOT(_k_openWalletDone(bool)));
312  connect(wallet.data(), SIGNAL(walletClosed()), q, SLOT(_k_walletClosed()));
313 }
314 
315 
316 void KWebWallet::KWebWalletPrivate::removeDataFromCache(const WebFormList &formList)
317 {
318  if (!wallet) {
319  kWarning(800) << "NULL KWallet instance!";
320  return;
321  }
322 
323  QListIterator<WebForm> formIt (formList);
324  while (formIt.hasNext())
325  wallet->removeEntry(walletKey(formIt.next()));
326 }
327 
328 void KWebWallet::KWebWalletPrivate::_k_openWalletDone(bool ok)
329 {
330  Q_ASSERT (wallet);
331 
332  if (ok &&
333  (wallet->hasFolder(KWallet::Wallet::FormDataFolder()) ||
334  wallet->createFolder(KWallet::Wallet::FormDataFolder())) &&
335  wallet->setFolder(KWallet::Wallet::FormDataFolder())) {
336 
337  // Do pending fill requests...
338  if (!pendingFillRequests.isEmpty()) {
339  KUrl::List urlList;
340  QMutableHashIterator<KUrl, FormsData> requestIt (pendingFillRequests);
341  while (requestIt.hasNext()) {
342  requestIt.next();
343  KWebWallet::WebFormList list = requestIt.value().forms;
344  fillDataFromCache(list);
345  q->fillWebForm(requestIt.key(), list);
346  }
347 
348  pendingFillRequests.clear();
349  }
350 
351  // Do pending save requests...
352  if (!pendingSaveRequests.isEmpty()) {
353  QListIterator<QString> keysIt (pendingSaveRequests.keys());
354  while (keysIt.hasNext())
355  saveDataToCache(keysIt.next());
356  }
357 
358  // Do pending remove requests...
359  if (!pendingRemoveRequests.isEmpty()) {
360  removeDataFromCache(pendingRemoveRequests);
361  pendingRemoveRequests.clear();
362  }
363  } else {
364  // Delete the wallet if opening the wallet failed or we were unable
365  // to change to the folder we wanted to change to.
366  delete wallet.take();
367  }
368 }
369 
370 void KWebWallet::KWebWalletPrivate::_k_walletClosed()
371 {
372  if (wallet)
373  wallet.take()->deleteLater();
374 
375  emit q->walletClosed();
376 }
377 
378 KWebWallet::KWebWallet(QObject *parent, WId wid)
379  :QObject(parent), d(new KWebWalletPrivate(this))
380 {
381  if (!wid) {
382  // If wid is 0, make a best effort attempt to discern it from our
383  // parent object.
384  QWidget* widget = topLevelWindow(parent);
385  if (widget) {
386  wid = widget->winId();
387  }
388  }
389 
390  d->wid = wid;
391 }
392 
393 KWebWallet::~KWebWallet()
394 {
395  delete d;
396 }
397 
398 KWebWallet::WebFormList KWebWallet::formsWithCachedData(QWebFrame* frame, bool recursive) const
399 {
400  WebFormList list;
401 
402  if (frame) {
403  list << d->parseFormData(frame);
404 
405  if (recursive) {
406  QList<QWebFrame*> childFrameList;
407  collectAllChildFrames(frame, childFrameList);
408  QListIterator <QWebFrame *> framesIt (childFrameList);
409  while (framesIt.hasNext()) {
410  list << d->parseFormData(framesIt.next());
411  }
412  }
413  }
414 
415  return list;
416 }
417 
418 void KWebWallet::fillFormData(QWebFrame *frame, bool recursive)
419 {
420  if (!frame)
421  return;
422 
423  KUrl::List urlList;
424  WebFormList formsList = d->parseFormData(frame);
425  if (!formsList.isEmpty()) {
426  const QUrl url (urlForFrame(frame));
427  if (d->pendingFillRequests.contains(url)) {
428  kWarning(800) << "Duplicate request rejected!";
429  } else {
430  KWebWalletPrivate::FormsData data;
431  data.frame = QWeakPointer<QWebFrame>(frame);
432  data.forms << formsList;
433  d->pendingFillRequests.insert(url, data);
434  urlList << url;
435  }
436  }
437 
438  if (recursive) {
439  QList<QWebFrame*> childFrameList;
440  collectAllChildFrames(frame, childFrameList);
441  QListIterator<QWebFrame*> frameIt (childFrameList);
442  while (frameIt.hasNext()) {
443  QWebFrame *childFrame = frameIt.next();
444  formsList = d->parseFormData(childFrame);
445  if (formsList.isEmpty())
446  continue;
447  const QUrl url (childFrame->url());
448  if (d->pendingFillRequests.contains(url)) {
449  kWarning(800) << "Duplicate request rejected!!!";
450  } else {
451  KWebWalletPrivate::FormsData data;
452  data.frame = QWeakPointer<QWebFrame>(childFrame);
453  data.forms << formsList;
454  d->pendingFillRequests.insert(url, data);
455  urlList << url;
456  }
457  }
458  }
459 
460  if (!urlList.isEmpty())
461  fillFormDataFromCache(urlList);
462 }
463 
464 void KWebWallet::saveFormData(QWebFrame *frame, bool recursive, bool ignorePasswordFields)
465 {
466  if (!frame)
467  return;
468 
469  WebFormList list = d->parseFormData(frame, false, ignorePasswordFields);
470  if (recursive) {
471  QList<QWebFrame*> childFrameList;
472  collectAllChildFrames(frame, childFrameList);
473  QListIterator<QWebFrame*> frameIt (childFrameList);
474  while (frameIt.hasNext())
475  list << d->parseFormData(frameIt.next(), false, ignorePasswordFields);
476  }
477 
478  if (list.isEmpty())
479  return;
480 
481  const QString key = QString::number(qHash(urlForFrame(frame).toString() + frame->frameName()), 16);
482  const bool isAlreadyPending = d->pendingSaveRequests.contains(key);
483  d->pendingSaveRequests.insert(key, list);
484 
485  if (isAlreadyPending)
486  return;
487 
488  for (int i = 0 ; i < list.count(); ++i) {
489  if (hasCachedFormData(list.at(i)))
490  list.takeAt(i);
491  }
492 
493  if (list.isEmpty()) {
494  d->confirmSaveRequestOverwrites.insert(urlForFrame(frame));
495  saveFormDataToCache(key);
496  return;
497  }
498 
499  emit saveFormDataRequested(key, urlForFrame(frame));
500 }
501 
502 void KWebWallet::removeFormData(QWebFrame *frame, bool recursive)
503 {
504  if (frame)
505  removeFormDataFromCache(formsWithCachedData(frame, recursive));
506 }
507 
508 void KWebWallet::removeFormData(const WebFormList &forms)
509 {
510  d->pendingRemoveRequests << forms;
511  removeFormDataFromCache(forms);
512 }
513 
514 void KWebWallet::acceptSaveFormDataRequest(const QString &key)
515 {
516  saveFormDataToCache(key);
517 }
518 
519 void KWebWallet::rejectSaveFormDataRequest(const QString & key)
520 {
521  d->pendingSaveRequests.remove(key);
522 }
523 
524 void KWebWallet::fillWebForm(const KUrl &url, const KWebWallet::WebFormList &forms)
525 {
526  QWeakPointer<QWebFrame> frame = d->pendingFillRequests.value(url).frame;
527  if (!frame)
528  return;
529 
530  QString script;
531  bool wasFilled = false;
532 
533  Q_FOREACH (const KWebWallet::WebForm& form, forms) {
534  Q_FOREACH(const KWebWallet::WebForm::WebField& field, form.fields) {
535  QString value = field.second;
536  value.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
537  script += QString::fromLatin1("if (document.forms[\"%1\"].elements[\"%2\"]) document.forms[\"%1\"].elements[\"%2\"].value=\"%3\";\n")
538  .arg((form.name.isEmpty() ? form.index : form.name))
539  .arg(field.first).arg(value);
540  }
541  }
542 
543  if (!script.isEmpty()) {
544  wasFilled = true;
545  frame.data()->evaluateJavaScript(script);
546  }
547 
548  emit fillFormRequestCompleted(wasFilled);
549 }
550 
551 KWebWallet::WebFormList KWebWallet::formsToFill(const KUrl &url) const
552 {
553  return d->pendingFillRequests.value(url).forms;
554 }
555 
556 KWebWallet::WebFormList KWebWallet::formsToSave(const QString &key) const
557 {
558  return d->pendingSaveRequests.value(key);
559 }
560 
561 bool KWebWallet::hasCachedFormData(const WebForm &form) const
562 {
563  return !KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
564  KWallet::Wallet::FormDataFolder(),
565  walletKey(form));
566 }
567 
568 void KWebWallet::fillFormDataFromCache(const KUrl::List &urlList)
569 {
570  if (d->wallet) {
571  QListIterator<KUrl> urlIt (urlList);
572  while (urlIt.hasNext()) {
573  const KUrl url = urlIt.next();
574  WebFormList list = formsToFill(url);
575  d->fillDataFromCache(list);
576  fillWebForm(url, list);
577  }
578  d->pendingFillRequests.clear();
579  }
580  d->openWallet();
581 }
582 
583 void KWebWallet::saveFormDataToCache(const QString &key)
584 {
585  if (d->wallet) {
586  d->saveDataToCache(key);
587  return;
588  }
589  d->openWallet();
590 }
591 
592 void KWebWallet::removeFormDataFromCache(const WebFormList &forms)
593 {
594  if (d->wallet) {
595  d->removeDataFromCache(forms);
596  d->pendingRemoveRequests.clear();
597  return;
598  }
599  d->openWallet();
600 }
601 
602 #include "kwebwallet.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Tue Jul 17 2012 07:39:23 by doxygen 1.8.1 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.4 API Reference

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