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