delay a few things on startup, such as setting the visibility mode, which ensures...
[personal-kdebase.git] / runtime / kwalletd / kwalletd.cpp
blob88ea986b15f631ae658e242d73aaf0418fcb432c
1 // -*- indent-tabs-mode: t; tab-width: 4; c-basic-offset: 4; -*-
2 /*
3 This file is part of the KDE libraries
5 Copyright (c) 2002-2004 George Staikos <staikos@kde.org>
6 Copyright (c) 2008 Michael Leupold <lemma@confuego.org>
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.
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.
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.
25 #include "kwalletd.h"
27 #include "kbetterthankdialog.h"
28 #include "kwalletwizard.h"
30 #include <kuniqueapplication.h>
31 #include <ktoolinvocation.h>
32 #include <kconfig.h>
33 #include <kconfiggroup.h>
34 #include <kdebug.h>
35 #include <kdirwatch.h>
36 #include <kglobal.h>
37 #include <klocale.h>
38 #include <kmessagebox.h>
39 #include <kpassworddialog.h>
40 #include <knewpassworddialog.h>
41 #include <kstandarddirs.h>
42 #include <kwalletentry.h>
43 #include <kwindowsystem.h>
44 #include <kpluginfactory.h>
45 #include <kpluginloader.h>
47 #include <QtCore/QDir>
48 #include <QtGui/QTextDocument> // Qt::escape
49 #include <QtCore/QRegExp>
50 #include <QtCore/QTimer>
51 #include <QtCore/QEventLoop>
53 #include <assert.h>
55 #include "kwalletadaptor.h"
56 #include "kwalletopenloop.h"
58 class KWalletTransaction {
60 public:
61 KWalletTransaction()
62 : tType(Unknown), cancelled(false), tId(nextTransactionId)
64 nextTransactionId++;
65 // make sure the id is never < 0 as that's used for the
66 // error conditions.
67 if (nextTransactionId < 0) {
68 nextTransactionId = 0;
72 ~KWalletTransaction() {
75 enum Type {
76 Unknown,
77 Open,
78 ChangePassword,
79 OpenFail,
80 CloseCancelled
82 Type tType;
83 QString appid;
84 qlonglong wId;
85 QString wallet;
86 QString service;
87 bool cancelled; // set true if the client dies before open
88 bool modal;
89 bool isPath;
90 int tId; // transaction id
92 private:
93 static int nextTransactionId;
96 int KWalletTransaction::nextTransactionId = 0;
98 KWalletD::KWalletD()
99 : QObject(0), _failed(0), _syncTime(5000), _curtrans(0) {
101 srand(time(0));
102 _showingFailureNotify = false;
103 _closeIdle = false;
104 _idleTime = 0;
105 connect(&_closeTimers, SIGNAL(timedOut(int)), this, SLOT(timedOutClose(int)));
106 connect(&_syncTimers, SIGNAL(timedOut(int)), this, SLOT(timedOutSync(int)));
108 (void)new KWalletAdaptor(this);
109 // register services
110 QDBusConnection::sessionBus().registerService(QLatin1String("org.kde.kwalletd"));
111 QDBusConnection::sessionBus().registerObject(QLatin1String("/modules/kwalletd"), this);
113 #ifdef Q_WS_X11
114 screensaver = new QDBusInterface("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver");
115 #endif
117 reconfigure();
118 KGlobal::dirs()->addResourceType("kwallet", 0, "share/apps/kwallet");
119 connect(QDBusConnection::sessionBus().interface(),
120 SIGNAL(serviceOwnerChanged(QString,QString,QString)),
121 SLOT(slotServiceOwnerChanged(QString,QString,QString)));
122 _dw = new KDirWatch(this );
123 _dw->setObjectName( "KWallet Directory Watcher" );
124 _dw->addDir(KGlobal::dirs()->saveLocation("kwallet"));
125 _dw->startScan(true);
126 connect(_dw, SIGNAL(dirty(const QString&)), this, SLOT(emitWalletListDirty()));
130 KWalletD::~KWalletD() {
131 #ifdef Q_WS_X11
132 delete screensaver;
133 screensaver = 0;
134 #endif
135 closeAllWallets();
136 qDeleteAll(_transactions);
140 int KWalletD::generateHandle() {
141 int rc;
143 // ASSUMPTION: RAND_MAX is fairly large.
144 do {
145 rc = rand();
146 } while (_wallets.contains(rc) || rc == 0);
148 return rc;
151 QPair<int, KWallet::Backend*> KWalletD::findWallet(const QString& walletName) const
153 Wallets::const_iterator it = _wallets.constBegin();
154 const Wallets::const_iterator end = _wallets.constEnd();
155 for (; it != end; ++it) {
156 if (it.value()->walletName() == walletName) {
157 return qMakePair(it.key(), it.value());
160 return qMakePair(-1, static_cast<KWallet::Backend*>(0));
163 void KWalletD::processTransactions() {
164 static bool processing = false;
166 if (processing) {
167 return;
170 processing = true;
172 // Process remaining transactions
173 while (!_transactions.isEmpty()) {
174 _curtrans = _transactions.takeFirst();
175 int res;
177 assert(_curtrans->tType != KWalletTransaction::Unknown);
179 switch (_curtrans->tType) {
180 case KWalletTransaction::Open:
181 res = doTransactionOpen(_curtrans->appid, _curtrans->wallet, _curtrans->isPath,
182 _curtrans->wId, _curtrans->modal, _curtrans->service);
184 // multiple requests from the same client
185 // should not produce multiple password
186 // dialogs on a failure
187 if (res < 0) {
188 QList<KWalletTransaction *>::iterator it;
189 for (it = _transactions.begin(); it != _transactions.end(); ++it) {
190 KWalletTransaction *x = *it;
191 if (_curtrans->appid == x->appid && x->tType == KWalletTransaction::Open
192 && x->wallet == _curtrans->wallet && x->wId == _curtrans->wId) {
193 x->tType = KWalletTransaction::OpenFail;
196 } else if (_curtrans->cancelled) {
197 // the wallet opened successfully but the application
198 // opening exited/crashed while the dialog was still shown.
199 KWalletTransaction *_xact = new KWalletTransaction();
200 _xact->tType = KWalletTransaction::CloseCancelled;
201 _xact->appid = _curtrans->appid;
202 _xact->wallet = _curtrans->wallet;
203 _xact->service = _curtrans->service;
204 _transactions.append(_xact);
207 // emit the AsyncOpened signal as a reply
208 emit walletAsyncOpened(_curtrans->tId, res);
209 break;
211 case KWalletTransaction::OpenFail:
212 // emit the AsyncOpened signal with an invalid handle
213 emit walletAsyncOpened(_curtrans->tId, -1);
214 break;
216 case KWalletTransaction::ChangePassword:
217 doTransactionChangePassword(_curtrans->appid, _curtrans->wallet, _curtrans->wId);
218 break;
220 case KWalletTransaction::CloseCancelled:
221 doTransactionOpenCancelled(_curtrans->appid, _curtrans->wallet,
222 _curtrans->service);
223 break;
225 case KWalletTransaction::Unknown:
226 break;
227 default:
228 break;
231 delete _curtrans;
232 _curtrans = 0;
235 processing = false;
240 int KWalletD::openPath(const QString& path, qlonglong wId, const QString& appid) {
241 int tId = openPathAsync(path, wId, appid, false);
242 if (tId < 0) {
243 return tId;
246 // wait for the open-transaction to be processed
247 KWalletOpenLoop loop(this);
248 return loop.waitForAsyncOpen(tId);
251 int KWalletD::open(const QString& wallet, qlonglong wId, const QString& appid) {
252 int tId = openAsync(wallet, wId, appid, false);
253 if (tId < 0) {
254 return tId;
257 // wait for the open-transaction to be processed
258 KWalletOpenLoop loop(this);
259 return loop.waitForAsyncOpen(tId);
262 int KWalletD::openAsync(const QString& wallet, qlonglong wId, const QString& appid,
263 bool handleSession) {
264 if (!_enabled) { // guard
265 return -1;
268 if (!QRegExp("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\.\\+\\_]+$").exactMatch(wallet)) {
269 return -1;
272 KWalletTransaction *xact = new KWalletTransaction;
273 _transactions.append(xact);
275 xact->appid = appid;
276 xact->wallet = wallet;
277 xact->wId = wId;
278 xact->modal = true; // mark dialogs as modal, the app has blocking wait
279 xact->tType = KWalletTransaction::Open;
280 xact->isPath = false;
281 if (handleSession) {
282 xact->service = message().service();
284 QTimer::singleShot(0, this, SLOT(processTransactions()));
285 checkActiveDialog();
286 // opening is in progress. return the transaction number
287 return xact->tId;
290 int KWalletD::openPathAsync(const QString& path, qlonglong wId, const QString& appid,
291 bool handleSession) {
292 if (!_enabled) { // gaurd
293 return -1;
296 KWalletTransaction *xact = new KWalletTransaction;
297 _transactions.append(xact);
299 xact->appid = appid;
300 xact->wallet = path;
301 xact->wId = wId;
302 xact->modal = true;
303 xact->tType = KWalletTransaction::Open;
304 xact->isPath = true;
305 if (handleSession) {
306 xact->service = message().service();
308 QTimer::singleShot(0, this, SLOT(processTransactions()));
309 checkActiveDialog();
310 // opening is in progress. return the transaction number
311 return xact->tId;
314 // Sets up a dialog that will be shown by kwallet.
315 void KWalletD::setupDialog( QWidget* dialog, WId wId, const QString& appid, bool modal ) {
316 if( wId != 0 )
317 KWindowSystem::setMainWindow( dialog, wId ); // correct, set dialog parent
318 else {
319 if( appid.isEmpty())
320 kWarning() << "Using kwallet without parent window!";
321 else
322 kWarning() << "Application '" << appid << "' using kwallet without parent window!";
323 // allow dialog activation even if it interrupts, better than trying hacks
324 // with keeping the dialog on top or on all desktops
325 kapp->updateUserTimestamp();
327 #ifdef Q_WS_X11
328 if( modal )
329 KWindowSystem::setState( dialog->winId(), NET::Modal );
330 else
331 KWindowSystem::clearState( dialog->winId(), NET::Modal );
332 #endif
333 activeDialog = dialog;
336 // If there's a dialog already open and another application tries some operation that'd lead to
337 // opening a dialog, that application will be blocked by this dialog. A proper solution would
338 // be to set the second application's window also as a parent for the active dialog, so that
339 // KWin properly handles focus changes and so on, but there's currently no support for multiple
340 // dialog parents. Hopefully to be done in KDE4, for now just use all kinds of bad hacks to make
341 // sure the user doesn't overlook the active dialog.
342 void KWalletD::checkActiveDialog() {
343 if( !activeDialog || activeDialog->isHidden())
344 return;
345 kapp->updateUserTimestamp();
346 #ifdef Q_WS_X11
347 KWindowSystem::setState( activeDialog->winId(), NET::KeepAbove );
348 KWindowSystem::setOnAllDesktops( activeDialog->winId(), true );
349 KWindowSystem::forceActiveWindow( activeDialog->winId());
350 #endif
354 int KWalletD::doTransactionOpen(const QString& appid, const QString& wallet, bool isPath,
355 qlonglong wId, bool modal, const QString& service) {
356 if (_firstUse && !wallets().contains(KWallet::Wallet::LocalWallet()) && !isPath) {
357 // First use wizard
358 KWalletWizard *wiz = new KWalletWizard(0);
359 wiz->setWindowTitle(i18n("KDE Wallet Service"));
360 setupDialog( wiz, (WId)wId, appid, modal );
361 int rc = wiz->exec();
362 if (rc == QDialog::Accepted) {
363 bool useWallet = wiz->field("useWallet").toBool();
364 KConfig kwalletrc("kwalletrc");
365 KConfigGroup cfg(&kwalletrc, "Wallet");
366 cfg.writeEntry("First Use", false);
367 cfg.writeEntry("Enabled", useWallet);
368 cfg.writeEntry("Close When Idle", wiz->field("closeWhenIdle").toBool());
369 cfg.writeEntry("Use One Wallet", !wiz->field("networkWallet").toBool());
370 cfg.sync();
371 reconfigure();
373 if (!useWallet) {
374 delete wiz;
375 return -1;
378 // Create the wallet
379 KWallet::Backend *b = new KWallet::Backend(KWallet::Wallet::LocalWallet());
380 QString pass = wiz->field("pass1").toString();
381 QByteArray p(pass.toUtf8(), pass.length());
382 b->open(p);
383 p.fill(0);
384 b->createFolder(KWallet::Wallet::PasswordFolder());
385 b->createFolder(KWallet::Wallet::FormDataFolder());
386 b->close(true);
387 delete b;
388 delete wiz;
389 } else {
390 delete wiz;
391 return -1;
393 } else if (_firstUse && !isPath) {
394 KConfig kwalletrc("kwalletrc");
395 KConfigGroup cfg(&kwalletrc, "Wallet");
396 _firstUse = false;
397 cfg.writeEntry("First Use", false);
400 int rc = internalOpen(appid, wallet, isPath, WId(wId), modal, service);
401 return rc;
405 int KWalletD::internalOpen(const QString& appid, const QString& wallet, bool isPath, WId w,
406 bool modal, const QString& service) {
407 bool brandNew = false;
409 QString thisApp;
410 if (appid.isEmpty()) {
411 thisApp = "KDE System";
412 } else {
413 thisApp = appid;
416 if (implicitDeny(wallet, thisApp)) {
417 return -1;
420 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
421 int rc = walletInfo.first;
422 if (rc == -1) {
423 if (_wallets.count() > 20) {
424 kDebug() << "Too many wallets open.";
425 return -1;
428 KWallet::Backend *b = new KWallet::Backend(wallet, isPath);
429 QString password;
430 bool emptyPass = false;
431 if ((isPath && QFile::exists(wallet)) || (!isPath && KWallet::Backend::exists(wallet))) {
432 int pwless = b->open(QByteArray());
433 if (0 != pwless || !b->isOpen()) {
434 if (pwless == 0) {
435 // release, start anew
436 delete b;
437 b = new KWallet::Backend(wallet, isPath);
439 KPasswordDialog *kpd = new KPasswordDialog();
440 if (appid.isEmpty()) {
441 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)));
442 } else {
443 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)));
445 brandNew = false;
446 // don't use KStdGuiItem::open() here which has trailing ellipsis!
447 kpd->setButtonGuiItem(KDialog::Ok,KGuiItem( i18n( "&Open" ), "document-open"));
448 kpd->setCaption(i18n("KDE Wallet Service"));
449 kpd->setPixmap(KIcon("kwalletmanager").pixmap(KIconLoader::SizeHuge));
450 while (!b->isOpen()) {
451 setupDialog( kpd, w, appid, modal );
452 if (kpd->exec() == KDialog::Accepted) {
453 password = kpd->password();
454 int rc = b->open(password.toUtf8());
455 if (!b->isOpen()) {
456 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)));
458 } else {
459 break;
462 delete kpd;
463 } else {
464 emptyPass = true;
466 } else {
467 KNewPasswordDialog *kpd = new KNewPasswordDialog();
468 if (wallet == KWallet::Wallet::LocalWallet() ||
469 wallet == KWallet::Wallet::NetworkWallet())
471 // Auto create these wallets.
472 if (appid.isEmpty()) {
473 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."));
474 } else {
475 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)));
477 } else {
478 if (appid.length() == 0) {
479 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)));
480 } else {
481 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)));
484 brandNew = true;
485 kpd->setCaption(i18n("KDE Wallet Service"));
486 kpd->setButtonGuiItem(KDialog::Ok,KGuiItem(i18n("C&reate"),"document-new"));
487 kpd->setPixmap(KIcon("kwalletmanager").pixmap(96, 96));
488 while (!b->isOpen()) {
489 setupDialog( kpd, w, appid, modal );
490 if (kpd->exec() == KDialog::Accepted) {
491 password = kpd->password();
492 int rc = b->open(password.toUtf8());
493 if (!b->isOpen()) {
494 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)));
496 } else {
497 break;
500 delete kpd;
505 if (!emptyPass && (password.isNull() || !b->isOpen())) {
506 delete b;
507 return -1;
510 if (emptyPass && _openPrompt && !isAuthorizedApp(appid, wallet, w)) {
511 delete b;
512 return -1;
515 _wallets.insert(rc = generateHandle(), b);
516 _sessions.addSession(appid, service, rc);
517 _syncTimers.addTimer(rc, _syncTime);
519 if (brandNew) {
520 createFolder(rc, KWallet::Wallet::PasswordFolder(), appid);
521 createFolder(rc, KWallet::Wallet::FormDataFolder(), appid);
524 b->ref();
525 if (_closeIdle) {
526 _closeTimers.addTimer(rc, _idleTime);
528 if (brandNew)
529 emit walletCreated(wallet);
530 emit walletOpened(wallet);
531 if (_wallets.count() == 1 && _launchManager) {
532 KToolInvocation::startServiceByDesktopName("kwalletmanager-kwalletd");
534 } else {
535 if (!_sessions.hasSession(appid, rc) && !isAuthorizedApp(appid, wallet, w)) {
536 return -1;
538 _sessions.addSession(appid, service, rc);
539 _wallets.value(rc)->ref();
542 return rc;
546 bool KWalletD::isAuthorizedApp(const QString& appid, const QString& wallet, WId w) {
547 int response = 0;
549 QString thisApp;
550 if (appid.isEmpty()) {
551 thisApp = "KDE System";
552 } else {
553 thisApp = appid;
556 if (!implicitAllow(wallet, thisApp)) {
557 KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc")->group("Auto Allow");
558 if (!cfg.isEntryImmutable(wallet)) {
559 KBetterThanKDialog *dialog = new KBetterThanKDialog;
560 if (appid.isEmpty()) {
561 dialog->setLabel(i18n("<qt>KDE has requested access to the open wallet '<b>%1</b>'.</qt>", Qt::escape(wallet)));
562 } else {
563 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)));
565 setupDialog( dialog, w, appid, false );
566 response = dialog->exec();
567 delete dialog;
571 if (response == 0 || response == 1) {
572 if (response == 1) {
573 KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc")->group("Auto Allow");
574 QStringList apps = cfg.readEntry(wallet, QStringList());
575 if (!apps.contains(thisApp)) {
576 if (cfg.isEntryImmutable(wallet)) {
577 return false;
579 apps += thisApp;
580 _implicitAllowMap[wallet] += thisApp;
581 cfg.writeEntry(wallet, apps);
582 cfg.sync();
585 } else if (response == 3) {
586 KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc")->group("Auto Deny");
587 QStringList apps = cfg.readEntry(wallet, QStringList());
588 if (!apps.contains(thisApp)) {
589 apps += thisApp;
590 _implicitDenyMap[wallet] += thisApp;
591 cfg.writeEntry(wallet, apps);
592 cfg.sync();
594 return false;
595 } else {
596 return false;
598 return true;
602 int KWalletD::deleteWallet(const QString& wallet) {
603 QString path = KGlobal::dirs()->saveLocation("kwallet") + QDir::separator() + wallet + ".kwl";
605 if (QFile::exists(path)) {
606 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
607 internalClose(walletInfo.second, walletInfo.first, true);
608 QFile::remove(path);
609 emit walletDeleted(wallet);
610 return 0;
613 return -1;
617 void KWalletD::changePassword(const QString& wallet, qlonglong wId, const QString& appid) {
618 KWalletTransaction *xact = new KWalletTransaction;
620 //msg.setDelayedReply(true);
621 xact->appid = appid;
622 xact->wallet = wallet;
623 xact->wId = wId;
624 xact->modal = false;
625 xact->tType = KWalletTransaction::ChangePassword;
627 _transactions.append(xact);
629 QTimer::singleShot(0, this, SLOT(processTransactions()));
630 checkActiveDialog();
631 checkActiveDialog();
634 void KWalletD::initiateSync(int handle) {
635 // add a timer and reset it right away
636 _syncTimers.addTimer(handle, _syncTime);
637 _syncTimers.resetTimer(handle, _syncTime);
640 void KWalletD::doTransactionChangePassword(const QString& appid, const QString& wallet, qlonglong wId) {
642 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
643 int handle = walletInfo.first;
644 KWallet::Backend* w = walletInfo.second;
646 bool reclose = false;
647 if (!w) {
648 handle = doTransactionOpen(appid, wallet, false, wId, false, false);
649 if (-1 == handle) {
650 KMessageBox::sorryWId((WId)wId, i18n("Unable to open wallet. The wallet must be opened in order to change the password."), i18n("KDE Wallet Service"));
651 return;
654 w = _wallets.value(handle);
655 reclose = true;
658 assert(w);
660 KNewPasswordDialog *kpd = new KNewPasswordDialog();
661 kpd->setPrompt(i18n("<qt>Please choose a new password for the wallet '<b>%1</b>'.</qt>", Qt::escape(wallet)));
662 kpd->setCaption(i18n("KDE Wallet Service"));
663 kpd->setAllowEmptyPasswords(true);
664 setupDialog( kpd, (WId)wId, appid, false );
665 if (kpd->exec() == KDialog::Accepted) {
666 QString p = kpd->password();
667 if (!p.isNull()) {
668 w->setPassword(p.toUtf8());
669 int rc = w->close(true);
670 if (rc < 0) {
671 KMessageBox::sorryWId((WId)wId, i18n("Error re-encrypting the wallet. Password was not changed."), i18n("KDE Wallet Service"));
672 reclose = true;
673 } else {
674 rc = w->open(p.toUtf8());
675 if (rc < 0) {
676 KMessageBox::sorryWId((WId)wId, i18n("Error reopening the wallet. Data may be lost."), i18n("KDE Wallet Service"));
677 reclose = true;
683 delete kpd;
685 if (reclose) {
686 internalClose(w, handle, true);
691 int KWalletD::close(const QString& wallet, bool force) {
692 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
693 int handle = walletInfo.first;
694 KWallet::Backend* w = walletInfo.second;
696 return internalClose(w, handle, force);
700 int KWalletD::internalClose(KWallet::Backend *w, int handle, bool force) {
701 if (w) {
702 const QString& wallet = w->walletName();
703 if ((w->refCount() == 0 && !_leaveOpen) || force) {
704 // this is only a safety measure. sessions should be gone already.
705 _sessions.removeAllSessions(handle);
706 if (_closeIdle) {
707 _closeTimers.removeTimer(handle);
709 _syncTimers.removeTimer(handle);
710 _wallets.remove(handle);
711 w->close(true);
712 doCloseSignals(handle, wallet);
713 delete w;
714 return 0;
716 return 1;
719 return -1;
723 int KWalletD::close(int handle, bool force, const QString& appid) {
724 KWallet::Backend *w = _wallets.value(handle);
726 if (w) {
727 if (_sessions.hasSession(appid, handle)) {
728 // remove one handle for the application
729 bool removed = _sessions.removeSession(appid, message().service(), handle);
730 // alternatively try sessionless
731 if (removed || _sessions.removeSession(appid, "", handle)) {
732 w->deref();
734 return internalClose(w, handle, force);
736 return 1; // not closed, handle unknown
738 return -1; // not open to begin with, or other error
742 bool KWalletD::isOpen(const QString& wallet) {
743 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
744 return walletInfo.second != 0;
748 bool KWalletD::isOpen(int handle) {
749 if (handle == 0) {
750 return false;
753 KWallet::Backend *rc = _wallets.value(handle);
755 if (rc == 0 && ++_failed > 5) {
756 _failed = 0;
757 QTimer::singleShot(0, this, SLOT(notifyFailures()));
758 } else if (rc != 0) {
759 _failed = 0;
762 return rc != 0;
766 QStringList KWalletD::wallets() const {
767 QString path = KGlobal::dirs()->saveLocation("kwallet");
768 QDir dir(path, "*.kwl");
769 QStringList rc;
771 dir.setFilter(QDir::Files | QDir::Hidden);
773 foreach (const QFileInfo &fi, dir.entryInfoList()) {
774 QString fn = fi.fileName();
775 if (fn.endsWith(".kwl")) {
776 fn.truncate(fn.length()-4);
778 rc += fn;
780 return rc;
784 void KWalletD::sync(int handle, const QString& appid) {
785 KWallet::Backend *b;
787 // get the wallet and check if we have a password for it (safety measure)
788 if ((b = getWallet(appid, handle))) {
789 QString wallet = b->walletName();
790 b->sync();
794 void KWalletD::timedOutSync(int handle) {
795 _syncTimers.removeTimer(handle);
796 if (_wallets.contains(handle) && _wallets[handle]) {
797 _wallets[handle]->sync();
801 void KWalletD::doTransactionOpenCancelled(const QString& appid, const QString& wallet,
802 const QString& service) {
804 // there will only be one session left to remove - all others
805 // have already been removed in slotServiceOwnerChanged and all
806 // transactions for opening new sessions have been deleted.
807 if (!_sessions.hasSession(appid)) {
808 return;
811 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
812 int handle = walletInfo.first;
813 KWallet::Backend *b = walletInfo.second;
814 if (handle != -1 && b) {
815 b->deref();
816 internalClose(b, handle, false);
819 // close the session in case the wallet hasn't been closed yet
820 _sessions.removeSession(appid, service, handle);
823 QStringList KWalletD::folderList(int handle, const QString& appid) {
824 KWallet::Backend *b;
826 if ((b = getWallet(appid, handle))) {
827 return b->folderList();
830 return QStringList();
834 bool KWalletD::hasFolder(int handle, const QString& f, const QString& appid) {
835 KWallet::Backend *b;
837 if ((b = getWallet(appid, handle))) {
838 return b->hasFolder(f);
841 return false;
845 bool KWalletD::removeFolder(int handle, const QString& f, const QString& appid) {
846 KWallet::Backend *b;
848 if ((b = getWallet(appid, handle))) {
849 bool rc = b->removeFolder(f);
850 initiateSync(handle);
851 emit folderListUpdated(b->walletName());
852 return rc;
855 return false;
859 bool KWalletD::createFolder(int handle, const QString& f, const QString& appid) {
860 KWallet::Backend *b;
862 if ((b = getWallet(appid, handle))) {
863 bool rc = b->createFolder(f);
864 initiateSync(handle);
865 emit folderListUpdated(b->walletName());
866 return rc;
869 return false;
873 QByteArray KWalletD::readMap(int handle, const QString& folder, const QString& key, const QString& appid) {
874 KWallet::Backend *b;
876 if ((b = getWallet(appid, handle))) {
877 b->setFolder(folder);
878 KWallet::Entry *e = b->readEntry(key);
879 if (e && e->type() == KWallet::Wallet::Map) {
880 return e->map();
884 return QByteArray();
888 QVariantMap KWalletD::readMapList(int handle, const QString& folder, const QString& key, const QString& appid) {
889 KWallet::Backend *b;
891 if ((b = getWallet(appid, handle))) {
892 b->setFolder(folder);
893 QVariantMap rc;
894 foreach (KWallet::Entry *entry, b->readEntryList(key)) {
895 if (entry->type() == KWallet::Wallet::Map) {
896 rc.insert(entry->key(), entry->map());
899 return rc;
902 return QVariantMap();
906 QByteArray KWalletD::readEntry(int handle, const QString& folder, const QString& key, const QString& appid) {
907 KWallet::Backend *b;
909 if ((b = getWallet(appid, handle))) {
910 b->setFolder(folder);
911 KWallet::Entry *e = b->readEntry(key);
912 if (e) {
913 return e->value();
917 return QByteArray();
921 QVariantMap KWalletD::readEntryList(int handle, const QString& folder, const QString& key, const QString& appid) {
922 KWallet::Backend *b;
924 if ((b = getWallet(appid, handle))) {
925 b->setFolder(folder);
926 QVariantMap rc;
927 foreach (KWallet::Entry *entry, b->readEntryList(key)) {
928 rc.insert(entry->key(), entry->value());
930 return rc;
933 return QVariantMap();
937 QStringList KWalletD::entryList(int handle, const QString& folder, const QString& appid) {
938 KWallet::Backend *b;
940 if ((b = getWallet(appid, handle))) {
941 b->setFolder(folder);
942 return b->entryList();
945 return QStringList();
949 QString KWalletD::readPassword(int handle, const QString& folder, const QString& key, const QString& appid) {
950 KWallet::Backend *b;
952 if ((b = getWallet(appid, handle))) {
953 b->setFolder(folder);
954 KWallet::Entry *e = b->readEntry(key);
955 if (e && e->type() == KWallet::Wallet::Password) {
956 return e->password();
960 return QString();
964 QVariantMap KWalletD::readPasswordList(int handle, const QString& folder, const QString& key, const QString& appid) {
965 KWallet::Backend *b;
967 if ((b = getWallet(appid, handle))) {
968 b->setFolder(folder);
969 QVariantMap rc;
970 foreach (KWallet::Entry *entry, b->readEntryList(key)) {
971 if (entry->type() == KWallet::Wallet::Password) {
972 rc.insert(entry->key(), entry->password());
975 return rc;
978 return QVariantMap();
982 int KWalletD::writeMap(int handle, const QString& folder, const QString& key, const QByteArray& value, const QString& appid) {
983 KWallet::Backend *b;
985 if ((b = getWallet(appid, handle))) {
986 b->setFolder(folder);
987 KWallet::Entry e;
988 e.setKey(key);
989 e.setValue(value);
990 e.setType(KWallet::Wallet::Map);
991 b->writeEntry(&e);
992 initiateSync(handle);
993 emitFolderUpdated(b->walletName(), folder);
994 return 0;
997 return -1;
1001 int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, const QByteArray& value, int entryType, const QString& appid) {
1002 KWallet::Backend *b;
1004 if ((b = getWallet(appid, handle))) {
1005 b->setFolder(folder);
1006 KWallet::Entry e;
1007 e.setKey(key);
1008 e.setValue(value);
1009 e.setType(KWallet::Wallet::EntryType(entryType));
1010 b->writeEntry(&e);
1011 initiateSync(handle);
1012 emitFolderUpdated(b->walletName(), folder);
1013 return 0;
1016 return -1;
1020 int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, const QByteArray& value, const QString& appid) {
1021 KWallet::Backend *b;
1023 if ((b = getWallet(appid, handle))) {
1024 b->setFolder(folder);
1025 KWallet::Entry e;
1026 e.setKey(key);
1027 e.setValue(value);
1028 e.setType(KWallet::Wallet::Stream);
1029 b->writeEntry(&e);
1030 initiateSync(handle);
1031 emitFolderUpdated(b->walletName(), folder);
1032 return 0;
1035 return -1;
1039 int KWalletD::writePassword(int handle, const QString& folder, const QString& key, const QString& value, const QString& appid) {
1040 KWallet::Backend *b;
1042 if ((b = getWallet(appid, handle))) {
1043 b->setFolder(folder);
1044 KWallet::Entry e;
1045 e.setKey(key);
1046 e.setValue(value);
1047 e.setType(KWallet::Wallet::Password);
1048 b->writeEntry(&e);
1049 initiateSync(handle);
1050 emitFolderUpdated(b->walletName(), folder);
1051 return 0;
1054 return -1;
1058 int KWalletD::entryType(int handle, const QString& folder, const QString& key, const QString& appid) {
1059 KWallet::Backend *b;
1061 if ((b = getWallet(appid, handle))) {
1062 if (!b->hasFolder(folder)) {
1063 return KWallet::Wallet::Unknown;
1065 b->setFolder(folder);
1066 if (b->hasEntry(key)) {
1067 return b->readEntry(key)->type();
1071 return KWallet::Wallet::Unknown;
1075 bool KWalletD::hasEntry(int handle, const QString& folder, const QString& key, const QString& appid) {
1076 KWallet::Backend *b;
1078 if ((b = getWallet(appid, handle))) {
1079 if (!b->hasFolder(folder)) {
1080 return false;
1082 b->setFolder(folder);
1083 return b->hasEntry(key);
1086 return false;
1090 int KWalletD::removeEntry(int handle, const QString& folder, const QString& key, const QString& appid) {
1091 KWallet::Backend *b;
1093 if ((b = getWallet(appid, handle))) {
1094 if (!b->hasFolder(folder)) {
1095 return 0;
1097 b->setFolder(folder);
1098 bool rc = b->removeEntry(key);
1099 initiateSync(handle);
1100 emitFolderUpdated(b->walletName(), folder);
1101 return rc ? 0 : -3;
1104 return -1;
1108 void KWalletD::slotServiceOwnerChanged(const QString& name, const QString &oldOwner,
1109 const QString& newOwner)
1111 Q_UNUSED(name);
1113 if (!newOwner.isEmpty()) {
1114 return; // no application exit, don't care.
1117 // as we don't have the application id we have to cycle
1118 // all sessions. As an application can basically open wallets
1119 // with several appids, we can't stop if we found one.
1120 QString service(oldOwner);
1121 QList<KWalletAppHandlePair> sessremove(_sessions.findSessions(service));
1122 KWallet::Backend *b = 0;
1124 // check all sessions for wallets to close
1125 Q_FOREACH(const KWalletAppHandlePair &s, sessremove) {
1126 b = getWallet(s.first, s.second);
1127 if (b) {
1128 b->deref();
1129 internalClose(b, s.second, false);
1133 // remove all the sessions in case they aren't gone yet
1134 Q_FOREACH(const KWalletAppHandlePair &s, sessremove) {
1135 _sessions.removeSession(s.first, service, s.second);
1138 // cancel all open-transactions still running for the service
1139 QList<KWalletTransaction*>::iterator tit;
1140 for (tit = _transactions.begin(); tit != _transactions.end(); ++tit) {
1141 if ((*tit)->tType == KWalletTransaction::Open && (*tit)->service == oldOwner) {
1142 delete (*tit);
1143 *tit = 0;
1146 _transactions.removeAll(0);
1148 // if there's currently an open-transaction being handled,
1149 // mark it as cancelled.
1150 if (_curtrans && _curtrans->tType == KWalletTransaction::Open &&
1151 _curtrans->service == oldOwner) {
1152 _curtrans->cancelled = true;
1156 KWallet::Backend *KWalletD::getWallet(const QString& appid, int handle) {
1157 if (handle == 0) {
1158 return 0L;
1161 KWallet::Backend *w = _wallets.value(handle);
1163 if (w) { // the handle is valid
1164 if (_sessions.hasSession(appid, handle)) {
1165 // the app owns this handle
1166 _failed = 0;
1167 if (_closeIdle) {
1168 _closeTimers.resetTimer(handle, _idleTime);
1170 return w;
1174 if (++_failed > 5) {
1175 _failed = 0;
1176 QTimer::singleShot(0, this, SLOT(notifyFailures()));
1179 return 0L;
1183 void KWalletD::notifyFailures() {
1184 if (!_showingFailureNotify) {
1185 _showingFailureNotify = true;
1186 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"));
1187 _showingFailureNotify = false;
1192 void KWalletD::doCloseSignals(int handle, const QString& wallet) {
1193 emit walletClosed(handle);
1194 emit walletClosed(wallet);
1195 if (_wallets.isEmpty()) {
1196 emit allWalletsClosed();
1201 int KWalletD::renameEntry(int handle, const QString& folder, const QString& oldName, const QString& newName, const QString& appid) {
1202 KWallet::Backend *b;
1204 if ((b = getWallet(appid, handle))) {
1205 b->setFolder(folder);
1206 int rc = b->renameEntry(oldName, newName);
1207 initiateSync(handle);
1208 emitFolderUpdated(b->walletName(), folder);
1209 return rc;
1212 return -1;
1216 QStringList KWalletD::users(const QString& wallet) const {
1217 const QPair<int,KWallet::Backend*> walletInfo = findWallet(wallet);
1218 return _sessions.getApplications(walletInfo.first);
1222 bool KWalletD::disconnectApplication(const QString& wallet, const QString& application) {
1223 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
1224 int handle = walletInfo.first;
1225 KWallet::Backend* backend = walletInfo.second;
1227 if (handle != -1 && _sessions.hasSession(application, handle)) {
1228 int removed = _sessions.removeAllSessions(application, handle);
1230 for (int i = 0; i < removed; ++i) {
1231 backend->deref();
1233 internalClose(backend, handle, false);
1235 emit applicationDisconnected(wallet, application);
1236 return true;
1239 return false;
1243 void KWalletD::emitFolderUpdated(const QString& wallet, const QString& folder) {
1244 emit folderUpdated(wallet, folder);
1248 void KWalletD::emitWalletListDirty() {
1249 emit walletListDirty();
1253 void KWalletD::reconfigure() {
1254 KConfig cfg("kwalletrc");
1255 KConfigGroup walletGroup(&cfg, "Wallet");
1256 _firstUse = walletGroup.readEntry("First Use", true);
1257 _enabled = walletGroup.readEntry("Enabled", true);
1258 _launchManager = walletGroup.readEntry("Launch Manager", true);
1259 _leaveOpen = walletGroup.readEntry("Leave Open", false);
1260 bool idleSave = _closeIdle;
1261 _closeIdle = walletGroup.readEntry("Close When Idle", false);
1262 _openPrompt = walletGroup.readEntry("Prompt on Open", true);
1263 int timeSave = _idleTime;
1264 // in minutes!
1265 _idleTime = walletGroup.readEntry("Idle Timeout", 10) * 60 * 1000;
1266 #ifdef Q_WS_X11
1267 if ( screensaver->isValid() ) {
1268 if (walletGroup.readEntry("Close on Screensaver", false)) {
1269 connect(screensaver, SIGNAL(ActiveChanged(bool)), SLOT(screenSaverChanged(bool)));
1270 } else {
1271 screensaver->disconnect(SIGNAL(ActiveChanged(bool)), this, SLOT(screenSaverChanged(bool)));
1274 #endif
1275 // Handle idle changes
1276 if (_closeIdle) {
1277 if (_idleTime != timeSave) { // Timer length changed
1278 Wallets::const_iterator it = _wallets.constBegin();
1279 const Wallets::const_iterator end = _wallets.constEnd();
1280 for (; it != end; ++it) {
1281 _closeTimers.resetTimer(it.key(), _idleTime);
1285 if (!idleSave) { // add timers for all the wallets
1286 Wallets::const_iterator it = _wallets.constBegin();
1287 const Wallets::const_iterator end = _wallets.constEnd();
1288 for (; it != end; ++it) {
1289 _closeTimers.addTimer(it.key(), _idleTime);
1292 } else {
1293 _closeTimers.clear();
1296 // Update the implicit allow stuff
1297 _implicitAllowMap.clear();
1298 const KConfigGroup autoAllowGroup(&cfg, "Auto Allow");
1299 QStringList entries = autoAllowGroup.entryMap().keys();
1300 for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) {
1301 _implicitAllowMap[*i] = autoAllowGroup.readEntry(*i, QStringList());
1304 // Update the implicit allow stuff
1305 _implicitDenyMap.clear();
1306 const KConfigGroup autoDenyGroup(&cfg, "Auto Deny");
1307 entries = autoDenyGroup.entryMap().keys();
1308 for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) {
1309 _implicitDenyMap[*i] = autoDenyGroup.readEntry(*i, QStringList());
1312 // Update if wallet was enabled/disabled
1313 if (!_enabled) { // close all wallets
1314 while (!_wallets.isEmpty()) {
1315 Wallets::const_iterator it = _wallets.constBegin();
1316 internalClose(it.value(), it.key(), true);
1318 KUniqueApplication::exit(0);
1323 bool KWalletD::isEnabled() const {
1324 return _enabled;
1328 bool KWalletD::folderDoesNotExist(const QString& wallet, const QString& folder) {
1329 if (!wallets().contains(wallet)) {
1330 return true;
1333 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
1334 if (walletInfo.second) {
1335 return walletInfo.second->folderDoesNotExist(folder);
1338 KWallet::Backend *b = new KWallet::Backend(wallet);
1339 b->open(QByteArray());
1340 bool rc = b->folderDoesNotExist(folder);
1341 delete b;
1342 return rc;
1346 bool KWalletD::keyDoesNotExist(const QString& wallet, const QString& folder, const QString& key) {
1347 if (!wallets().contains(wallet)) {
1348 return true;
1351 const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
1352 if (walletInfo.second) {
1353 return walletInfo.second->entryDoesNotExist(folder, key);
1356 KWallet::Backend *b = new KWallet::Backend(wallet);
1357 b->open(QByteArray());
1358 bool rc = b->entryDoesNotExist(folder, key);
1359 delete b;
1360 return rc;
1364 bool KWalletD::implicitAllow(const QString& wallet, const QString& app) {
1365 return _implicitAllowMap[wallet].contains(app);
1369 bool KWalletD::implicitDeny(const QString& wallet, const QString& app) {
1370 return _implicitDenyMap[wallet].contains(app);
1374 void KWalletD::timedOutClose(int id) {
1375 KWallet::Backend *w = _wallets.value(id);
1376 if (w) {
1377 internalClose(w, id, true);
1382 void KWalletD::closeAllWallets() {
1383 Wallets walletsCopy = _wallets;
1385 Wallets::const_iterator it = walletsCopy.constBegin();
1386 const Wallets::const_iterator end = walletsCopy.constEnd();
1387 for (; it != end; ++it) {
1388 internalClose(it.value(), it.key(), true);
1391 walletsCopy.clear();
1393 // All of this should be basically noop. Let's just be safe.
1394 _wallets.clear();
1398 QString KWalletD::networkWallet() {
1399 return KWallet::Wallet::NetworkWallet();
1403 QString KWalletD::localWallet() {
1404 return KWallet::Wallet::LocalWallet();
1407 void KWalletD::screenSaverChanged(bool s)
1409 if (s)
1410 closeAllWallets();
1413 #include "kwalletd.moc"