1// -*- indent-tabs-mode: t; tab-width: 4; c-basic-offset: 4; -*-
2/*
3 This file is part of the KDE libraries
4
5 Copyright (c) 2002-2004 George Staikos <staikos@kde.org>
6 Copyright (c) 2008 Michael Leupold <lemma@confuego.org>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22
23*/
24
25#include "kwalletd.h"
26
27#include "kbetterthankdialog.h"
28#include "kwalletwizard.h"
29
30#ifdef HAVE_QGPGME
31#include "knewwalletdialog.h"
32#endif
33
34#include <kuniqueapplication.h>
35#include <ktoolinvocation.h>
36#include <kconfig.h>
37#include <kconfiggroup.h>
38#include <kdebug.h>
39#include <kdirwatch.h>
40#include <kglobal.h>
41#include <klocale.h>
42#include <kmessagebox.h>
43#include <kpassworddialog.h>
44#include <knewpassworddialog.h>
45#include <kstandarddirs.h>
46#include <kwalletentry.h>
47#include <kicon.h>
48#include <kwindowsystem.h>
49#include <kpluginfactory.h>
50#include <kpluginloader.h>
51#include <KNotification>
52#ifdef HAVE_QGPGME
53#include <gpgme++/key.h>
54#endif
55
56#include <QtCore/QDir>
57#include <QTextDocument> // Qt::escape
58#include <QtCore/QRegExp>
59#include <QtCore/QTimer>
60#include <QtCore/QEventLoop>
61
62#include <assert.h>
63
64#include "kwalletadaptor.h"
65
66class KWalletTransaction {
67
68 public:
69 explicit KWalletTransaction(QDBusConnection conn)
70 : tType(Unknown), cancelled(false), tId(nextTransactionId), res(-1), connection(conn)
71 {
72 nextTransactionId++;
73 // make sure the id is never < 0 as that's used for the
74 // error conditions.
75 if (nextTransactionId < 0) {
76 nextTransactionId = 0;
77 }
78 }
79
80 ~KWalletTransaction() {
81 }
82
83 enum Type {
84 Unknown,
85 Open,
86 ChangePassword,
87 OpenFail,
88 CloseCancelled
89 };
90 Type tType;
91 QString appid;
92 qlonglong wId;
93 QString wallet;
94 QString service;
95 bool cancelled; // set true if the client dies before open
96 bool modal;
97 bool isPath;
98 int tId; // transaction id
99 int res;
100 QDBusMessage message;
101 QDBusConnection connection;
102
103 private:
104 static int nextTransactionId;
105};
106
107int KWalletTransaction::nextTransactionId = 0;
108
109KWalletD::KWalletD()
110 : QObject(0), _failed(0), _syncTime(5000), _curtrans(0), _useGpg(false) {
111#ifdef HAVE_QGPGME
112 _useGpg = true;
113#endif
114
115 srand(time(0));
116 _showingFailureNotify = false;
117 _closeIdle = false;
118 _idleTime = 0;
119 connect(&_closeTimers, SIGNAL(timedOut(int)), this, SLOT(timedOutClose(int)));
120 connect(&_syncTimers, SIGNAL(timedOut(int)), this, SLOT(timedOutSync(int)));
121
122 (void)new KWalletAdaptor(this);
123 // register services
124 QDBusConnection::sessionBus().registerService(QLatin1String("org.kde.kwalletd"));
125 QDBusConnection::sessionBus().registerObject(QLatin1String("/modules/kwalletd"), this);
126
127#ifdef Q_WS_X11
128 screensaver = 0;
129#endif
130
131 reconfigure();
132 KGlobal::dirs()->addResourceType("kwallet", 0, "share/apps/kwallet");
133 _dw = new KDirWatch(this );
134 _dw->setObjectName( QLatin1String( "KWallet Directory Watcher" ) );
135 _dw->addDir(KGlobal::dirs()->saveLocation("kwallet"));
136 _dw->startScan(true);
137 connect(_dw, SIGNAL(dirty(const QString&)), this, SLOT(emitWalletListDirty()));
138
139 _serviceWatcher.setWatchMode( QDBusServiceWatcher::WatchForOwnerChange );
140 connect(&_serviceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), this, SLOT(slotServiceOwnerChanged(QString,QString,QString)));
141}
142
143
144KWalletD::~KWalletD() {
145#ifdef Q_WS_X11
146 delete screensaver;
147 screensaver = 0;
148#endif
149 closeAllWallets();
150 qDeleteAll(_transactions);
151}
152
153#ifdef Q_WS_X11
154void KWalletD::connectToScreenSaver()
155{
156 screensaver = new QDBusInterface("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver");
157 if (!screensaver->isValid()) {
158 kDebug() << "Service org.freedesktop.ScreenSaver not found. Retrying in 10 seconds...";
159 // keep attempting every 10 seconds
160 QTimer::singleShot(10000, this, SLOT(connectToScreenSaver()));
161 } else {
162 connect(screensaver, SIGNAL(ActiveChanged(bool)), SLOT(screenSaverChanged(bool)));
163 kDebug() << "connected to screen saver service.";
164 }
165}
166#endif
167
168int KWalletD::generateHandle() {
169 int rc;
170
171 // ASSUMPTION: RAND_MAX is fairly large.
172 do {
173 rc = rand();
174 } while (_wallets.contains(rc) || rc == 0);
175
176 return rc;
177}
178
179QPair<int, KWallet::Backend*> KWalletD::findWallet(const QString& walletName) const
180{
181 Wallets::const_iterator it = _wallets.constBegin();
182 const Wallets::const_iterator end = _wallets.constEnd();
183 for (; it != end; ++it) {
184 if (it.value()->walletName() == walletName) {
185 return qMakePair(it.key(), it.value());
186 }
187 }
188 return qMakePair(-1, static_cast<KWallet::Backend*>(0));
189}
190
191bool KWalletD::_processing = false;
192
193void KWalletD::processTransactions() {
194 if (_processing) {
195 return;
196 }
197
198 _processing = true;
199
200 // Process remaining transactions
201 while (!_transactions.isEmpty()) {
202 _curtrans = _transactions.takeFirst();
203 int res;
204
205 assert(_curtrans->tType != KWalletTransaction::Unknown);
206
207 switch (_curtrans->tType) {
208 case KWalletTransaction::Open:
209 res = doTransactionOpen(_curtrans->appid, _curtrans->wallet, _curtrans->isPath,
210 _curtrans->wId, _curtrans->modal, _curtrans->service);
211
212 // multiple requests from the same client
213 // should not produce multiple password
214 // dialogs on a failure
215 if (res < 0) {
216 QList<KWalletTransaction *>::iterator it;
217 for (it = _transactions.begin(); it != _transactions.end(); ++it) {
218 KWalletTransaction *x = *it;
219 if (_curtrans->appid == x->appid && x->tType == KWalletTransaction::Open
220 && x->wallet == _curtrans->wallet && x->wId == _curtrans->wId) {
221 x->tType = KWalletTransaction::OpenFail;
222 }
223 }
224 } else if (_curtrans->cancelled) {
225 // the wallet opened successfully but the application
226 // opening exited/crashed while the dialog was still shown.
227 KWalletTransaction *_xact = new KWalletTransaction(_curtrans->connection);
228 _xact->tType = KWalletTransaction::CloseCancelled;
229 _xact->appid = _curtrans->appid;
230 _xact->wallet = _curtrans->wallet;
231 _xact->service = _curtrans->service;
232 _transactions.append(_xact);
233 }
234
235 // emit the AsyncOpened signal as a reply
236 _curtrans->res = res;
237 emit walletAsyncOpened(_curtrans->tId, res);
238 break;
239
240 case KWalletTransaction::OpenFail:
241 // emit the AsyncOpened signal with an invalid handle
242 _curtrans->res = -1;
243 emit walletAsyncOpened(_curtrans->tId, -1);
244 break;
245
246 case KWalletTransaction::ChangePassword:
247 doTransactionChangePassword(_curtrans->appid, _curtrans->wallet, _curtrans->wId);
248 break;
249
250 case KWalletTransaction::CloseCancelled:
251 doTransactionOpenCancelled(_curtrans->appid, _curtrans->wallet,
252 _curtrans->service);
253 break;
254
255 case KWalletTransaction::Unknown:
256 break;
257 default:
258 break;
259 }
260
261 // send delayed dbus message reply to the caller
262 if (_curtrans->message.type() != QDBusMessage::InvalidMessage) {
263 if (_curtrans->connection.isConnected()) {
264 QDBusMessage reply = _curtrans->message.createReply();
265 reply << _curtrans->res;
266 _curtrans->connection.send(reply);
267 }
268 }
269
270 delete _curtrans;
271 _curtrans = 0;
272 }
273
274 _processing = false;
275}
276
277int KWalletD::openPath(const QString& path, qlonglong wId, const QString& appid) {
278 int tId = openPathAsync(path, wId, appid, false);
279 if (tId < 0) {
280 return tId;
281 }
282
283 // NOTE the real return value will be sent by the dbusmessage delayed reply
284 return 0;
285 // wait for the open-transaction to be processed
286// KWalletOpenLoop loop(this);
287// return loop.waitForAsyncOpen(tId);
288}
289
290int KWalletD::open(const QString& wallet, qlonglong wId, const QString& appid) {
291 if (!_enabled) { // guard
292 return -1;
293 }
294
295 if (!QRegExp("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\.\\+\\_\\s]+$").exactMatch(wallet)) {
296 return -1;
297 }
298
299 KWalletTransaction *xact = new KWalletTransaction(connection());
300 _transactions.append(xact);
301
302 message().setDelayedReply(true);
303 xact->message = message();
304
305 xact->appid = appid;
306 xact->wallet = wallet;
307 xact->wId = wId;
308 xact->modal = true; // mark dialogs as modal, the app has blocking wait
309 xact->tType = KWalletTransaction::Open;
310 xact->isPath = false;
311
312 QTimer::singleShot(0, this, SLOT(processTransactions()));
313 checkActiveDialog();
314 // NOTE the real return value will be sent by the dbusmessage delayed reply
315 return 0;
316}
317
318int KWalletD::openAsync(const QString& wallet, qlonglong wId, const QString& appid,
319 bool handleSession) {
320 if (!_enabled) { // guard
321 return -1;
322 }
323
324 if (!QRegExp("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\.\\+\\_\\s]+$").exactMatch(wallet)) {
325 return -1;
326 }
327
328 KWalletTransaction *xact = new KWalletTransaction(connection());
329 _transactions.append(xact);
330
331 xact->appid = appid;
332 xact->wallet = wallet;
333 xact->wId = wId;
334 xact->modal = true; // mark dialogs as modal, the app has blocking wait
335 xact->tType = KWalletTransaction::Open;
336 xact->isPath = false;
337 if (handleSession) {
338 kDebug() << "openAsync for " << message().service();
339 _serviceWatcher.setConnection(connection());
340 _serviceWatcher.addWatchedService(message().service());
341 xact->service = message().service();
342 }
343 QTimer::singleShot(0, this, SLOT(processTransactions()));
344 checkActiveDialog();
345 // opening is in progress. return the transaction number
346 return xact->tId;
347}
348
349int KWalletD::openPathAsync(const QString& path, qlonglong wId, const QString& appid,
350 bool handleSession) {
351 if (!_enabled) { // gaurd
352 return -1;
353 }
354
355 KWalletTransaction *xact = new KWalletTransaction(connection());
356 _transactions.append(xact);
357
358 xact->appid = appid;
359 xact->wallet = path;
360 xact->wId = wId;
361 xact->modal = true;
362 xact->tType = KWalletTransaction::Open;
363 xact->isPath = true;
364 if (handleSession) {
365 kDebug() << "openPathAsync " << message().service();
366 _serviceWatcher.setConnection(connection());
367 _serviceWatcher.addWatchedService(message().service());
368 xact->service = message().service();
369 }
370 QTimer::singleShot(0, this, SLOT(processTransactions()));
371 checkActiveDialog();
372 // opening is in progress. return the transaction number
373 return xact->tId;
374}
375
376// Sets up a dialog that will be shown by kwallet.
377void KWalletD::setupDialog( QWidget* dialog, WId wId, const QString& appid, bool modal ) {
378 if( wId != 0 )
379 KWindowSystem::setMainWindow( dialog, wId ); // correct, set dialog parent
380 else {
381 if( appid.isEmpty())
382 kWarning() << "Using kwallet without parent window!";
383 else
384 kWarning() << "Application '" << appid << "' using kwallet without parent window!";
385 // allow dialog activation even if it interrupts, better than trying hacks
386 // with keeping the dialog on top or on all desktops
387 kapp->updateUserTimestamp();
388 }
389 if( modal )
390 KWindowSystem::setState( dialog->winId(), NET::Modal );
391 else
392 KWindowSystem::clearState( dialog->winId(), NET::Modal );
393 activeDialog = dialog;
394}
395
396// If there's a dialog already open and another application tries some operation that'd lead to
397// opening a dialog, that application will be blocked by this dialog. A proper solution would
398// be to set the second application's window also as a parent for the active dialog, so that
399// KWin properly handles focus changes and so on, but there's currently no support for multiple
400// dialog parents. Hopefully to be done in KDE4, for now just use all kinds of bad hacks to make
401// sure the user doesn't overlook the active dialog.
402void KWalletD::checkActiveDialog() {
403 if( !activeDialog )
404 return;
405
406 kapp->updateUserTimestamp();
407
408 activeDialog->show();
409
410 WId window = activeDialog->winId();
411 KWindowSystem::setState( window, NET::KeepAbove );
412 KWindowSystem::setOnAllDesktops( window, true );
413 KWindowSystem::forceActiveWindow( window );
414 KWindowSystem::raiseWindow( window );
415}
416
417
418int KWalletD::doTransactionOpen(const QString& appid, const QString& wallet, bool isPath,
419 qlonglong wId, bool modal, const QString& service) {
420 if (_firstUse && !wallets().contains(KWallet::Wallet::LocalWallet()) && !isPath) {
421 // First use wizard
422 // TODO GPG adjust new smartcard options gathered by the wizard
423 QPointer<KWalletWizard> wiz = new KWalletWizard(0);
424 wiz->setWindowTitle(i18n("KDE Wallet Service"));
425 setupDialog( wiz, (WId)wId, appid, modal );
426 int rc = wiz->exec();
427 if (rc == QDialog::Accepted && wiz) {
428 bool useWallet = wiz->field("useWallet").toBool();
429 KConfig kwalletrc("kwalletrc");
430 KConfigGroup cfg(&kwalletrc, "Wallet");
431 cfg.writeEntry("First Use", false);
432 cfg.writeEntry("Enabled", useWallet);
433 cfg.writeEntry("Close When Idle", wiz->field("closeWhenIdle").toBool());
434 cfg.writeEntry("Use One Wallet", !wiz->field("networkWallet").toBool());
435 cfg.sync();
436 reconfigure();
437
438 if (!useWallet) {
439 delete wiz;
440 return -1;
441 }
442
443 // Create the wallet
444 // TODO GPG select the correct wallet type upon cretion (GPG or blowfish based)
445 KWallet::Backend *b = new KWallet::Backend(KWallet::Wallet::LocalWallet());
446#ifdef HAVE_QGPGME
447 if (wiz->field("useBlowfish").toBool()) {
448 b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH);
449#endif
450 QString pass = wiz->field("pass1").toString();
451 QByteArray p(pass.toUtf8(), pass.length());
452 b->open(p);
453 p.fill(0);
454#ifdef HAVE_QGPGME
455 } else {
456 assert(wiz->field("useGpg").toBool());
457 b->setCipherType(KWallet::BACKEND_CIPHER_GPG);
458 b->open(wiz->gpgKey());
459 }
460#endif
461 b->createFolder(KWallet::Wallet::PasswordFolder());
462 b->createFolder(KWallet::Wallet::FormDataFolder());
463 b->close(true);
464 delete b;
465 delete wiz;
466 } else {
467 delete wiz;
468 return -1;
469 }
470 } else if (_firstUse && !isPath) {
471 KConfig kwalletrc("kwalletrc");
472 KConfigGroup cfg(&kwalletrc, "Wallet");
473 _firstUse = false;
474 cfg.writeEntry("First Use", false);
475 }
476
477 int rc = internalOpen(appid, wallet, isPath, WId(wId), modal, service);
478 return rc;
479}
480
481
482int KWalletD::internalOpen(const QString& appid, const QString& wallet, bool isPath, WId w,
483 bool modal, const QString& service) {
484 bool brandNew = false;
485
486 QString thisApp;
487 if (appid.isEmpty()) {
488 thisApp = "KDE System";
489 } else {
490 thisApp = appid;
491 }
492
493 if (implicitDeny(wallet, thisApp)) {
494 return -1;
495 }
496
497 QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
498 int rc = walletInfo.first;
499 if (rc == -1) {
500 if (_wallets.count() > 20) {
501 kDebug() << "Too many wallets open.";
502 return -1;
503 }
504
505 KWallet::Backend *b = new KWallet::Backend(wallet, isPath);
506 QString password;
507 bool emptyPass = false;
508 if ((isPath && QFile::exists(wallet)) || (!isPath && KWallet::Backend::exists(wallet))) {
509 // this open attempt will set wallet type from the file header, even if password is needed
510 int pwless = b->open(QByteArray(), w);
511#ifdef HAVE_QGPGME
512 assert(b->cipherType() != KWallet::BACKEND_CIPHER_UNKNOWN);
513 if (b->cipherType() == KWallet::BACKEND_CIPHER_GPG) {
514 // GPG based wallets do not prompt for password here. Instead, GPG should already have popped pinentry utility for wallet decryption
515 if (!b->isOpen()){
516 // for some reason, GPG operation failed
517 delete b;
518 return -1;
519 }
520 emptyPass = true;
521 } else {
522#endif
523 if (0 != pwless || !b->isOpen()) {
524 if (pwless == 0) {
525 // release, start anew
526 delete b;
527 b = new KWallet::Backend(wallet, isPath);
528 }
529 KPasswordDialog *kpd = new KPasswordDialog();
530 if (appid.isEmpty()) {
531 kpd->setPrompt(i18n("<qt>KDE has requested to open the wallet '<b>%1</b>'. Please enter the password for this wallet below.</qt>", Qt::escape(wallet)));
532 } else {
533 kpd->setPrompt(i18n("<qt>The application '<b>%1</b>' has requested to open the wallet '<b>%2</b>'. Please enter the password for this wallet below.</qt>", Qt::escape(appid), Qt::escape(wallet)));
534 }
535 brandNew = false;
536 // don't use KStdGuiItem::open() here which has trailing ellipsis!
537 kpd->setButtonGuiItem(KDialog::Ok,KGuiItem( i18n( "&Open" ), "wallet-open"));
538 kpd->setCaption(i18n("KDE Wallet Service"));
539 kpd->setPixmap(KIcon("kwalletmanager").pixmap(KIconLoader::SizeHuge));
540 if (w != KWindowSystem::activeWindow() && w != 0L) {
541 // If the dialog is modal to a minimized window it might not be visible
542 // (but still blocking the calling application). Notify the user about
543 // the request to open the wallet.
544 KNotification *notification = new KNotification("needsPassword", kpd,
545 KNotification::Persistent |
546 KNotification::CloseWhenWidgetActivated);
547 QStringList actions(i18nc("Text of a button to ignore the open-wallet notification", "Ignore"));
548 if (appid.isEmpty()) {
549 notification->setText(i18n("<b>KDE</b> has requested to open a wallet (%1).",
550 Qt::escape(wallet)));
551 actions.append(i18nc("Text of a button for switching to the (unnamed) application "
552 "requesting a password", "Switch there"));
553 } else {
554 notification->setText(i18n("<b>%1</b> has requested to open a wallet (%2).",
555 Qt::escape(appid), Qt::escape(wallet)));
556 actions.append(i18nc("Text of a button for switching to the application requesting "
557 "a password", "Switch to %1", Qt::escape(appid)));
558 }
559 notification->setActions(actions);
560 connect(notification, SIGNAL(action1Activated()),
561 notification, SLOT(close()));
562 connect(notification, SIGNAL(action2Activated()),
563 this, SLOT(activatePasswordDialog()));
564 notification->sendEvent();
565 }
566 while (!b->isOpen()) {
567 setupDialog( kpd, w, appid, modal );
568 if (kpd->exec() == KDialog::Accepted) {
569 password = kpd->password();
570 int rc = b->open(password.toUtf8());
571 if (!b->isOpen()) {
572 kpd->setPrompt(i18n("<qt>Error opening the wallet '<b>%1</b>'. Please try again.<br />(Error code %2: %3)</qt>", Qt::escape(wallet), rc, KWallet::Backend::openRCToString(rc)));
573 kpd->setPassword("");
574 }
575 } else {
576 break;
577 }
578 }
579 delete kpd;
580 } else {
581 emptyPass = true;
582 }
583#ifdef HAVE_QGPGME
584 }
585#endif
586 } else {
587 brandNew = true;
588#ifdef HAVE_QGPGME
589 // prompt the user for the new wallet format here
590 KWallet::BackendCipherType newWalletType = KWallet::BACKEND_CIPHER_UNKNOWN;
591
592 boost::shared_ptr<KWallet::KNewWalletDialog> newWalletDlg( new KWallet::KNewWalletDialog(appid, wallet, QWidget::find(w)));
593 GpgME::Key gpgKey;
594 setupDialog( newWalletDlg.get(), (WId)w, appid, true );
595 if (newWalletDlg->exec() == QDialog::Accepted) {
596 newWalletType = newWalletDlg->isBlowfish() ? KWallet::BACKEND_CIPHER_BLOWFISH : KWallet::BACKEND_CIPHER_GPG;
597 gpgKey = newWalletDlg->gpgKey();
598 } else {
599 // user cancelled the dialog box
600 delete b;
601 return -1;
602 }
603
604 if (newWalletType == KWallet::BACKEND_CIPHER_GPG) {
605 b->setCipherType(newWalletType);
606 b->open(gpgKey);
607 } else if (newWalletType == KWallet::BACKEND_CIPHER_BLOWFISH) {
608#endif // HAVE_QGPGME
609 b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH);
610 KNewPasswordDialog *kpd = new KNewPasswordDialog();
611 if (wallet == KWallet::Wallet::LocalWallet() ||
612 wallet == KWallet::Wallet::NetworkWallet())
613 {
614 // Auto create these wallets.
615 if (appid.isEmpty()) {
616 kpd->setPrompt(i18n("KDE has requested to open the wallet. This is used to store sensitive data in a secure fashion. Please enter a password to use with this wallet or click cancel to deny the application's request."));
617 } else {
618 kpd->setPrompt(i18n("<qt>The application '<b>%1</b>' has requested to open the KDE wallet. This is used to store sensitive data in a secure fashion. Please enter a password to use with this wallet or click cancel to deny the application's request.</qt>", Qt::escape(appid)));
619 }
620 } else {
621 if (appid.length() == 0) {
622 kpd->setPrompt(i18n("<qt>KDE has requested to create a new wallet named '<b>%1</b>'. Please choose a password for this wallet, or cancel to deny the application's request.</qt>", Qt::escape(wallet)));
623 } else {
624 kpd->setPrompt(i18n("<qt>The application '<b>%1</b>' has requested to create a new wallet named '<b>%2</b>'. Please choose a password for this wallet, or cancel to deny the application's request.</qt>", Qt::escape(appid), Qt::escape(wallet)));
625 }
626 }
627 kpd->setCaption(i18n("KDE Wallet Service"));
628 kpd->setButtonGuiItem(KDialog::Ok,KGuiItem(i18n("C&reate"),"document-new"));
629 kpd->setPixmap(KIcon("kwalletmanager").pixmap(96, 96));
630 while (!b->isOpen()) {
631 setupDialog( kpd, w, appid, modal );
632 if (kpd->exec() == KDialog::Accepted) {
633 password = kpd->password();
634 int rc = b->open(password.toUtf8());
635 if (!b->isOpen()) {
636 kpd->setPrompt(i18n("<qt>Error opening the wallet '<b>%1</b>'. Please try again.<br />(Error code %2: %3)</qt>", Qt::escape(wallet), rc, KWallet::Backend::openRCToString(rc)));
637 }
638 } else {
639 break;
640 }
641 }
642 delete kpd;
643#ifdef HAVE_QGPGME
644 }
645#endif
646 }
647
648
649 if ((b->cipherType() == KWallet::BACKEND_CIPHER_BLOWFISH) &&
650 !emptyPass && (password.isNull() || !b->isOpen())) {
651 delete b;
652 return -1;
653 }
654
655 if (emptyPass && !isAuthorizedApp(appid, wallet, w)) {
656 delete b;
657 return -1;
658 }
659
660 _wallets.insert(rc = generateHandle(), b);
661 _sessions.addSession(appid, service, rc);
662 _syncTimers.addTimer(rc, _syncTime);
663
664 if (brandNew) {
665 createFolder(rc, KWallet::Wallet::PasswordFolder(), appid);
666 createFolder(rc, KWallet::Wallet::FormDataFolder(), appid);
667 }
668
669 b->ref();
670 if (_closeIdle) {
671 _closeTimers.addTimer(rc, _idleTime);
672 }
673 if (brandNew)
674 emit walletCreated(wallet);
675 emit walletOpened(wallet);
676 if (_wallets.count() == 1 && _launchManager) {
677 KToolInvocation::startServiceByDesktopName("kwalletmanager-kwalletd");
678 }
679 } else {
680 // prematurely add a reference so that the wallet does not close while the
681 // authorization dialog is being shown.
682 walletInfo.second->ref();
683 bool isAuthorized = _sessions.hasSession(appid, rc) || isAuthorizedApp(appid, wallet, w);
684 // as the wallet might have been forcefully closed, find it again to make sure it's
685 // still available (isAuthorizedApp might show a dialog).
686 walletInfo = findWallet(wallet);
687 if (!isAuthorized) {
688 if (walletInfo.first != -1) {
689 walletInfo.second->deref();
690 // check if the wallet should be closed now.
691 internalClose(walletInfo.second, walletInfo.first, false);
692 }
693 return -1;
694 } else {
695 if (walletInfo.first != -1) {
696 _sessions.addSession(appid, service, rc);
697 } else {
698 // wallet was forcefully closed.
699 return -1;
700 }
701 }
702 }
703
704 return rc;
705}
706
707
708bool KWalletD::isAuthorizedApp(const QString& appid, const QString& wallet, WId w) {
709 if (!_openPrompt) {
710 return true;
711 }
712
713 int response = 0;
714
715 QString thisApp;
716 if (appid.isEmpty()) {
717 thisApp = "KDE System";
718 } else {
719 thisApp = appid;
720 }
721
722 if (!implicitAllow(wallet, thisApp)) {
723 KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc")->group("Auto Allow");
724 if (!cfg.isEntryImmutable(wallet)) {
725 KBetterThanKDialog *dialog = new KBetterThanKDialog;
726 dialog->setWindowTitle(i18n("KDE Wallet Service"));
727 if (appid.isEmpty()) {
728 dialog->setLabel(i18n("<qt>KDE has requested access to the open wallet '<b>%1</b>'.</qt>", Qt::escape(wallet)));
729 } else {
730 dialog->setLabel(i18n("<qt>The application '<b>%1</b>' has requested access to the open wallet '<b>%2</b>'.</qt>", Qt::escape(QString(appid)), Qt::escape(wallet)));
731 }
732 setupDialog( dialog, w, appid, false );
733 response = dialog->exec();
734 delete dialog;
735 }
736 }
737
738 if (response == 0 || response == 1) {
739 if (response == 1) {
740 KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc")->group("Auto Allow");
741 QStringList apps = cfg.readEntry(wallet, QStringList());
742 if (!apps.contains(thisApp)) {
743 if (cfg.isEntryImmutable(wallet)) {
744 return false;
745 }
746 apps += thisApp;
747 _implicitAllowMap[wallet] += thisApp;
748 cfg.writeEntry(wallet, apps);
749 cfg.sync();
750 }
751 }
752 } else if (response == 3) {
753 KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc")->group("Auto Deny");
754 QStringList apps = cfg.readEntry(wallet, QStringList());
755 if (!apps.contains(thisApp)) {
756 apps += thisApp;
757 _implicitDenyMap[wallet] += thisApp;
758 cfg.writeEntry(wallet, apps);
759 cfg.sync();
760 }
761 return false;
762 } else {
763 return false;
764 }
765 return true;
766}
767
768
769int KWalletD::deleteWallet(const QString& wallet) {
770 int result = -1;
771 QString path = KGlobal::dirs()->saveLocation("kwallet") + QDir::separator() + wallet + ".kwl";
772
773 if (QFile::exists(path)) {
774 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
775 internalClose(walletInfo.second, walletInfo.first, true);
776 QFile::remove(path);
777 emit walletDeleted(wallet);
778 // also delete access control entries
779 KConfigGroup cfgAllow = KSharedConfig::openConfig("kwalletrc")->group("Auto Allow");
780 cfgAllow.deleteEntry(wallet);
781
782 KConfigGroup cfgDeny = KSharedConfig::openConfig("kwalletrc")->group("Auto Deny");
783 cfgDeny.deleteEntry(wallet);
784
785 result = 0;
786 }
787
788 return result;
789}
790
791
792void KWalletD::changePassword(const QString& wallet, qlonglong wId, const QString& appid) {
793 KWalletTransaction *xact = new KWalletTransaction(connection());
794
795 message().setDelayedReply(true);
796 xact->message = message();
797 // TODO GPG this shouldn't be allowed on a GPG managed wallet; a warning should be displayed about this
798
799 xact->appid = appid;
800 xact->wallet = wallet;
801 xact->wId = wId;
802 xact->modal = false;
803 xact->tType = KWalletTransaction::ChangePassword;
804
805 _transactions.append(xact);
806
807 QTimer::singleShot(0, this, SLOT(processTransactions()));
808 checkActiveDialog();
809 checkActiveDialog();
810}
811
812void KWalletD::initiateSync(int handle) {
813 // add a timer and reset it right away
814 _syncTimers.addTimer(handle, _syncTime);
815 _syncTimers.resetTimer(handle, _syncTime);
816}
817
818void KWalletD::doTransactionChangePassword(const QString& appid, const QString& wallet, qlonglong wId) {
819
820 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
821 int handle = walletInfo.first;
822 KWallet::Backend* w = walletInfo.second;
823
824 bool reclose = false;
825 if (!w) {
826 handle = doTransactionOpen(appid, wallet, false, wId, false, "");
827 if (-1 == handle) {
828 KMessageBox::sorryWId((WId)wId, i18n("Unable to open wallet. The wallet must be opened in order to change the password."), i18n("KDE Wallet Service"));
829 return;
830 }
831
832 w = _wallets.value(handle);
833 reclose = true;
834 }
835
836 assert(w);
837
838#ifdef HAVE_QGPGME
839 if (w->cipherType() == KWallet::BACKEND_CIPHER_GPG) {
840 QString keyID = w->gpgKey().shortKeyID();
841 assert(!keyID.isNull());
842 KMessageBox::errorWId((WId)wId, i18n("<qt>The <b>%1</b> wallet is encrypted using GPG key <b>%2</b>. Please use <b>GPG</b> tools (such as <b>kleopatra</b>) to change the passphrase associated to that key.</qt>", Qt::escape(wallet), keyID));
843 } else {
844#endif
845 QPointer<KNewPasswordDialog> kpd = new KNewPasswordDialog();
846 kpd->setPrompt(i18n("<qt>Please choose a new password for the wallet '<b>%1</b>'.</qt>", Qt::escape(wallet)));
847 kpd->setCaption(i18n("KDE Wallet Service"));
848 kpd->setAllowEmptyPasswords(true);
849 setupDialog( kpd, (WId)wId, appid, false );
850 if (kpd->exec() == KDialog::Accepted && kpd) {
851 QString p = kpd->password();
852 if (!p.isNull()) {
853 w->setPassword(p.toUtf8());
854 int rc = w->close(true);
855 if (rc < 0) {
856 KMessageBox::sorryWId((WId)wId, i18n("Error re-encrypting the wallet. Password was not changed."), i18n("KDE Wallet Service"));
857 reclose = true;
858 } else {
859 rc = w->open(p.toUtf8());
860 if (rc < 0) {
861 KMessageBox::sorryWId((WId)wId, i18n("Error reopening the wallet. Data may be lost."), i18n("KDE Wallet Service"));
862 reclose = true;
863 }
864 }
865 }
866 }
867
868 delete kpd;
869#ifdef HAVE_QGPGME
870 }
871#endif
872
873 if (reclose) {
874 internalClose(w, handle, true);
875 }
876}
877
878
879int KWalletD::close(const QString& wallet, bool force) {
880 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
881 int handle = walletInfo.first;
882 KWallet::Backend* w = walletInfo.second;
883
884 return internalClose(w, handle, force);
885}
886
887
888int KWalletD::internalClose(KWallet::Backend *w, int handle, bool force) {
889 if (w) {
890 const QString& wallet = w->walletName();
891 if ((w->refCount() == 0 && !_leaveOpen) || force) {
892 // this is only a safety measure. sessions should be gone already.
893 _sessions.removeAllSessions(handle);
894 if (_closeIdle) {
895 _closeTimers.removeTimer(handle);
896 }
897 _syncTimers.removeTimer(handle);
898 _wallets.remove(handle);
899 w->close(true);
900 doCloseSignals(handle, wallet);
901 delete w;
902 return 0;
903 }
904 return 1;
905 }
906
907 return -1;
908}
909
910
911int KWalletD::close(int handle, bool force, const QString& appid) {
912 KWallet::Backend *w = _wallets.value(handle);
913
914 if (w) {
915 if (_sessions.hasSession(appid, handle)) {
916 // remove one handle for the application
917 bool removed = _sessions.removeSession(appid, message().service(), handle);
918 // alternatively try sessionless
919 if (removed || _sessions.removeSession(appid, "", handle)) {
920 w->deref();
921 }
922 return internalClose(w, handle, force);
923 }
924 return 1; // not closed, handle unknown
925 }
926 return -1; // not open to begin with, or other error
927}
928
929
930bool KWalletD::isOpen(const QString& wallet) {
931 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
932 return walletInfo.second != 0;
933}
934
935
936bool KWalletD::isOpen(int handle) {
937 if (handle == 0) {
938 return false;
939 }
940
941 KWallet::Backend *rc = _wallets.value(handle);
942
943 if (rc == 0 && ++_failed > 5) {
944 _failed = 0;
945 QTimer::singleShot(0, this, SLOT(notifyFailures()));
946 } else if (rc != 0) {
947 _failed = 0;
948 }
949
950 return rc != 0;
951}
952
953
954QStringList KWalletD::wallets() const {
955 QString path = KGlobal::dirs()->saveLocation("kwallet");
956 QDir dir(path, "*.kwl");
957 QStringList rc;
958
959 dir.setFilter(QDir::Files | QDir::Hidden);
960
961 foreach (const QFileInfo &fi, dir.entryInfoList()) {
962 QString fn = fi.fileName();
963 if (fn.endsWith(QLatin1String(".kwl"))) {
964 fn.truncate(fn.length()-4);
965 }
966 rc += fn;
967 }
968 return rc;
969}
970
971
972void KWalletD::sync(int handle, const QString& appid) {
973 KWallet::Backend *b;
974
975 // get the wallet and check if we have a password for it (safety measure)
976 if ((b = getWallet(appid, handle))) {
977 QString wallet = b->walletName();
978 b->sync(0);
979 }
980}
981
982void KWalletD::timedOutSync(int handle) {
983 _syncTimers.removeTimer(handle);
984 if (_wallets.contains(handle) && _wallets[handle]) {
985 _wallets[handle]->sync(0);
986 }
987}
988
989void KWalletD::doTransactionOpenCancelled(const QString& appid, const QString& wallet,
990 const QString& service) {
991
992 // there will only be one session left to remove - all others
993 // have already been removed in slotServiceOwnerChanged and all
994 // transactions for opening new sessions have been deleted.
995 if (!_sessions.hasSession(appid)) {
996 return;
997 }
998
999 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
1000 int handle = walletInfo.first;
1001 KWallet::Backend *b = walletInfo.second;
1002 if (handle != -1 && b) {
1003 b->deref();
1004 internalClose(b, handle, false);
1005 }
1006
1007 // close the session in case the wallet hasn't been closed yet
1008 _sessions.removeSession(appid, service, handle);
1009}
1010
1011QStringList KWalletD::folderList(int handle, const QString& appid) {
1012 KWallet::Backend *b;
1013
1014 if ((b = getWallet(appid, handle))) {
1015 return b->folderList();
1016 }
1017
1018 return QStringList();
1019}
1020
1021
1022bool KWalletD::hasFolder(int handle, const QString& f, const QString& appid) {
1023 KWallet::Backend *b;
1024
1025 if ((b = getWallet(appid, handle))) {
1026 return b->hasFolder(f);
1027 }
1028
1029 return false;
1030}
1031
1032
1033bool KWalletD::removeFolder(int handle, const QString& f, const QString& appid) {
1034 KWallet::Backend *b;
1035
1036 if ((b = getWallet(appid, handle))) {
1037 bool rc = b->removeFolder(f);
1038 initiateSync(handle);
1039 emit folderListUpdated(b->walletName());
1040 return rc;
1041 }
1042
1043 return false;
1044}
1045
1046
1047bool KWalletD::createFolder(int handle, const QString& f, const QString& appid) {
1048 KWallet::Backend *b;
1049
1050 if ((b = getWallet(appid, handle))) {
1051 bool rc = b->createFolder(f);
1052 initiateSync(handle);
1053 emit folderListUpdated(b->walletName());
1054 return rc;
1055 }
1056
1057 return false;
1058}
1059
1060
1061QByteArray KWalletD::readMap(int handle, const QString& folder, const QString& key, const QString& appid) {
1062 KWallet::Backend *b;
1063
1064 if ((b = getWallet(appid, handle))) {
1065 b->setFolder(folder);
1066 KWallet::Entry *e = b->readEntry(key);
1067 if (e && e->type() == KWallet::Wallet::Map) {
1068 return e->map();
1069 }
1070 }
1071
1072 return QByteArray();
1073}
1074
1075
1076QVariantMap KWalletD::readMapList(int handle, const QString& folder, const QString& key, const QString& appid) {
1077 KWallet::Backend *b;
1078
1079 if ((b = getWallet(appid, handle))) {
1080 b->setFolder(folder);
1081 QVariantMap rc;
1082 foreach (KWallet::Entry *entry, b->readEntryList(key)) {
1083 if (entry->type() == KWallet::Wallet::Map) {
1084 rc.insert(entry->key(), entry->map());
1085 }
1086 }
1087 return rc;
1088 }
1089
1090 return QVariantMap();
1091}
1092
1093
1094QByteArray KWalletD::readEntry(int handle, const QString& folder, const QString& key, const QString& appid) {
1095 KWallet::Backend *b;
1096
1097 if ((b = getWallet(appid, handle))) {
1098 b->setFolder(folder);
1099 KWallet::Entry *e = b->readEntry(key);
1100 if (e) {
1101 return e->value();
1102 }
1103 }
1104
1105 return QByteArray();
1106}
1107
1108
1109QVariantMap KWalletD::readEntryList(int handle, const QString& folder, const QString& key, const QString& appid) {
1110 KWallet::Backend *b;
1111
1112 if ((b = getWallet(appid, handle))) {
1113 b->setFolder(folder);
1114 QVariantMap rc;
1115 foreach (KWallet::Entry *entry, b->readEntryList(key)) {
1116 rc.insert(entry->key(), entry->value());
1117 }
1118 return rc;
1119 }
1120
1121 return QVariantMap();
1122}
1123
1124
1125QStringList KWalletD::entryList(int handle, const QString& folder, const QString& appid) {
1126 KWallet::Backend *b;
1127
1128 if ((b = getWallet(appid, handle))) {
1129 b->setFolder(folder);
1130 return b->entryList();
1131 }
1132
1133 return QStringList();
1134}
1135
1136
1137QString KWalletD::readPassword(int handle, const QString& folder, const QString& key, const QString& appid) {
1138 KWallet::Backend *b;
1139
1140 if ((b = getWallet(appid, handle))) {
1141 b->setFolder(folder);
1142 KWallet::Entry *e = b->readEntry(key);
1143 if (e && e->type() == KWallet::Wallet::Password) {
1144 return e->password();
1145 }
1146 }
1147
1148 return QString();
1149}
1150
1151
1152QVariantMap KWalletD::readPasswordList(int handle, const QString& folder, const QString& key, const QString& appid) {
1153 KWallet::Backend *b;
1154
1155 if ((b = getWallet(appid, handle))) {
1156 b->setFolder(folder);
1157 QVariantMap rc;
1158 foreach (KWallet::Entry *entry, b->readEntryList(key)) {
1159 if (entry->type() == KWallet::Wallet::Password) {
1160 rc.insert(entry->key(), entry->password());
1161 }
1162 }
1163 return rc;
1164 }
1165
1166 return QVariantMap();
1167}
1168
1169
1170int KWalletD::writeMap(int handle, const QString& folder, const QString& key, const QByteArray& value, const QString& appid) {
1171 KWallet::Backend *b;
1172
1173 if ((b = getWallet(appid, handle))) {
1174 b->setFolder(folder);
1175 KWallet::Entry e;
1176 e.setKey(key);
1177 e.setValue(value);
1178 e.setType(KWallet::Wallet::Map);
1179 b->writeEntry(&e);
1180 initiateSync(handle);
1181 emitFolderUpdated(b->walletName(), folder);
1182 return 0;
1183 }
1184
1185 return -1;
1186}
1187
1188
1189int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, const QByteArray& value, int entryType, const QString& appid) {
1190 KWallet::Backend *b;
1191
1192 if ((b = getWallet(appid, handle))) {
1193 b->setFolder(folder);
1194 KWallet::Entry e;
1195 e.setKey(key);
1196 e.setValue(value);
1197 e.setType(KWallet::Wallet::EntryType(entryType));
1198 b->writeEntry(&e);
1199 initiateSync(handle);
1200 emitFolderUpdated(b->walletName(), folder);
1201 return 0;
1202 }
1203
1204 return -1;
1205}
1206
1207
1208int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, const QByteArray& value, const QString& appid) {
1209 KWallet::Backend *b;
1210
1211 if ((b = getWallet(appid, handle))) {
1212 b->setFolder(folder);
1213 KWallet::Entry e;
1214 e.setKey(key);
1215 e.setValue(value);
1216 e.setType(KWallet::Wallet::Stream);
1217 b->writeEntry(&e);
1218 initiateSync(handle);
1219 emitFolderUpdated(b->walletName(), folder);
1220 return 0;
1221 }
1222
1223 return -1;
1224}
1225
1226
1227int KWalletD::writePassword(int handle, const QString& folder, const QString& key, const QString& value, const QString& appid) {
1228 KWallet::Backend *b;
1229
1230 if ((b = getWallet(appid, handle))) {
1231 b->setFolder(folder);
1232 KWallet::Entry e;
1233 e.setKey(key);
1234 e.setValue(value);
1235 e.setType(KWallet::Wallet::Password);
1236 b->writeEntry(&e);
1237 initiateSync(handle);
1238 emitFolderUpdated(b->walletName(), folder);
1239 return 0;
1240 }
1241
1242 return -1;
1243}
1244
1245
1246int KWalletD::entryType(int handle, const QString& folder, const QString& key, const QString& appid) {
1247 KWallet::Backend *b;
1248
1249 if ((b = getWallet(appid, handle))) {
1250 if (!b->hasFolder(folder)) {
1251 return KWallet::Wallet::Unknown;
1252 }
1253 b->setFolder(folder);
1254 if (b->hasEntry(key)) {
1255 return b->readEntry(key)->type();
1256 }
1257 }
1258
1259 return KWallet::Wallet::Unknown;
1260}
1261
1262
1263bool KWalletD::hasEntry(int handle, const QString& folder, const QString& key, const QString& appid) {
1264 KWallet::Backend *b;
1265
1266 if ((b = getWallet(appid, handle))) {
1267 if (!b->hasFolder(folder)) {
1268 return false;
1269 }
1270 b->setFolder(folder);
1271 return b->hasEntry(key);
1272 }
1273
1274 return false;
1275}
1276
1277
1278int KWalletD::removeEntry(int handle, const QString& folder, const QString& key, const QString& appid) {
1279 KWallet::Backend *b;
1280
1281 if ((b = getWallet(appid, handle))) {
1282 if (!b->hasFolder(folder)) {
1283 return 0;
1284 }
1285 b->setFolder(folder);
1286 bool rc = b->removeEntry(key);
1287 initiateSync(handle);
1288 emitFolderUpdated(b->walletName(), folder);
1289 return rc ? 0 : -3;
1290 }
1291
1292 return -1;
1293}
1294
1295
1296void KWalletD::slotServiceOwnerChanged(const QString& name, const QString &oldOwner,
1297 const QString& newOwner)
1298{
1299 Q_UNUSED(name);
1300 kDebug() << "slotServiceOwnerChanged " << name << ", " << oldOwner << ", " << newOwner;
1301
1302 if (!newOwner.isEmpty()) {
1303 return; // no application exit, don't care.
1304 }
1305
1306 // as we don't have the application id we have to cycle
1307 // all sessions. As an application can basically open wallets
1308 // with several appids, we can't stop if we found one.
1309 QString service(oldOwner);
1310 QList<KWalletAppHandlePair> sessremove(_sessions.findSessions(service));
1311 KWallet::Backend *b = 0;
1312
1313 // check all sessions for wallets to close
1314 Q_FOREACH(const KWalletAppHandlePair &s, sessremove) {
1315 b = getWallet(s.first, s.second);
1316 if (b) {
1317 b->deref();
1318 internalClose(b, s.second, false);
1319 }
1320 }
1321
1322 // remove all the sessions in case they aren't gone yet
1323 Q_FOREACH(const KWalletAppHandlePair &s, sessremove) {
1324 _sessions.removeSession(s.first, service, s.second);
1325 }
1326
1327 // cancel all open-transactions still running for the service
1328 QList<KWalletTransaction*>::iterator tit;
1329 for (tit = _transactions.begin(); tit != _transactions.end(); ++tit) {
1330 if ((*tit)->tType == KWalletTransaction::Open && (*tit)->service == oldOwner) {
1331 delete (*tit);
1332 *tit = 0;
1333 }
1334 }
1335 _transactions.removeAll(0);
1336
1337 // if there's currently an open-transaction being handled,
1338 // mark it as cancelled.
1339 if (_curtrans && _curtrans->tType == KWalletTransaction::Open &&
1340 _curtrans->service == oldOwner) {
1341 kDebug() << "Cancelling current transaction!";
1342 _curtrans->cancelled = true;
1343 }
1344 _serviceWatcher.removeWatchedService(oldOwner);
1345}
1346
1347KWallet::Backend *KWalletD::getWallet(const QString& appid, int handle) {
1348 if (handle == 0) {
1349 return 0L;
1350 }
1351
1352 KWallet::Backend *w = _wallets.value(handle);
1353
1354 if (w) { // the handle is valid
1355 if (_sessions.hasSession(appid, handle)) {
1356 // the app owns this handle
1357 _failed = 0;
1358 if (_closeIdle) {
1359 _closeTimers.resetTimer(handle, _idleTime);
1360 }
1361 return w;
1362 }
1363 }
1364
1365 if (++_failed > 5) {
1366 _failed = 0;
1367 QTimer::singleShot(0, this, SLOT(notifyFailures()));
1368 }
1369
1370 return 0L;
1371}
1372
1373
1374void KWalletD::notifyFailures() {
1375 if (!_showingFailureNotify) {
1376 _showingFailureNotify = true;
1377 KMessageBox::information(0, i18n("There have been repeated failed attempts to gain access to a wallet. An application may be misbehaving."), i18n("KDE Wallet Service"));
1378 _showingFailureNotify = false;
1379 }
1380}
1381
1382
1383void KWalletD::doCloseSignals(int handle, const QString& wallet) {
1384 emit walletClosed(handle);
1385 emit walletClosed(wallet);
1386 if (_wallets.isEmpty()) {
1387 emit allWalletsClosed();
1388 }
1389}
1390
1391
1392int KWalletD::renameEntry(int handle, const QString& folder, const QString& oldName, const QString& newName, const QString& appid) {
1393 KWallet::Backend *b;
1394
1395 if ((b = getWallet(appid, handle))) {
1396 b->setFolder(folder);
1397 int rc = b->renameEntry(oldName, newName);
1398 initiateSync(handle);
1399 emitFolderUpdated(b->walletName(), folder);
1400 return rc;
1401 }
1402
1403 return -1;
1404}
1405
1406
1407QStringList KWalletD::users(const QString& wallet) const {
1408 const QPair<int,KWallet::Backend*> walletInfo = findWallet(wallet);
1409 return _sessions.getApplications(walletInfo.first);
1410}
1411
1412
1413bool KWalletD::disconnectApplication(const QString& wallet, const QString& application) {
1414 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
1415 int handle = walletInfo.first;
1416 KWallet::Backend* backend = walletInfo.second;
1417
1418 if (handle != -1 && _sessions.hasSession(application, handle)) {
1419 int removed = _sessions.removeAllSessions(application, handle);
1420
1421 for (int i = 0; i < removed; ++i) {
1422 backend->deref();
1423 }
1424 internalClose(backend, handle, false);
1425
1426 emit applicationDisconnected(wallet, application);
1427 return true;
1428 }
1429
1430 return false;
1431}
1432
1433
1434void KWalletD::emitFolderUpdated(const QString& wallet, const QString& folder) {
1435 emit folderUpdated(wallet, folder);
1436}
1437
1438
1439void KWalletD::emitWalletListDirty() {
1440 emit walletListDirty();
1441}
1442
1443
1444void KWalletD::reconfigure() {
1445 KConfig cfg("kwalletrc");
1446 KConfigGroup walletGroup(&cfg, "Wallet");
1447 _firstUse = walletGroup.readEntry("First Use", true);
1448 _enabled = walletGroup.readEntry("Enabled", true);
1449 _launchManager = walletGroup.readEntry("Launch Manager", false);
1450 _leaveOpen = walletGroup.readEntry("Leave Open", false);
1451 bool idleSave = _closeIdle;
1452 _closeIdle = walletGroup.readEntry("Close When Idle", false);
1453 _openPrompt = walletGroup.readEntry("Prompt on Open", false);
1454 int timeSave = _idleTime;
1455 // in minutes!
1456 _idleTime = walletGroup.readEntry("Idle Timeout", 10) * 60 * 1000;
1457#ifdef Q_WS_X11
1458 if (walletGroup.readEntry("Close on Screensaver", false)) {
1459 // BUG 254273 : if kwalletd starts before the screen saver, then the connection fails and kwalletd never receives it's notifications
1460 // To fix this, we use a timer and perform periodic connection attempts until connection succeeds
1461 QTimer::singleShot(0, this, SLOT(connectToScreenSaver()));
1462 } else {
1463 if (screensaver && screensaver->isValid()) {
1464 screensaver->disconnect(SIGNAL(ActiveChanged(bool)), this, SLOT(screenSaverChanged(bool)));
1465 delete screensaver;
1466 screensaver = 0;
1467 }
1468 }
1469#endif
1470 // Handle idle changes
1471 if (_closeIdle) {
1472 if (_idleTime != timeSave) { // Timer length changed
1473 Wallets::const_iterator it = _wallets.constBegin();
1474 const Wallets::const_iterator end = _wallets.constEnd();
1475 for (; it != end; ++it) {
1476 _closeTimers.resetTimer(it.key(), _idleTime);
1477 }
1478 }
1479
1480 if (!idleSave) { // add timers for all the wallets
1481 Wallets::const_iterator it = _wallets.constBegin();
1482 const Wallets::const_iterator end = _wallets.constEnd();
1483 for (; it != end; ++it) {
1484 _closeTimers.addTimer(it.key(), _idleTime);
1485 }
1486 }
1487 } else {
1488 _closeTimers.clear();
1489 }
1490
1491 // Update the implicit allow stuff
1492 _implicitAllowMap.clear();
1493 const KConfigGroup autoAllowGroup(&cfg, "Auto Allow");
1494 QStringList entries = autoAllowGroup.entryMap().keys();
1495 for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) {
1496 _implicitAllowMap[*i] = autoAllowGroup.readEntry(*i, QStringList());
1497 }
1498
1499 // Update the implicit allow stuff
1500 _implicitDenyMap.clear();
1501 const KConfigGroup autoDenyGroup(&cfg, "Auto Deny");
1502 entries = autoDenyGroup.entryMap().keys();
1503 for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) {
1504 _implicitDenyMap[*i] = autoDenyGroup.readEntry(*i, QStringList());
1505 }
1506
1507 // Update if wallet was enabled/disabled
1508 if (!_enabled) { // close all wallets
1509 while (!_wallets.isEmpty()) {
1510 Wallets::const_iterator it = _wallets.constBegin();
1511 internalClose(it.value(), it.key(), true);
1512 }
1513 KUniqueApplication::exit(0);
1514 }
1515}
1516
1517
1518bool KWalletD::isEnabled() const {
1519 return _enabled;
1520}
1521
1522
1523bool KWalletD::folderDoesNotExist(const QString& wallet, const QString& folder) {
1524 if (!wallets().contains(wallet)) {
1525 return true;
1526 }
1527
1528 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
1529 if (walletInfo.second) {
1530 return walletInfo.second->folderDoesNotExist(folder);
1531 }
1532
1533 KWallet::Backend *b = new KWallet::Backend(wallet);
1534 b->open(QByteArray());
1535 bool rc = b->folderDoesNotExist(folder);
1536 delete b;
1537 return rc;
1538}
1539
1540
1541bool KWalletD::keyDoesNotExist(const QString& wallet, const QString& folder, const QString& key) {
1542 if (!wallets().contains(wallet)) {
1543 return true;
1544 }
1545
1546 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
1547 if (walletInfo.second) {
1548 return walletInfo.second->entryDoesNotExist(folder, key);
1549 }
1550
1551 KWallet::Backend *b = new KWallet::Backend(wallet);
1552 b->open(QByteArray());
1553 bool rc = b->entryDoesNotExist(folder, key);
1554 delete b;
1555 return rc;
1556}
1557
1558
1559bool KWalletD::implicitAllow(const QString& wallet, const QString& app) {
1560 return _implicitAllowMap[wallet].contains(app);
1561}
1562
1563
1564bool KWalletD::implicitDeny(const QString& wallet, const QString& app) {
1565 return _implicitDenyMap[wallet].contains(app);
1566}
1567
1568
1569void KWalletD::timedOutClose(int id) {
1570 KWallet::Backend *w = _wallets.value(id);
1571 if (w) {
1572 internalClose(w, id, true);
1573 }
1574}
1575
1576
1577void KWalletD::closeAllWallets() {
1578 Wallets walletsCopy = _wallets;
1579
1580 Wallets::const_iterator it = walletsCopy.constBegin();
1581 const Wallets::const_iterator end = walletsCopy.constEnd();
1582 for (; it != end; ++it) {
1583 internalClose(it.value(), it.key(), true);
1584 }
1585
1586 walletsCopy.clear();
1587
1588 // All of this should be basically noop. Let's just be safe.
1589 _wallets.clear();
1590}
1591
1592
1593QString KWalletD::networkWallet() {
1594 return KWallet::Wallet::NetworkWallet();
1595}
1596
1597
1598QString KWalletD::localWallet() {
1599 return KWallet::Wallet::LocalWallet();
1600}
1601
1602void KWalletD::screenSaverChanged(bool s)
1603{
1604 if (s)
1605 closeAllWallets();
1606}
1607
1608void KWalletD::activatePasswordDialog()
1609{
1610 checkActiveDialog();
1611}
1612
1613int KWalletD::pamOpen(const QString &wallet, const QByteArray &passwordHash, int sessionTimeout)
1614{
1615 if (_processing) {
1616 return -1;
1617 }
1618
1619 if (!QRegExp("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\.\\+\\_\\s]+$").exactMatch(wallet)) {
1620 return -1;
1621 }
1622
1623 // check if the wallet is already open
1624 QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
1625 int rc = walletInfo.first;
1626 if (rc != -1) {
1627 return rc;//Wallet already opened, return handle
1628 }
1629
1630 KWallet::Backend *b = 0;
1631 //If the wallet we want to open does not exists. create it and set pam hash
1632 if (!wallets().contains(wallet)) {
1633 b = new KWallet::Backend(wallet);
1634 b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH);
1635 } else {
1636 b = new KWallet::Backend(wallet);
1637 }
1638
1639 if (_wallets.count() > 20) {
1640 return -1;
1641 }
1642
1643 int openrc = b->openPreHashed(passwordHash);
1644 if (openrc != 0 || !b->isOpen()) {
1645 return -1;
1646 }
1647
1648 // opening the wallet was successful
1649 int handle = generateHandle();
1650 _wallets.insert(handle, b);
1651 _syncTimers.addTimer(handle, _syncTime);
1652
1653 // don't reference the wallet or add a session so it
1654 // can be reclosed easily.
1655
1656 if (sessionTimeout > 0) {
1657 _closeTimers.addTimer(handle, sessionTimeout);
1658 } else if (_closeIdle) {
1659 _closeTimers.addTimer(handle, _idleTime);
1660 }
1661 emit walletOpened(wallet);
1662
1663 if (_wallets.count() == 1 && _launchManager) {
1664 KToolInvocation::startServiceByDesktopName("kwalletmanager-kwalletd");
1665 }
1666
1667 return handle;
1668}
1669
1670#include "kwalletd.moc"
1671