KWallet

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

KDE's Doxygen guidelines are available online.