KWallet

kwalletd.cpp
1/*
2 SPDX-FileCopyrightText: 2024 Marco Martin <notmart@gmail.com>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "kwalletd.h"
8
9#include <QDBusConnection>
10#include <QDebug>
11
12#include <KConfig>
13#include <KConfigGroup>
14#include <KLocalizedString>
15#include <KPasswordDialog>
16
17#include "kwalletadaptor.h"
18#include "secretserviceclient.h"
19
20#include "../kwalletbackend/kwalletbackend.h"
21#include "../kwalletbackend/kwalletentry.h"
22
23unsigned int KWalletD::s_lastTransaction = 0;
24
25static void startManagerForKwalletd()
26{
27 if (!QStandardPaths::findExecutable(QStringLiteral("kstart")).isEmpty()) {
28 QProcess::startDetached(QStringLiteral("kstart"), {QStringLiteral("kwalletmanager5"), QStringLiteral("--"), QStringLiteral("--kwalletd")});
29 } else {
30 QProcess::startDetached(QStringLiteral("kwalletmanager5"), QStringList{QStringLiteral("--kwalletd")});
31 }
32}
33
34KWalletD::KWalletD(QObject *parent)
35 : QObject(parent)
36{
37 m_backend = new SecretServiceClient(this);
38
39 new KWalletAdaptor(this);
41 dbus.registerObject(QStringLiteral("/modules/kwalletd5"), this);
42 dbus.registerObject(QStringLiteral("/modules/kwalletd6"), this);
43 dbus.registerService(QStringLiteral("org.kde.kwalletd5"));
44 dbus.registerService(QStringLiteral("org.kde.kwalletd6"));
45
46 auto readStructure = [this]() {
47 bool ok;
48 for (const QString &wallet : m_backend->listCollections(&ok)) {
49 for (const QString &folder : m_backend->listFolders(wallet, &ok)) {
50 m_structure.insert(wallet, folder);
51 }
52 }
53 qCDebug(KWALLETD_LOG) << "Structure:" << m_structure;
54 qCDebug(KWALLETD_LOG) << "Default wallet:" << m_backend->defaultCollection(&ok);
55
56 // This function will only do something if the config file tells it to actually do the migration
57 migrateData();
58 };
59 readStructure();
60
61 connect(m_backend, &SecretServiceClient::serviceChanged, this, [this, readStructure]() {
62 if (m_backend->isAvailable()) {
63 readStructure();
64 Q_EMIT walletListDirty();
65 } else {
66 closeAllWallets();
67 }
68 });
69
70 connect(m_backend, &SecretServiceClient::collectionDirty, this, [this, readStructure](const QString &wallet) {
71 bool ok;
72 QStringList folders = m_structure.values(wallet);
73 QSet<QString> oldFolders(folders.constBegin(), folders.constEnd());
74 folders = m_backend->listFolders(wallet, &ok);
75 QSet<QString> newFolders(folders.constBegin(), folders.constEnd());
76
77 // Reload m_structure
78 m_structure.clear();
79 readStructure();
80
81 Q_EMIT folderListUpdated(wallet);
82 // We are not sure which folder or item has been updated, reload all folders of all wallets
83 for (const QString &folder : m_structure.values(wallet)) {
84 Q_EMIT folderUpdated(folder, wallet);
85 }
86 });
87
88 connect(m_backend, &SecretServiceClient::collectionCreated, this, &KWalletD::walletCreated);
89 connect(m_backend, &SecretServiceClient::collectionDeleted, this, &KWalletD::walletDeleted);
90 connect(m_backend, &SecretServiceClient::collectionListDirty, this, &KWalletD::walletListDirty);
91
92 reconfigure();
93
94 m_configWatcher = KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("kwalletrc")));
95 connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this]() {
96 bool ok = false;
97 KConfig cfg(QStringLiteral("kwalletrc"));
98 KConfigGroup walletGroup(&cfg, QStringLiteral("Wallet"));
99 const QString defaultCollection = m_backend->defaultCollection(&ok);
100 const QString newDefaultWallet = walletGroup.readEntry(QStringLiteral("Default Wallet"), defaultCollection);
101
102 if (newDefaultWallet != defaultCollection) {
103 m_backend->setDefaultCollection(newDefaultWallet, &ok);
104 }
105 });
106}
107
108KWalletD::~KWalletD()
109{
110}
111
112bool KWalletD::migrateWallet(const QString &sourceWallet, const QString &destWallet)
113{
114 qCWarning(KWALLETD_LOG) << "Migrating" << sourceWallet;
115 KWallet::Backend backend(sourceWallet, sourceWallet.endsWith(QStringLiteral(".kwl")));
116 backend.open(QByteArray());
117 while (!backend.isOpen()) {
118 KPasswordDialog dlg;
119 dlg.setPrompt(i18n("Migrating the wallet \"%1\". Please provide the password to unlock.", sourceWallet));
120 dlg.setRevealPasswordMode(KPassword::RevealMode::OnlyNew);
121
122 if (dlg.exec() != QDialog::Accepted) {
123 return false;
124 }
125 backend.open(dlg.password().toUtf8());
126 }
127
128 bool ok = false;
129
130 const QStringList allDestWallets = m_backend->listCollections(&ok);
131 if (!ok) {
132 return false;
133 }
134 if (!allDestWallets.contains(destWallet)) {
135 qCDebug(KWALLETD_LOG) << "Creating" << destWallet;
136 m_backend->createCollection(destWallet, &ok);
137 if (!ok) {
138 return false;
139 }
140 }
141 qCDebug(KWALLETD_LOG) << "Unlocking" << destWallet;
142 bool unlocked = m_backend->unlockCollection(destWallet, &ok);
143 if (!ok || !unlocked) {
144 return false;
145 }
146
147 for (const QString &folder : backend.folderList()) {
148 backend.setFolder(folder);
149 for (KWallet::Entry *entry : backend.entriesList()) {
150 switch (entry->type()) {
151 case KWallet::Wallet::Password:
152 writeString(entry->key(), entry->password(), folder, destWallet, &ok);
153 break;
154 case KWallet::Wallet::Stream:
155 writeBinary(entry->key(), entry->value(), folder, destWallet, &ok);
156 break;
157 case KWallet::Wallet::Map: {
158 QByteArray mapData = entry->map();
159 if (!mapData.isEmpty()) {
160 QDataStream ds(&mapData, QIODevice::ReadOnly);
161 QMap<QString, QString> v;
162 ds >> v;
163 QJsonObject obj;
164 for (auto it = v.constBegin(); it != v.constEnd(); it++) {
165 obj[it.key()] = it.value();
166 }
167
168 writeRawJson(entry->key(), QJsonDocument(obj).toJson(QJsonDocument::Compact), folder, destWallet, &ok);
169 }
170 break;
171 }
172 default:
173 break;
174 }
175 }
176 }
177 return true;
178}
179
180void KWalletD::migrateData()
181{
182 if (!m_backend->isAvailable() || m_backend->useKSecretBackend()) {
183 return;
184 }
185 KConfig cfg(QStringLiteral("kwalletrc"));
186 KConfigGroup walletGroup(&cfg, QStringLiteral("Wallet"));
187 KConfigGroup migrationGroup(&cfg, QStringLiteral("Migration"));
188
189 if (!migrationGroup.readEntry(QStringLiteral("MigrateTo3rdParty"), false)) {
190 return;
191 }
192
193 QStringList walletsMigrated = migrationGroup.readEntry(QStringLiteral("WalletsMigratedToSecretService"), QStringList());
194
195 // Get the list of all known kwallets
196 const QString path = KWallet::Backend::getSaveLocation();
197 QDir dir(path, QStringLiteral("*.kwl"));
198 QStringList wallets;
199
200 dir.setFilter(QDir::Files | QDir::Hidden);
201
202 const auto list = dir.entryInfoList();
203 // This code to list wallets comed from original kwalletd, to be use the
204 // list is the same
205 for (const QFileInfo &fi : list) {
206 QString fn = fi.fileName();
207 if (fn.endsWith(QLatin1String(".kwl"))) {
208 fn.truncate(fn.length() - 4);
209 }
210 wallets += KWallet::Backend::decodeWalletName(fn);
211 }
212
213 bool ok = false;
214 const QString oldDefaultWallet = walletGroup.readEntry(QStringLiteral("Default Wallet"), QStringLiteral("kdewallet"));
215 for (const QString &sourceWallet : std::as_const(wallets)) {
216 QString destWallet = sourceWallet;
217 // Use the SecretService default collection as the new default wallet
218 if (sourceWallet == oldDefaultWallet) {
219 destWallet = m_backend->defaultCollection(&ok);
220 }
221 if (walletsMigrated.contains(sourceWallet)) {
222 continue;
223 }
224 if (migrateWallet(sourceWallet, destWallet)) {
225 walletsMigrated << sourceWallet;
226 }
227 }
228
229 // The default wallet will always be the one Secret Service says
230 walletGroup.writeEntry(QStringLiteral("Default Wallet"), m_backend->defaultCollection(&ok));
231
232 migrationGroup.writeEntry(QStringLiteral("WalletsMigratedToSecretService"), walletsMigrated);
233 cfg.sync();
234}
235
236QString KWalletD::walletForHandle(int handle, const QString &appId)
237{
238 // This is called by all accessing functions, so reset the idle timer here
239 if (m_closeIdle) {
240 const QPair<int, QString> key(handle, appId);
241 killTimer(m_idleTimers[key]);
242 m_idleTimers[key] = startTimer(m_idleTime);
243 }
244
245 const QString walletName = m_openWallets.value(QPair<int, QString>(handle, appId));
246
247 return walletName;
248}
249
250QString KWalletD::folderPath(const QString &folder, const QString &key) const
251{
252 return folder % QStringLiteral("/") % key;
253}
254
255KWalletD::EntryType KWalletD::keyType(const QString &wallet, const QString &folder, const QString &key)
256{
257 bool ok;
258 const QHash<QString, QString> hash = m_backend->readMetadata(key, folder, wallet, &ok);
259
260 if (!ok) {
261 return Unknown;
262 }
263
264 const QString typeStr = hash.value(QStringLiteral("type"));
265
266 if (typeStr == QStringLiteral("map")) {
267 return Map;
268 } else if (typeStr == QStringLiteral("base64") || typeStr == QStringLiteral("binary")) {
269 return Stream;
270 } else {
271 return Password;
272 }
273}
274
275QString KWalletD::readString(const QString &key, const QString &folder, const QString &wallet, bool *ok)
276{
277 return QString::fromUtf8(m_backend->readEntry(key, SecretServiceClient::PlainText, folder, wallet, ok));
278}
279
280QByteArray KWalletD::readRawJson(const QString &key, const QString &folder, const QString &wallet, bool *ok)
281{
282 return m_backend->readEntry(key, SecretServiceClient::Map, folder, wallet, ok);
283}
284
285QByteArray KWalletD::readBinary(const QString &key, const QString &folder, const QString &wallet, bool *ok)
286{
287 // Try first to read the base64 version, if fails, read the raw binary version as legacy fallback
288 QByteArray ret = m_backend->readEntry(key, SecretServiceClient::Base64, folder, wallet, ok);
289 if (!ok || ret.isEmpty()) {
290 ret = m_backend->readEntry(key, SecretServiceClient::Binary, folder, wallet, ok);
291 }
292 return ret;
293}
294
295void KWalletD::writeString(const QString &key, const QString &value, const QString &folder, const QString &wallet, bool *ok)
296{
297 m_backend->writeEntry(folderPath(folder, key), key, value.toUtf8(), SecretServiceClient::PlainText, folder, wallet, ok);
298}
299
300void KWalletD::writeBinary(const QString &key, const QByteArray &value, const QString &folder, const QString &wallet, bool *ok)
301{
302 m_backend->writeEntry(folderPath(folder, key), key, value, SecretServiceClient::Base64, folder, wallet, ok);
303}
304
305void KWalletD::writeRawJson(const QString &key, const QByteArray &value, const QString &folder, const QString &wallet, bool *ok)
306{
307 m_backend->writeEntry(folderPath(folder, key), key, value, SecretServiceClient::Map, folder, wallet, ok);
308}
309
310void KWalletD::removeItem(const QString &key, const QString &folder, const QString &wallet, bool *ok)
311{
312 m_backend->deleteEntry(key, folder, wallet, ok);
313}
314
315void KWalletD::timerEvent(QTimerEvent *ev)
316{
317 const int timer = ev->timerId();
318
319 for (auto it = m_idleTimers.constBegin(); it != m_idleTimers.end(); it++) {
320 if (it.value() == timer) {
321 close(it.key().first, true, it.key().second);
322 break;
323 }
324 }
325 killTimer(timer);
326}
327
328// KWallet API
329
330bool KWalletD::isEnabled() const
331{
332 return m_enabled && m_backend->isAvailable();
333}
334
335int KWalletD::openInternal(const QString &wallet, qlonglong wId, const QString &appId)
336{
337 if (wId < 0) {
338 return -1;
339 }
340
341 bool ok;
342 const QStringList wallets = m_backend->listCollections(&ok);
343 if (!ok) {
344 return -1;
345 }
346 if (!wallets.contains(wallet)) {
347 m_backend->createCollection(wallet, &ok);
348 if (!ok) {
349 return -1;
350 }
351 }
352 bool unlocked = m_backend->unlockCollection(wallet, &ok);
353 if (!ok || !unlocked) {
354 return -1;
355 }
356
357 for (const QString &folder : m_backend->listFolders(wallet, &ok)) {
358 m_structure.insert(wallet, folder);
359 }
360
361 QRandomGenerator rand = QRandomGenerator::securelySeeded();
362 const int rnd = std::abs(int(rand.generate()));
363
364 m_openWallets[QPair<int, QString>(rnd, appId)] = wallet;
365
366 if (m_closeIdle) {
367 m_idleTimers[QPair<int, QString>(rnd, appId)] = startTimer(m_idleTime);
368 }
369
370 return rnd;
371}
372
373int KWalletD::open(const QString &wallet, qlonglong wId, const QString &appId)
374{
375 int rnd = openInternal(wallet, wId, appId);
376 if (rnd < 0) {
377 return -1;
378 }
379
380 Q_EMIT walletOpened(wallet);
381 return rnd;
382}
383
384int KWalletD::openPath(const QString &path, qlonglong wId, const QString &appId)
385{
386 // This is intended to just be a stub, so it will always fail
387 Q_UNUSED(path);
388 Q_UNUSED(wId);
389 Q_UNUSED(appId);
390 return -1;
391}
392
393int KWalletD::openAsync(const QString &wallet, qlonglong wId, const QString &appId, bool handleSession)
394{
395 Q_UNUSED(handleSession);
396
397 if (wId < 0) {
398 return -1;
399 }
400
401 const int tid = ++s_lastTransaction;
402
403 QTimer::singleShot(0, [this, wallet, wId, appId, tid]() {
404 int rnd = openInternal(wallet, wId, appId);
405 if (rnd < 0) {
406 return;
407 }
408 Q_EMIT walletAsyncOpened(tid, rnd);
409 });
410
411 return tid;
412}
413
414int KWalletD::openPathAsync(const QString &path, qlonglong wId, const QString &appId, bool handleSession)
415{
416 // This is intended to just be a stub, so it will always fail
417 Q_UNUSED(path);
418 Q_UNUSED(wId);
419 Q_UNUSED(appId);
420 Q_UNUSED(handleSession);
421 return -1;
422}
423
424int KWalletD::close(const QString &wallet, bool force)
425{
426 QSet<QString> apps;
427 auto it = m_openWallets.begin();
428
429 while (it != m_openWallets.end()) {
430 if (it.value() == wallet) {
431 if (force) {
432 apps << it.key().second;
433 it = m_openWallets.erase(it);
434 } else {
435 return 1;
436 }
437 } else {
438 ++it;
439 }
440 }
441
442 Q_EMIT walletClosed(wallet);
443 if (m_openWallets.isEmpty()) {
444 Q_EMIT allWalletsClosed();
445 }
446 QSet<QString> newApps;
447 for (auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
448 newApps << it.key().second;
449 }
450 QSet<QString> disconnected = apps.subtract(newApps);
451 for (const QString &appId : std::as_const(disconnected)) {
452 Q_EMIT applicationDisconnected(wallet, appId);
453 }
454
455 return 0;
456}
457
458int KWalletD::close(int handle, bool force, const QString &appId)
459{
460 Q_UNUSED(force);
461 const QString wallet = walletForHandle(handle, appId);
462 if (wallet.isEmpty()) {
463 return 1;
464 }
465 auto it = m_openWallets.begin();
466
467 while (it != m_openWallets.end()) {
468 if (it.key().first == handle && it.key().second == appId) {
469 if (force) {
470 it = m_openWallets.erase(it);
471 } else {
472 return 1;
473 }
474 } else {
475 ++it;
476 }
477 }
478
479 Q_EMIT walletClosed(handle);
480 Q_EMIT walletClosedId(handle);
481 if (m_openWallets.isEmpty()) {
482 Q_EMIT allWalletsClosed();
483 }
484 QSet<QString> newApps;
485 for (auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
486 newApps << it.key().second;
487 }
488 if (!newApps.contains(appId)) {
489 Q_EMIT applicationDisconnected(wallet, appId);
490 }
491
492 return 0;
493}
494
495void KWalletD::sync(int handle, const QString &appId)
496{
497 // STUB
498 Q_UNUSED(handle);
499 Q_UNUSED(appId);
500}
501
502int KWalletD::deleteWallet(const QString &wallet)
503{
504 bool ok;
505 m_backend->deleteCollection(wallet, &ok);
506
507 if (ok) {
508 m_structure.remove(wallet);
509 return 0;
510 }
511 return -1;
512}
513
514bool KWalletD::isOpen(const QString &wallet)
515{
516 for (const QString &w : std::as_const(m_openWallets)) {
517 if (w == wallet) {
518 return true;
519 }
520 }
521 return false;
522}
523
524bool KWalletD::isOpen(int handle)
525{
526 for (auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
527 if (it.key().first == handle) {
528 return true;
529 }
530 }
531 return false;
532}
533
534QStringList KWalletD::users(const QString &wallet) const
535{
536 QSet<QString> allApps;
537
538 for (auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
539 if (it.value() == wallet) {
540 allApps.insert(it.key().second);
541 }
542 }
543
544 return allApps.values();
545}
546
547void KWalletD::changePassword(const QString &wallet, qlonglong wId, const QString &appId)
548{
549 if (!m_backend->useKSecretBackend()) {
550 return;
551 }
552
553 QDBusInterface legacyKWalletInterface(QStringLiteral("org.kde.ksecretd"),
554 QStringLiteral("/ksecretd"),
555 QStringLiteral("org.kde.KWallet"),
557
558 if (!legacyKWalletInterface.isValid()) {
559 return;
560 }
561
562 legacyKWalletInterface.asyncCall(QStringLiteral("changePassword"), wallet, wId, appId);
563}
564
565QStringList KWalletD::wallets() const
566{
567 bool ok;
568 return m_backend->listCollections(&ok);
569}
570
571QStringList KWalletD::folderList(int handle, const QString &appId)
572{
573 const QString wallet = walletForHandle(handle, appId);
574 if (wallet.isEmpty()) {
575 return QStringList();
576 }
577
578 return m_structure.values(wallet);
579}
580
581bool KWalletD::hasFolder(int handle, const QString &folder, const QString &appId)
582{
583 const QString wallet = walletForHandle(handle, appId);
584 if (wallet.isEmpty()) {
585 return false;
586 }
587 for (auto it = m_structure.constBegin(); it != m_structure.constEnd(); it++) {
588 if (it.value().contains(folder)) {
589 return true;
590 }
591 }
592 return m_structure.contains(folder);
593}
594
595bool KWalletD::createFolder(int handle, const QString &folder, const QString &appId)
596{
597 const QString wallet = walletForHandle(handle, appId);
598 if (wallet.isEmpty()) {
599 return false;
600 }
601
602 // We can't save it on disk: if no key will be written,
603 // after daemon restart this folder will be gone
604 m_structure.insert(wallet, folder);
605 return true;
606}
607
608bool KWalletD::removeFolder(int handle, const QString &folder, const QString &appId)
609{
610 const QString wallet = walletForHandle(handle, appId);
611 if (wallet.isEmpty()) {
612 return false;
613 }
614
615 bool ok;
616 m_backend->deleteFolder(folder, wallet, &ok);
617 if (ok) {
618 m_structure.remove(wallet, folder);
619 }
620 return ok;
621}
622
623QStringList KWalletD::entryList(int handle, const QString &folder, const QString &appId)
624{
625 const QString wallet = walletForHandle(handle, appId);
626 if (wallet.isEmpty()) {
627 return QStringList();
628 }
629
630 bool ok;
631 return m_backend->listEntries(folder, wallet, &ok);
632}
633
634QByteArray KWalletD::readEntry(int handle, const QString &folder, const QString &key, const QString &appId)
635{
636 const QString wallet = walletForHandle(handle, appId);
637 if (wallet.isEmpty()) {
638 return QByteArray();
639 }
640
641 bool ok;
642 EntryType type = keyType(wallet, folder, key);
643
644 switch (type) {
645 case Stream:
646 return readBinary(key, folder, wallet, &ok);
647 case Map:
648 return readMap(handle, folder, key, appId);
649 default:
650 return readString(key, folder, wallet, &ok).toUtf8();
651 }
652}
653
654QByteArray KWalletD::readMap(int handle, const QString &folder, const QString &key, const QString &appId)
655{
656 const QString wallet = walletForHandle(handle, appId);
657 if (wallet.isEmpty()) {
658 return QByteArray();
659 }
660
661 bool ok;
662
663 QJsonObject obj = QJsonDocument::fromJson(readRawJson(key, folder, wallet, &ok)).object();
664 if (!ok) {
665 return QByteArray();
666 }
667
668 QMap<QString, QString> map;
669 for (auto it = obj.constBegin(); it != obj.constEnd(); it++) {
670 map[it.key()] = it.value().toString();
671 }
672
673 QByteArray data;
674 QDataStream ds(&data, QIODevice::WriteOnly);
675 ds << map;
676
677 return data;
678}
679
680QString KWalletD::readPassword(int handle, const QString &folder, const QString &key, const QString &appId)
681{
682 const QString wallet = walletForHandle(handle, appId);
683 if (wallet.isEmpty()) {
684 return QString();
685 }
686
687 bool ok;
688 return readString(key, folder, wallet, &ok);
689}
690
691QVariantMap KWalletD::readEntryList(int handle, const QString &folder, const QString &key, const QString &appId)
692{
693 QVariantMap map;
694 const QString wallet = walletForHandle(handle, appId);
695 if (wallet.isEmpty()) {
696 return map;
697 }
698
699 bool ok;
700
701 EntryType type = keyType(wallet, folder, key);
702 switch (type) {
703 case Stream:
704 map[key] = readBinary(key, folder, wallet, &ok);
705 break;
706 case Map:
707 map[key] = readMapList(handle, folder, key, appId);
708 break;
709 default:
710 map[key] = readString(key, folder, wallet, &ok).toUtf8();
711 }
712
713 return map;
714}
715
716QVariantMap KWalletD::entriesList(int handle, const QString &folder, const QString &appId)
717{
718 QVariantMap map;
719 const QString wallet = walletForHandle(handle, appId);
720 if (wallet.isEmpty()) {
721 return map;
722 }
723
724 bool ok;
725
726 const QStringList keys = m_backend->listEntries(folder, wallet, &ok);
727 if (!ok) {
728 return map;
729 }
730
731 for (const QString &key : std::as_const(keys)) {
732 EntryType type = keyType(wallet, folder, key);
733 switch (type) {
734 case Stream:
735 map[key] = readBinary(key, folder, wallet, &ok);
736 break;
737 case Map:
738 map[key] = readMap(handle, folder, key, appId);
739 break;
740 default:
741 map[key] = readString(key, folder, wallet, &ok).toUtf8();
742 }
743 if (!ok) {
744 return QVariantMap();
745 }
746 }
747
748 return map;
749}
750
751QVariantMap KWalletD::readMapList(int handle, const QString &folder, const QString &key, const QString &appId)
752{
753 QVariantMap map;
754 const QString wallet = walletForHandle(handle, appId);
755 if (wallet.isEmpty()) {
756 return map;
757 }
758
759 bool ok;
760
761 EntryType type = keyType(wallet, folder, key);
762 if (type != Map) {
763 return map;
764 }
765
766 QJsonObject obj = QJsonDocument::fromJson(readRawJson(key, folder, wallet, &ok)).object();
767 if (!ok) {
768 return map;
769 }
770
771 for (auto it = obj.constBegin(); it != obj.constEnd(); it++) {
772 map[it.key()] = it.value().toString();
773 }
774
775 return map;
776}
777
778QVariantMap KWalletD::mapList(int handle, const QString &folder, const QString &appId)
779{
780 QVariantMap map;
781 const QString wallet = walletForHandle(handle, appId);
782 if (wallet.isEmpty()) {
783 return map;
784 }
785
786 bool ok;
787
788 const QStringList keys = m_backend->listEntries(folder, wallet, &ok);
789 if (!ok) {
790 return map;
791 }
792
793 for (const QString &key : std::as_const(keys)) {
794 EntryType type = keyType(wallet, folder, key);
795 if (type == Map) {
796 map[key] = readMap(handle, folder, key, appId);
797 }
798 }
799
800 return map;
801}
802
803QVariantMap KWalletD::readPasswordList(int handle, const QString &folder, const QString &key, const QString &appId)
804{
805 QVariantMap map;
806 const QString wallet = walletForHandle(handle, appId);
807 if (wallet.isEmpty()) {
808 return map;
809 }
810
811 bool ok;
812
813 EntryType type = keyType(wallet, folder, key);
814 if (type == Password) {
815 map[key] = readString(key, folder, wallet, &ok);
816 if (!ok) {
817 return QVariantMap();
818 }
819 }
820
821 return map;
822}
823
824QVariantMap KWalletD::passwordList(int handle, const QString &folder, const QString &appId)
825{
826 QVariantMap map;
827 const QString wallet = walletForHandle(handle, appId);
828 if (wallet.isEmpty()) {
829 return map;
830 }
831
832 bool ok;
833
834 const QStringList keys = m_backend->listEntries(folder, wallet, &ok);
835 if (!ok) {
836 return map;
837 }
838
839 for (const QString &key : std::as_const(keys)) {
840 EntryType type = keyType(wallet, folder, key);
841 if (type == Password) {
842 map[key] = readString(key, folder, wallet, &ok);
843 if (!ok) {
844 return QVariantMap();
845 }
846 }
847 }
848
849 return map;
850}
851
852int KWalletD::renameEntry(int handle, const QString &folder, const QString &oldName, const QString &newName, const QString &appId)
853{
854 const QString wallet = walletForHandle(handle, appId);
855 if (wallet.isEmpty()) {
856 return -1;
857 }
858
859 bool ok;
860 m_backend->renameEntry(folderPath(folder, newName), oldName, newName, folder, wallet, &ok);
861
862 if (!ok) {
863 return -1;
864 }
865
866 return 0;
867}
868
869int KWalletD::writeEntry(int handle, const QString &folder, const QString &key, const QByteArray &value, int entryType, const QString &appId)
870{
871 const QString wallet = walletForHandle(handle, appId);
872
873 if (wallet.isEmpty()) {
874 return -1;
875 }
876
877 bool ok;
878 QStringList oldFolders = m_backend->listFolders(wallet, &ok);
879 QStringList oldEntries = m_backend->listEntries(wallet, folder, &ok);
880
881 switch (entryType) {
882 case Password:
883 writeString(key, QString::fromUtf8(value), folder, wallet, &ok);
884 break;
885 case Stream:
886 writeBinary(key, value, folder, wallet, &ok);
887 break;
888 case Map: {
889 QMap<QString, QString> map;
890 QByteArray data;
891 QDataStream ds(value);
892 ds >> map;
893
894 QJsonObject obj;
895 for (auto it = map.constBegin(); it != map.constEnd(); it++) {
896 obj.insert(it.key(), it.value());
897 }
898
899 writeRawJson(key, QJsonDocument(obj).toJson(QJsonDocument::Compact), folder, wallet, &ok);
900 break;
901 }
902 default:
903 return -1;
904 }
905
906 if (!ok) {
907 return -1;
908 }
909
910 if (!oldFolders.contains(folder)) {
911 m_structure.insert(wallet, folder);
912 Q_EMIT folderListUpdated(wallet);
913 }
914 if (!oldEntries.contains(key)) {
915 Q_EMIT folderUpdated(wallet, folder);
916 } else {
917 Q_EMIT entryUpdated(wallet, folder, key);
918 }
919 return 0;
920}
921
922int KWalletD::writeEntry(int handle, const QString &folder, const QString &key, const QByteArray &value, const QString &appId)
923{
924 return writeEntry(handle, folder, key, value, Stream, appId);
925}
926
927int KWalletD::writeMap(int handle, const QString &folder, const QString &key, const QByteArray &value, const QString &appId)
928{
929 return writeEntry(handle, folder, key, value, Map, appId);
930}
931
932int KWalletD::writePassword(int handle, const QString &folder, const QString &key, const QString &value, const QString &appId)
933{
934 return writeEntry(handle, folder, key, value.toUtf8(), Password, appId);
935}
936
937bool KWalletD::hasEntry(int handle, const QString &folder, const QString &key, const QString &appId)
938{
939 const QString wallet = walletForHandle(handle, appId);
940 if (wallet.isEmpty()) {
941 return false;
942 }
943
944 bool ok;
945 const QStringList entries = m_backend->listEntries(folder, wallet, &ok);
946
947 if (!ok) {
948 return false;
949 }
950
951 return entries.contains(key);
952}
953
954int KWalletD::entryType(int handle, const QString &folder, const QString &key, const QString &appId)
955{
956 const QString wallet = walletForHandle(handle, appId);
957 if (wallet.isEmpty()) {
958 return Unknown;
959 }
960
961 return keyType(wallet, folder, key);
962}
963
964int KWalletD::removeEntry(int handle, const QString &folder, const QString &key, const QString &appId)
965{
966 const QString wallet = walletForHandle(handle, appId);
967 if (wallet.isEmpty()) {
968 return -1;
969 }
970
971 bool ok;
972 removeItem(key, folder, wallet, &ok);
973 if (ok) {
974 QStringList folders = m_backend->listFolders(wallet, &ok);
975 if (ok && !folders.contains(folder)) {
976 m_structure.remove(wallet, folder);
977 folderListUpdated(wallet);
978 }
979
980 Q_EMIT entryDeleted(wallet, folder, key);
981 return 0;
982 }
983
984 return -1;
985}
986
987bool KWalletD::disconnectApplication(const QString &wallet, const QString &application)
988{
989 bool found = false;
990 auto it = m_openWallets.begin();
991
992 while (it != m_openWallets.end()) {
993 if (it.key().second == application && it.value() == wallet) {
994 found = true;
995 it = m_openWallets.erase(it);
996 } else {
997 ++it;
998 }
999 }
1000
1001 if (found) {
1002 Q_EMIT walletClosed(wallet);
1003 if (m_openWallets.isEmpty()) {
1004 Q_EMIT allWalletsClosed();
1005 }
1006
1007 Q_EMIT applicationDisconnected(wallet, application);
1008 }
1009
1010 return false;
1011}
1012
1013void KWalletD::reconfigure()
1014{
1015 KConfig cfg(QStringLiteral("kwalletrc"));
1016 KConfigGroup walletGroup(&cfg, QStringLiteral("Wallet"));
1017 const bool wasEnabled = m_enabled;
1018 m_enabled = walletGroup.readEntry("Enabled", true);
1019 m_launchManager = walletGroup.readEntry("Launch Manager", false);
1020 const bool closeIdle = m_closeIdle;
1021 m_closeIdle = walletGroup.readEntry("Close When Idle", false);
1022 const int idleTime = m_idleTime;
1023 // in minutes!
1024 m_idleTime = walletGroup.readEntry("Idle Timeout", 10) * 60 * 1000;
1025
1026 if (wasEnabled != m_enabled) {
1027 if (isEnabled()) {
1028 Q_EMIT walletListDirty();
1029 } else {
1030 closeAllWallets();
1031 }
1032 }
1033
1034 if (closeIdle != m_closeIdle) {
1035 if (m_closeIdle) {
1036 for (auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
1037 m_idleTimers[it.key()] = startTimer(m_idleTime);
1038 }
1039 } else {
1040 for (auto it = m_idleTimers.constBegin(); it != m_idleTimers.constEnd(); it++) {
1041 killTimer(it.value());
1042 }
1043 m_idleTimers.clear();
1044 }
1045 } else if (idleTime != m_idleTime) {
1046 for (auto it = m_idleTimers.begin(); it != m_idleTimers.constEnd(); it++) {
1047 killTimer(it.value());
1048 *it = (startTimer(m_idleTime));
1049 }
1050 }
1051
1052 if (m_launchManager) {
1053 startManagerForKwalletd();
1054 }
1055}
1056
1057bool KWalletD::folderDoesNotExist(const QString &wallet, const QString &folder)
1058{
1059 bool ok;
1060 const QStringList entries = m_backend->listFolders(wallet, &ok);
1061
1062 if (!ok) {
1063 return true;
1064 }
1065
1066 return !entries.contains(folder);
1067}
1068
1069bool KWalletD::keyDoesNotExist(const QString &wallet, const QString &folder, const QString &key)
1070{
1071 bool ok;
1072 const QStringList entries = m_backend->listEntries(folder, wallet, &ok);
1073
1074 if (!ok) {
1075 return true;
1076 }
1077
1078 return !entries.contains(key);
1079}
1080
1081void KWalletD::closeAllWallets()
1082{
1083 QSet<QString> openWallets;
1084 QSet<QPair<QString, QString>> apps;
1085
1086 for (auto it = m_openWallets.constBegin(); it != m_openWallets.constEnd(); it++) {
1087 openWallets.insert(it.value());
1088 apps.insert({it.value(), it.key().second});
1089 }
1090
1091 m_openWallets.clear();
1092 for (const QPair<QString, QString> &connection : apps.values()) {
1093 Q_EMIT applicationDisconnected(connection.first, connection.second);
1094 }
1095 for (const QString &wallet : openWallets.values()) {
1096 Q_EMIT walletClosed(wallet);
1097 }
1098 Q_EMIT allWalletsClosed();
1099}
1100
1101QString KWalletD::networkWallet()
1102{
1103 bool ok;
1104 const QString defaultWallet = m_backend->defaultCollection(&ok);
1105
1106 KConfig cfg(QStringLiteral("kwalletrc"));
1107 KConfigGroup walletGroup(&cfg, QStringLiteral("Wallet"));
1108 return walletGroup.readEntry(QStringLiteral("Default Wallet"), defaultWallet);
1109}
1110
1111QString KWalletD::localWallet()
1112{
1113 KConfig cfg(QStringLiteral("kwalletrc"));
1114 KConfigGroup walletGroup(&cfg, QStringLiteral("Wallet"));
1115 return walletGroup.readEntry(QStringLiteral("Local Wallet"), networkWallet());
1116}
1117
1118int KWalletD::pamOpen(const QString &wallet, const QByteArray &passwordHash, int sessionTimeout)
1119{
1120 if (!m_backend->useKSecretBackend()) {
1121 return -1;
1122 }
1123
1124 QDBusInterface secretdInterface(QStringLiteral("org.kde.ksecretd"),
1125 QStringLiteral("/ksecretd"),
1126 QStringLiteral("org.kde.KWallet"),
1128
1129 secretdInterface.call(QStringLiteral("pamOpen"), wallet, passwordHash, sessionTimeout);
1130
1131 // Return value is not much important as is not exposed on dbus
1132 return 0;
1133}
1134
1135#include "moc_kwalletd.cpp"
static Ptr create(const KSharedConfig::Ptr &config)
void configChanged(const KConfigGroup &group, const QByteArrayList &names)
QString password() const
void setPrompt(const QString &prompt)
void setRevealPasswordMode(KPassword::RevealMode revealPasswordMode)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
bool isEmpty() const const
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
bool registerService(const QString &serviceName)
QDBusConnection sessionBus()
QDBusConnection connection() const const
virtual int exec()
T value(const Key &key) const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator insert(QLatin1StringView key, const QJsonValue &value)
QJsonValue value(QLatin1StringView key) const const
const_iterator constBegin() const const
const_iterator constEnd() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
Q_EMITQ_EMIT
void killTimer(int id)
int startTimer(int interval, Qt::TimerType timerType)
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
quint32 generate()
QRandomGenerator securelySeeded()
void clear()
bool contains(const QSet< T > &other) const const
iterator erase(const_iterator pos)
iterator insert(const T &value)
QSet< T > & subtract(const QSet< T > &other)
QList< T > values() const const
QString findExecutable(const QString &executableName, const QStringList &paths)
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
qsizetype length() const const
QByteArray toUtf8() const const
void truncate(qsizetype position)
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
int timerId() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 11:53:00 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.