diff --git a/src/api/KWallet/kwallet.cpp b/src/api/KWallet/kwallet.cpp index 15be0eb..5992356 100644 --- a/src/api/KWallet/kwallet.cpp +++ b/src/api/KWallet/kwallet.cpp @@ -1,1670 +1,1670 @@ /* This file is part of the KDE project * * Copyright (C) 2002-2004 George Staikos * Copyright (C) 2008 Michael Leupold * Copyright (C) 2011 Valentin Rusu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kwallet.h" #include "config-kwallet.h" #include #include #include #include #include #include #include #include #if HAVE_KSECRETSSERVICE #include "ksecretsservice/ksecretsservicecollection.h" #endif #include "kwallet_interface.h" #if HAVE_KSECRETSSERVICE typedef QMap StringToStringStringMapMap; Q_DECLARE_METATYPE(StringToStringStringMapMap) #endif typedef QMap StringByteArrayMap; Q_DECLARE_METATYPE(StringByteArrayMap) namespace KWallet { class KWalletDLauncher { public: KWalletDLauncher(); ~KWalletDLauncher(); org::kde::KWallet &getInterface(); // this static variable is used below to switch between old KWallet // infrastructure and the new one which is built on top of the new // KSecretsService infrastructure. It's value can be changed via the // the Wallet configuration module in System Settings bool m_useKSecretsService; org::kde::KWallet *m_wallet_deamon; KConfigGroup m_cgroup; bool m_walletEnabled; }; Q_GLOBAL_STATIC(KWalletDLauncher, walletLauncher) static QString appid() { return qApp->applicationName(); } static void registerTypes() { static bool registered = false; if (!registered) { #if HAVE_KSECRETSSERVICE qDBusRegisterMetaType(); qDBusRegisterMetaType(); #endif qDBusRegisterMetaType(); registered = true; } } bool Wallet::isUsingKSecretsService() { return walletLauncher()->m_useKSecretsService; } const QString Wallet::LocalWallet() { // NOTE: This method stays unchanged for KSecretsService KConfigGroup cfg(KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Wallet")); if (!cfg.readEntry("Use One Wallet", true)) { QString tmp = cfg.readEntry("Local Wallet", "localwallet"); if (tmp.isEmpty()) { return QStringLiteral("localwallet"); } return tmp; } QString tmp = cfg.readEntry("Default Wallet", "kdewallet"); if (tmp.isEmpty()) { return QStringLiteral("kdewallet"); } return tmp; } const QString Wallet::NetworkWallet() { // NOTE: This method stays unchanged for KSecretsService KConfigGroup cfg(KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Wallet")); QString tmp = cfg.readEntry("Default Wallet", "kdewallet"); if (tmp.isEmpty()) { return QStringLiteral("kdewallet"); } return tmp; } const QString Wallet::PasswordFolder() { return QStringLiteral("Passwords"); } const QString Wallet::FormDataFolder() { return QStringLiteral("Form Data"); } class Wallet::WalletPrivate { public: WalletPrivate(Wallet *wallet, int h, const QString &n) : q(wallet), name(n), handle(h) #if HAVE_KSECRETSSERVICE , secretsCollection(0) #endif {} void walletServiceUnregistered(); #if HAVE_KSECRETSSERVICE template int writeEntry(const QString &key, const T &value, Wallet::EntryType entryType) { int rc = -1; KSecretsService::Secret secret; secret.setValue(QVariant::fromValue(value)); KSecretsService::StringStringMap attrs; attrs[KSS_ATTR_ENTRYFOLDER] = folder; attrs[KSS_ATTR_WALLETTYPE] = QString("%1").arg((int)entryType); KSecretsService::CreateCollectionItemJob *createItemJob = secretsCollection->createItem(key, attrs, secret); if (!createItemJob->exec()) { qDebug() << "Cannot execute CreateCollectionItemJob : " << createItemJob->errorString(); } rc = createItemJob->error(); return rc; } QExplicitlySharedDataPointer findItem(const QString &key) const; template int readEntry(const QString &key, T &value) const; bool readSecret(const QString &key, KSecretsService::Secret &value) const; template int forEachItemThatMatches(const QString &key, V verb) { int rc = -1; KSecretsService::StringStringMap attrs; attrs[KSS_ATTR_ENTRYFOLDER] = folder; KSecretsService::SearchCollectionItemsJob *searchItemsJob = secretsCollection->searchItems(attrs); if (searchItemsJob->exec()) { QRegExp re(key, Qt::CaseSensitive, QRegExp::Wildcard); foreach (KSecretsService::SearchCollectionItemsJob::Item item, searchItemsJob->items()) { KSecretsService::ReadItemPropertyJob *readLabelJob = item->label(); if (readLabelJob->exec()) { QString label = readLabelJob->propertyValue().toString(); if (re.exactMatch(label)) { if (verb(this, label, item.data())) { rc = 0; // one successful iteration already produced results, so success return } } } else { qDebug() << "Cannot execute ReadItemPropertyJob " << readLabelJob->errorString(); } } } else { qDebug() << "Cannot execute KSecretsService::SearchCollectionItemsJob " << searchItemsJob->errorString(); } return rc; } void createDefaultFolders(); struct InsertIntoEntryList; struct InsertIntoMapList; struct InsertIntoPasswordList; KSecretsService::Collection *secretsCollection; #endif // HAVE_KSECRETSSERVICE Wallet *q; QString name; QString folder; int handle; int transactionId; }; #if HAVE_KSECRETSSERVICE void Wallet::WalletPrivate::createDefaultFolders() { // NOTE: KWalletManager expects newly created wallets to have two default folders // b->createFolder(KWallet::Wallet::PasswordFolder()); // b->createFolder(KWallet::Wallet::FormDataFolder()); QString strDummy(""); folder = PasswordFolder(); writeEntry(PasswordFolder(), strDummy, KWallet::Wallet::Unknown); folder = FormDataFolder(); writeEntry(FormDataFolder(), strDummy, KWallet::Wallet::Unknown); } #endif // HAVE_KSECRETSSERVICE static const char s_kwalletdServiceName[] = "org.kde.kwalletd5"; Wallet::Wallet(int handle, const QString &name) - : QObject(0L), d(new WalletPrivate(this, handle, name)) + : QObject(nullptr), d(new WalletPrivate(this, handle, name)) { if (walletLauncher()->m_useKSecretsService) { // see openWallet for initialization code; this constructor does not have any code } else { QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QString::fromLatin1(s_kwalletdServiceName), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration, this); connect(watcher, SIGNAL(serviceUnregistered(QString)), this, SLOT(walletServiceUnregistered())); connect(&walletLauncher()->getInterface(), SIGNAL(walletClosed(int)), SLOT(slotWalletClosed(int))); connect(&walletLauncher()->getInterface(), SIGNAL(folderListUpdated(QString)), SLOT(slotFolderListUpdated(QString))); connect(&walletLauncher()->getInterface(), SIGNAL(folderUpdated(QString,QString)), SLOT(slotFolderUpdated(QString,QString))); connect(&walletLauncher()->getInterface(), SIGNAL(applicationDisconnected(QString,QString)), SLOT(slotApplicationDisconnected(QString,QString))); // Verify that the wallet is still open if (d->handle != -1) { QDBusReply r = walletLauncher()->getInterface().isOpen(d->handle); if (r.isValid() && !r) { d->handle = -1; d->name.clear(); } } } } Wallet::~Wallet() { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { d->folder.clear(); d->name.clear(); delete d->secretsCollection; } else { #endif if (d->handle != -1) { if (!walletLauncher.isDestroyed()) { walletLauncher()->getInterface().close(d->handle, false, appid()); } else { qDebug() << "Problem with static destruction sequence." "Destroy any static Wallet before the event-loop exits."; } d->handle = -1; d->folder.clear(); d->name.clear(); } #if HAVE_KSECRETSSERVICE } #endif delete d; } QStringList Wallet::walletList() { QStringList result; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { KSecretsService::ListCollectionsJob *listJob = KSecretsService::Collection::listCollections(); if (listJob->exec()) { result = listJob->collections(); } else { qDebug() << "Cannot execute ListCollectionsJob: " << listJob->errorString(); } } else { #endif if (walletLauncher()->m_walletEnabled) { QDBusReply r = walletLauncher()->getInterface().wallets(); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); } else { result = r; } } #if HAVE_KSECRETSSERVICE } #endif return result; } void Wallet::changePassword(const QString &name, WId w) { if (w == 0) { qDebug() << "Pass a valid window to KWallet::Wallet::changePassword()."; } // Make sure the password prompt window will be visible and activated KWindowSystem::allowExternalProcessWindowActivation(); #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { KSecretsService::Collection *coll = KSecretsService::Collection::findCollection(name); KSecretsService::ChangeCollectionPasswordJob *changePwdJob = coll->changePassword(); if (!changePwdJob->exec()) { qDebug() << "Cannot execute change password job: " << changePwdJob->errorString(); } coll->deleteLater(); } else { #endif if (walletLauncher()->m_walletEnabled) { walletLauncher()->getInterface().changePassword(name, (qlonglong)w, appid()); } #if HAVE_KSECRETSSERVICE } #endif } bool Wallet::isEnabled() { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { return walletLauncher()->m_cgroup.readEntry("Enabled", true); } else { #endif return walletLauncher()->m_walletEnabled; #if HAVE_KSECRETSSERVICE } #endif } bool Wallet::isOpen(const QString &name) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { KSecretsService::Collection *coll = KSecretsService::Collection::findCollection(name, KSecretsService::Collection::OpenOnly); KSecretsService::ReadCollectionPropertyJob *readLocked = coll->isLocked(); if (readLocked->exec()) { return !readLocked->propertyValue().toBool(); } else { qDebug() << "ReadLocked job failed"; return false; } } else { #endif if (walletLauncher()->m_walletEnabled) { QDBusReply r = walletLauncher()->getInterface().isOpen(name); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return false; } else { return r; } } else return false; #if HAVE_KSECRETSSERVICE } #endif } int Wallet::closeWallet(const QString &name, bool force) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { qDebug() << "Wallet::closeWallet NOOP"; return 0; } else { #endif if (walletLauncher()->m_walletEnabled) { QDBusReply r = walletLauncher()->getInterface().close(name, force); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return -1; } else { return r; } } else return -1; #if HAVE_KSECRETSSERVICE } #endif } int Wallet::deleteWallet(const QString &name) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { KSecretsService::Collection *coll = KSecretsService::Collection::findCollection(name, KSecretsService::Collection::OpenOnly); KJob *deleteJob = coll->deleteCollection(); if (!deleteJob->exec()) { qDebug() << "Cannot execute delete job " << deleteJob->errorString(); } return deleteJob->error(); } else { #endif if (walletLauncher->m_walletEnabled) { QDBusReply r = walletLauncher()->getInterface().deleteWallet(name); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return -1; } else { return r; } } else return -1; #if HAVE_KSECRETSSERVICE } #endif } Wallet *Wallet::openWallet(const QString &name, WId w, OpenType ot) { if (w == 0) { qDebug() << "Pass a valid window to KWallet::Wallet::openWallet()."; } if (!walletLauncher()->m_walletEnabled) { qDebug() << "User disabled the wallet system so returning 0 here."; - return 0; + return nullptr; } #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { Wallet *wallet = new Wallet(-1, name); // FIXME: should we specify CreateCollection or OpenOnly here? wallet->d->secretsCollection = KSecretsService::Collection::findCollection(name, KSecretsService::Collection::CreateCollection, QVariantMap(), w); connect(wallet->d->secretsCollection, SIGNAL(statusChanged(int)), wallet, SLOT(slotCollectionStatusChanged(int))); connect(wallet->d->secretsCollection, SIGNAL(deleted()), wallet, SLOT(slotCollectionDeleted())); if (ot == Synchronous) { qDebug() << "WARNING openWallet OpenType=Synchronous requested"; // TODO: not sure what to do with in this case; however, all other KSecretsService API methods are already // async and will perform sync inside this API because of it's design } return wallet; } else { #endif Wallet *wallet = new Wallet(-1, name); // connect the daemon's opened signal to the slot filtering the // signals we need connect(&walletLauncher()->getInterface(), SIGNAL(walletAsyncOpened(int,int)), wallet, SLOT(walletAsyncOpened(int,int))); // Make sure the password prompt window will be visible and activated KWindowSystem::allowExternalProcessWindowActivation(); org::kde::KWallet &interface = walletLauncher->getInterface(); // do the call QDBusReply r; if (ot == Synchronous) { interface.setTimeout(0x7FFFFFFF); // Don't timeout after 25s, but 24 days r = interface.open(name, (qlonglong)w, appid()); interface.setTimeout(-1); // Back to the default 25s // after this call, r would contain a transaction id >0 if OK or -1 if NOK // if OK, the slot walletAsyncOpened should have been received, but the transaction id // will not match. We'll get that handle from the reply - see below } else if (ot == Asynchronous) { r = interface.openAsync(name, (qlonglong)w, appid(), true); } else if (ot == Path) { r = interface.openPathAsync(name, (qlonglong)w, appid(), true); } else { delete wallet; - return 0; + return nullptr; } // error communicating with the daemon (maybe not running) if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); delete wallet; - return 0; + return nullptr; } wallet->d->transactionId = r.value(); if (ot == Synchronous || ot == Path) { // check for an immediate error if (wallet->d->transactionId < 0) { delete wallet; - wallet = 0; + wallet = nullptr; } else { wallet->d->handle = r.value(); } } else if (ot == Asynchronous) { if (wallet->d->transactionId < 0) { QTimer::singleShot(0, wallet, SLOT(emitWalletAsyncOpenError())); // client code is responsible for deleting the wallet } } return wallet; #if HAVE_KSECRETSSERVICE } #endif } void Wallet::slotCollectionStatusChanged(int status) { #if HAVE_KSECRETSSERVICE KSecretsService::Collection::Status collStatus = (KSecretsService::Collection::Status)status; switch (collStatus) { case KSecretsService::Collection::NewlyCreated: d->createDefaultFolders(); // fall through case KSecretsService::Collection::FoundExisting: emitWalletOpened(); break; case KSecretsService::Collection::Deleted: case KSecretsService::Collection::Invalid: case KSecretsService::Collection::Pending: // nothing to do break; case KSecretsService::Collection::NotFound: emitWalletAsyncOpenError(); break; } #else Q_UNUSED(status) #endif } void Wallet::slotCollectionDeleted() { d->folder.clear(); d->name.clear(); emit walletClosed(); } bool Wallet::disconnectApplication(const QString &wallet, const QString &app) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { qDebug() << "Wallet::disconnectApplication NOOP"; return true; } else { #endif if (walletLauncher()->m_walletEnabled) { QDBusReply r = walletLauncher()->getInterface().disconnectApplication(wallet, app); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return false; } else { return r; } } else return -1; #if HAVE_KSECRETSSERVICE } #endif } QStringList Wallet::users(const QString &name) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { qDebug() << "KSecretsService does not handle users list"; return QStringList(); } else { #endif if (walletLauncher()->m_walletEnabled) { QDBusReply r = walletLauncher()->getInterface().users(name); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return QStringList(); } else { return r; } } else return QStringList(); #if HAVE_KSECRETSSERVICE } #endif } int Wallet::sync() { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { // NOOP with KSecretsService } else { #endif if (d->handle == -1) { return -1; } walletLauncher()->getInterface().sync(d->handle, appid()); #if HAVE_KSECRETSSERVICE } #endif return 0; } int Wallet::lockWallet() { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { KSecretsService::CollectionLockJob *lockJob = d->secretsCollection->lock(); if (lockJob->exec()) { d->folder.clear(); d->name.clear(); } else { qDebug() << "Cannot execute KSecretsService::CollectionLockJob : " << lockJob->errorString(); return -1; } return lockJob->error(); } else { #endif if (d->handle == -1) { return -1; } QDBusReply r = walletLauncher()->getInterface().close(d->handle, true, appid()); d->handle = -1; d->folder.clear(); d->name.clear(); if (r.isValid()) { return r; } else { qDebug() << "Invalid DBus reply: " << r.error(); return -1; } #if HAVE_KSECRETSSERVICE } #endif } const QString &Wallet::walletName() const { return d->name; } bool Wallet::isOpen() const { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { return !d->secretsCollection->isLocked(); } else { #endif return d->handle != -1; #if HAVE_KSECRETSSERVICE } #endif } void Wallet::requestChangePassword(WId w) { if (w == 0) { qDebug() << "Pass a valid window to KWallet::Wallet::requestChangePassword()."; } #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { KSecretsService::ChangeCollectionPasswordJob *changePwdJob = d->secretsCollection->changePassword(); if (!changePwdJob->exec()) { qDebug() << "Cannot execute ChangeCollectionPasswordJob : " << changePwdJob->errorString(); } } else { #endif if (d->handle == -1) { return; } // Make sure the password prompt window will be visible and activated KWindowSystem::allowExternalProcessWindowActivation(); walletLauncher()->getInterface().changePassword(d->name, (qlonglong)w, appid()); #if HAVE_KSECRETSSERVICE } #endif } void Wallet::slotWalletClosed(int handle) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { // TODO: implement this Q_ASSERT(0); } else { #endif if (d->handle == handle) { d->handle = -1; d->folder.clear(); d->name.clear(); emit walletClosed(); } #if HAVE_KSECRETSSERVICE } #endif } QStringList Wallet::folderList() { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { QStringList result; KSecretsService::StringStringMap attrs; attrs[KSS_ATTR_ENTRYFOLDER] = ""; // search for items having this attribute no matter what value it has KSecretsService::SearchCollectionItemsJob *searchJob = d->secretsCollection->searchItems(attrs); if (searchJob->exec()) { KSecretsService::ReadCollectionItemsJob::ItemList itemList = searchJob->items(); foreach (const KSecretsService::ReadCollectionItemsJob::Item &item, itemList) { KSecretsService::ReadItemPropertyJob *readAttrsJob = item->attributes(); if (readAttrsJob->exec()) { KSecretsService::StringStringMap attrs = readAttrsJob->propertyValue().value(); const QString folder = attrs[KSS_ATTR_ENTRYFOLDER]; if (!folder.isEmpty() && !result.contains(folder)) { result.append(folder); } } else { qDebug() << "Cannot read item attributes : " << readAttrsJob->errorString(); } } } else { qDebug() << "Cannot execute ReadCollectionItemsJob : " << searchJob->errorString(); } return result; } else { #endif if (d->handle == -1) { return QStringList(); } QDBusReply r = walletLauncher()->getInterface().folderList(d->handle, appid()); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return QStringList(); } else { return r; } #if HAVE_KSECRETSSERVICE } #endif } QStringList Wallet::entryList() { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { QStringList result; KSecretsService::StringStringMap attrs; attrs[KSS_ATTR_ENTRYFOLDER] = d->folder; KSecretsService::SearchCollectionItemsJob *readItemsJob = d->secretsCollection->searchItems(attrs); if (readItemsJob->exec()) { foreach (KSecretsService::SearchCollectionItemsJob::Item item, readItemsJob->items()) { KSecretsService::ReadItemPropertyJob *readLabelJob = item->label(); if (readLabelJob->exec()) { result.append(readLabelJob->propertyValue().toString()); } else { qDebug() << "Cannot execute readLabelJob" << readItemsJob->errorString(); } } } else { qDebug() << "Cannot execute readItemsJob" << readItemsJob->errorString(); } return result; } else { #endif if (d->handle == -1) { return QStringList(); } QDBusReply r = walletLauncher()->getInterface().entryList(d->handle, d->folder, appid()); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return QStringList(); } else { return r; } #if HAVE_KSECRETSSERVICE } #endif } bool Wallet::hasFolder(const QString &f) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { // FIXME: well, this is not the best implementation, but it's done quickly :) // the best way would be to searchItems with the attribute label having the value f // doing that would reduce DBus traffic. But KWallet API wille not last. QStringList folders = folderList(); return folders.contains(f); } else { #endif if (d->handle == -1) { return false; } QDBusReply r = walletLauncher()->getInterface().hasFolder(d->handle, f, appid()); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return false; } else { return r; } #if HAVE_KSECRETSSERVICE } #endif } bool Wallet::createFolder(const QString &f) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { QString strDummy(""); d->folder = f; d->writeEntry(f, strDummy, KWallet::Wallet::Unknown); return true; } else { #endif if (d->handle == -1) { return false; } if (!hasFolder(f)) { QDBusReply r = walletLauncher()->getInterface().createFolder(d->handle, f, appid()); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return false; } else { return r; } } return true; // folder already exists #if HAVE_KSECRETSSERVICE } #endif } bool Wallet::setFolder(const QString &f) { bool rc = false; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { if (hasFolder(f)) { d->folder = f; rc = true; } } else { #endif if (d->handle == -1) { return rc; } // Don't do this - the folder could have disappeared? #if 0 if (f == d->folder) { return true; } #endif if (hasFolder(f)) { d->folder = f; rc = true; } #if HAVE_KSECRETSSERVICE } #endif return rc; } bool Wallet::removeFolder(const QString &f) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { bool result = false; // search for all items having the folder f then delete them KSecretsService::StringStringMap attrs; attrs[KSS_ATTR_ENTRYFOLDER] = f; KSecretsService::SearchCollectionItemsJob *searchJob = d->secretsCollection->searchItems(attrs); if (searchJob->exec()) { KSecretsService::SearchCollectionItemsJob::ItemList itemList = searchJob->items(); if (!itemList.isEmpty()) { result = true; foreach (const KSecretsService::SearchCollectionItemsJob::Item &item, itemList) { KSecretsService::SecretItemDeleteJob *deleteJob = item->deleteItem(); if (!deleteJob->exec()) { qDebug() << "Cannot delete item : " << deleteJob->errorString(); result = false; } result &= true; } } } else { qDebug() << "Cannot execute KSecretsService::SearchCollectionItemsJob : " << searchJob->errorString(); } return result; } else { #endif if (d->handle == -1) { return false; } QDBusReply r = walletLauncher()->getInterface().removeFolder(d->handle, f, appid()); if (d->folder == f) { setFolder(QString()); } if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return false; } else { return r; } #if HAVE_KSECRETSSERVICE } #endif } const QString &Wallet::currentFolder() const { return d->folder; } #if HAVE_KSECRETSSERVICE QExplicitlySharedDataPointer Wallet::WalletPrivate::findItem(const QString &key) const { QExplicitlySharedDataPointer result; KSecretsService::StringStringMap attrs; attrs[KSS_ATTR_ENTRYFOLDER] = folder; attrs["Label"] = key; KSecretsService::SearchCollectionItemsJob *searchJob = secretsCollection->searchItems(attrs); if (searchJob->exec()) { KSecretsService::SearchCollectionItemsJob::ItemList itemList = searchJob->items(); if (!itemList.isEmpty()) { result = itemList.first(); } else { qDebug() << "entry named " << key << " not found in folder " << folder; } } else { qDebug() << "Cannot exec KSecretsService::SearchCollectionItemsJob : " << searchJob->errorString(); } return result; } template int Wallet::WalletPrivate::readEntry(const QString &key, T &value) const { int rc = -1; QExplicitlySharedDataPointer item = findItem(key); if (item) { KSecretsService::GetSecretItemSecretJob *readJob = item->getSecret(); if (readJob->exec()) { KSecretsService::Secret theSecret = readJob->secret(); qDebug() << "Secret contentType is " << theSecret.contentType(); value = theSecret.value().value(); rc = 0; } else { qDebug() << "Cannot exec GetSecretItemSecretJob : " << readJob->errorString(); } } return rc; } bool Wallet::WalletPrivate::readSecret(const QString &key, KSecretsService::Secret &value) const { bool result = false; QExplicitlySharedDataPointer item = findItem(key); if (item) { KSecretsService::GetSecretItemSecretJob *readJob = item->getSecret(); if (readJob->exec()) { value = readJob->secret(); result = true; } else { qDebug() << "Cannot exec GetSecretItemSecretJob : " << readJob->errorString(); } } return result; } #endif int Wallet::readEntry(const QString &key, QByteArray &value) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { return d->readEntry(key, value); } else { #endif if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().readEntry(d->handle, d->folder, key, appid()); if (r.isValid()) { value = r; rc = 0; } #if HAVE_KSECRETSSERVICE } #endif return rc; } #if HAVE_KSECRETSSERVICE struct Wallet::WalletPrivate::InsertIntoEntryList { InsertIntoEntryList(QMap< QString, QByteArray> &value) : _value(value) {} bool operator()(Wallet::WalletPrivate *, const QString &label, KSecretsService::SecretItem *item) { bool result = false; KSecretsService::GetSecretItemSecretJob *readSecretJob = item->getSecret(); if (readSecretJob->exec()) { _value.insert(label, readSecretJob->secret().value().toByteArray()); result = true; } else { qDebug() << "Cannot execute GetSecretItemSecretJob " << readSecretJob->errorString(); } return result; } QMap< QString, QByteArray > _value; }; #endif int Wallet::readEntryList(const QString &key, QMap &value) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { rc = d->forEachItemThatMatches(key, WalletPrivate::InsertIntoEntryList(value)); } else { #endif registerTypes(); if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().readEntryList(d->handle, d->folder, key, appid()); if (r.isValid()) { rc = 0; // convert to const QVariantMap val = r.value(); for (QVariantMap::const_iterator it = val.begin(); it != val.end(); ++it) { value.insert(it.key(), it.value().toByteArray()); } } #if HAVE_KSECRETSSERVICE } #endif return rc; } int Wallet::renameEntry(const QString &oldName, const QString &newName) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { QExplicitlySharedDataPointer item = d->findItem(oldName); if (item) { KSecretsService::WriteItemPropertyJob *writeJob = item->setLabel(newName); if (!writeJob->exec()) { qDebug() << "Cannot exec WriteItemPropertyJob : " << writeJob->errorString(); } rc = writeJob->error(); } else { qDebug() << "Cannot locate item " << oldName << " in folder " << d->folder; } } else { #endif if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().renameEntry(d->handle, d->folder, oldName, newName, appid()); if (r.isValid()) { rc = r; } #if HAVE_KSECRETSSERVICE } #endif return rc; } int Wallet::readMap(const QString &key, QMap &value) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { QByteArray ba; rc = d->readEntry< QByteArray >(key, ba); if (rc == 0 && !ba.isEmpty()) { QDataStream ds(&ba, QIODevice::ReadOnly); ds >> value; } } else { #endif registerTypes(); if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().readMap(d->handle, d->folder, key, appid()); if (r.isValid()) { rc = 0; QByteArray v = r; if (!v.isEmpty()) { QDataStream ds(&v, QIODevice::ReadOnly); ds >> value; } } #if HAVE_KSECRETSSERVICE } #endif return rc; } #if HAVE_KSECRETSSERVICE struct Wallet::WalletPrivate::InsertIntoMapList { InsertIntoMapList(QMap< QString, QMap< QString, QString > > &value) : _value(value) {} bool operator()(Wallet::WalletPrivate *d, const QString &label, KSecretsService::SecretItem *) { bool result = false; QMap map; if (d->readEntry< QMap< QString, QString> >(label, map)) { _value.insert(label, map); result = true; } return result; } QMap< QString, QMap< QString, QString> > &_value; }; #endif int Wallet::readMapList(const QString &key, QMap > &value) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { rc = d->forEachItemThatMatches(key, WalletPrivate::InsertIntoMapList(value)); } else { #endif registerTypes(); if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().readMapList(d->handle, d->folder, key, appid()); if (r.isValid()) { rc = 0; const QVariantMap val = r.value(); for (QVariantMap::const_iterator it = val.begin(); it != val.end(); ++it) { QByteArray mapData = it.value().toByteArray(); if (!mapData.isEmpty()) { QDataStream ds(&mapData, QIODevice::ReadOnly); QMap v; ds >> v; value.insert(it.key(), v); } } } #if HAVE_KSECRETSSERVICE } #endif return rc; } int Wallet::readPassword(const QString &key, QString &value) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { rc = d->readEntry(key, value); } else { #endif if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().readPassword(d->handle, d->folder, key, appid()); if (r.isValid()) { value = r; rc = 0; } #if HAVE_KSECRETSSERVICE } #endif return rc; } #if HAVE_KSECRETSSERVICE struct Wallet::WalletPrivate::InsertIntoPasswordList { InsertIntoPasswordList(QMap< QString, QString> &value) : _value(value) {} bool operator()(Wallet::WalletPrivate *d, const QString &label, KSecretsService::SecretItem *) { bool result = false; QString pwd; if (d->readEntry(label, pwd) == 0) { _value.insert(label, pwd); result = true; } return result; } QMap< QString, QString > &_value; }; #endif int Wallet::readPasswordList(const QString &key, QMap &value) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { rc = d->forEachItemThatMatches(key, WalletPrivate::InsertIntoPasswordList(value)); } else { #endif registerTypes(); if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().readPasswordList(d->handle, d->folder, key, appid()); if (r.isValid()) { rc = 0; const QVariantMap val = r.value(); for (QVariantMap::const_iterator it = val.begin(); it != val.end(); ++it) { value.insert(it.key(), it.value().toString()); } } #if HAVE_KSECRETSSERVICE } #endif return rc; } int Wallet::writeEntry(const QString &key, const QByteArray &value, EntryType entryType) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { rc = d->writeEntry(key, value, entryType); } else { #endif if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().writeEntry(d->handle, d->folder, key, value, int(entryType), appid()); if (r.isValid()) { rc = r; } #if HAVE_KSECRETSSERVICE } #endif return rc; } int Wallet::writeEntry(const QString &key, const QByteArray &value) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { rc = writeEntry(key, value, Stream); } else { #endif if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().writeEntry(d->handle, d->folder, key, value, appid()); if (r.isValid()) { rc = r; } #if HAVE_KSECRETSSERVICE } #endif return rc; } int Wallet::writeMap(const QString &key, const QMap &value) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { d->writeEntry(key, value, Map); } else { #endif registerTypes(); if (d->handle == -1) { return rc; } QByteArray mapData; QDataStream ds(&mapData, QIODevice::WriteOnly); ds << value; QDBusReply r = walletLauncher()->getInterface().writeMap(d->handle, d->folder, key, mapData, appid()); if (r.isValid()) { rc = r; } #if HAVE_KSECRETSSERVICE } #endif return rc; } int Wallet::writePassword(const QString &key, const QString &value) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { rc = d->writeEntry(key, value, Password); } else { #endif if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().writePassword(d->handle, d->folder, key, value, appid()); if (r.isValid()) { rc = r; } #if HAVE_KSECRETSSERVICE } #endif return rc; } bool Wallet::hasEntry(const QString &key) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { QExplicitlySharedDataPointer item = d->findItem(key); return item; } else { #endif if (d->handle == -1) { return false; } QDBusReply r = walletLauncher()->getInterface().hasEntry(d->handle, d->folder, key, appid()); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return false; } else { return r; } #if HAVE_KSECRETSSERVICE } #endif } int Wallet::removeEntry(const QString &key) { int rc = -1; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { QExplicitlySharedDataPointer item = d->findItem(key); if (item) { KSecretsService::SecretItemDeleteJob *deleteJob = item->deleteItem(); if (!deleteJob->exec()) { qDebug() << "Cannot execute SecretItemDeleteJob " << deleteJob->errorString(); } rc = deleteJob->error(); } } else { #endif if (d->handle == -1) { return rc; } QDBusReply r = walletLauncher()->getInterface().removeEntry(d->handle, d->folder, key, appid()); if (r.isValid()) { rc = r; } #if HAVE_KSECRETSSERVICE } #endif return rc; } Wallet::EntryType Wallet::entryType(const QString &key) { int rc = 0; #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { QExplicitlySharedDataPointer item = d->findItem(key); if (item) { KSecretsService::ReadItemPropertyJob *readAttrsJob = item->attributes(); if (readAttrsJob->exec()) { KSecretsService::StringStringMap attrs = readAttrsJob->propertyValue().value(); if (attrs.contains(KSS_ATTR_WALLETTYPE)) { QString entryType = attrs[KSS_ATTR_WALLETTYPE]; bool ok = false; rc = entryType.toInt(&ok); if (!ok) { rc = 0; qDebug() << KSS_ATTR_WALLETTYPE << " attribute holds non int value " << attrs[KSS_ATTR_WALLETTYPE]; } } } else { qDebug() << "Cannot execute GetSecretItemSecretJob " << readAttrsJob->errorString(); } } } else { #endif if (d->handle == -1) { return Wallet::Unknown; } QDBusReply r = walletLauncher()->getInterface().entryType(d->handle, d->folder, key, appid()); if (r.isValid()) { rc = r; } #if HAVE_KSECRETSSERVICE } #endif return static_cast(rc); } void Wallet::WalletPrivate::walletServiceUnregistered() { if (handle >= 0) { q->slotWalletClosed(handle); } } void Wallet::slotFolderUpdated(const QString &wallet, const QString &folder) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { // TODO: implement this Q_ASSERT(0); } else { #endif if (d->name == wallet) { emit folderUpdated(folder); } #if HAVE_KSECRETSSERVICE } #endif } void Wallet::slotFolderListUpdated(const QString &wallet) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { // TODO: implement this Q_ASSERT(0); } else { #endif if (d->name == wallet) { emit folderListUpdated(); } #if HAVE_KSECRETSSERVICE } #endif } void Wallet::slotApplicationDisconnected(const QString &wallet, const QString &application) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { // TODO: implement this Q_ASSERT(0); } else { #endif if (d->handle >= 0 && d->name == wallet && application == appid()) { slotWalletClosed(d->handle); } #if HAVE_KSECRETSSERVICE } #endif } void Wallet::walletAsyncOpened(int tId, int handle) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { // TODO: implement this Q_ASSERT(0); } else { #endif // ignore responses to calls other than ours if (d->transactionId != tId || d->handle != -1) { return; } // disconnect the async signal disconnect(this, SLOT(walletAsyncOpened(int,int))); d->handle = handle; emit walletOpened(handle > 0); #if HAVE_KSECRETSSERVICE } #endif } void Wallet::emitWalletAsyncOpenError() { emit walletOpened(false); } void Wallet::emitWalletOpened() { emit walletOpened(true); } bool Wallet::folderDoesNotExist(const QString &wallet, const QString &folder) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { qDebug() << "WARNING: changing semantics of folderDoesNotExist with KSS: will prompt for the password"; Wallet *w = openWallet(wallet, 0, Synchronous); if (w) { return !w->hasFolder(folder); } else { return true; } } else { #endif if (walletLauncher()->m_walletEnabled) { QDBusReply r = walletLauncher()->getInterface().folderDoesNotExist(wallet, folder); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return false; } else { return r; } } else return false; #if HAVE_KSECRETSSERVICE } #endif } bool Wallet::keyDoesNotExist(const QString &wallet, const QString &folder, const QString &key) { #if HAVE_KSECRETSSERVICE if (walletLauncher()->m_useKSecretsService) { qDebug() << "WARNING: changing semantics of keyDoesNotExist with KSS: will prompt for the password"; Wallet *w = openWallet(wallet, 0, Synchronous); if (w) { return !w->hasEntry(key); } return false; } else { #endif if (walletLauncher()->m_walletEnabled) { QDBusReply r = walletLauncher()->getInterface().keyDoesNotExist(wallet, folder, key); if (!r.isValid()) { qDebug() << "Invalid DBus reply: " << r.error(); return false; } else { return r; } } else return false; #if HAVE_KSECRETSSERVICE } #endif } void Wallet::virtual_hook(int, void *) { //BASE::virtual_hook( id, data ); } KWalletDLauncher::KWalletDLauncher() - : m_wallet_deamon(0) + : m_wallet_deamon(nullptr) , m_cgroup(KSharedConfig::openConfig(QStringLiteral("kwalletrc") , KConfig::NoGlobals)->group("Wallet")) , m_walletEnabled(false) { m_useKSecretsService = m_cgroup.readEntry("UseKSecretsService", false); m_walletEnabled = m_cgroup.readEntry("Enabled", true); if (!m_walletEnabled) { qDebug() << "The wallet service was disabled by the user"; return; } #if HAVE_KSECRETSSERVICE if (m_useKSecretsService) { // NOOP } else { #endif m_wallet_deamon = new org::kde::KWallet(QString::fromLatin1(s_kwalletdServiceName), QStringLiteral("/modules/kwalletd5"), QDBusConnection::sessionBus()); #if HAVE_KSECRETSSERVICE } #endif } KWalletDLauncher::~KWalletDLauncher() { delete m_wallet_deamon; } org::kde::KWallet &KWalletDLauncher::getInterface() { // Q_ASSERT(!m_useKSecretsService); - Q_ASSERT(m_wallet_deamon != 0); + Q_ASSERT(m_wallet_deamon != nullptr); // check if kwalletd is already running QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); if (!bus->isServiceRegistered(QString::fromLatin1(s_kwalletdServiceName))) { // not running! check if it is enabled. if (m_walletEnabled) { // wallet is enabled! try launching it QDBusReply reply = bus->startService(s_kwalletdServiceName); if (!reply.isValid()) { qCritical() << "Couldn't start kwalletd: " << reply.error(); } if (!bus->isServiceRegistered(QString::fromLatin1(s_kwalletdServiceName))) { qDebug() << "The kwalletd service is still not registered"; } else { qDebug() << "The kwalletd service has been registered"; } } else { qCritical() << "The kwalletd service has been disabled"; } } return *m_wallet_deamon; } } // namespace KWallet #include "moc_kwallet.cpp" diff --git a/src/runtime/kwallet-query/src/querydriver.cpp b/src/runtime/kwallet-query/src/querydriver.cpp index 4ad0e66..c0880e3 100644 --- a/src/runtime/kwallet-query/src/querydriver.cpp +++ b/src/runtime/kwallet-query/src/querydriver.cpp @@ -1,204 +1,204 @@ /* This file is part of the KDE Copyright (C) 2015 Valentin Rusu (kde@rusu.info) This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "querydriver.h" #include #include #include #include #include #include #include #include QueryDriver::QueryDriver(int &argc, char* argv[]) : QApplication(argc, argv) - , theWallet(0) + , theWallet(nullptr) , verbose(false) , entryFolder(QStringLiteral("Passwords")) { QTimerEvent *timerEvent = new QTimerEvent(100); postEvent(this, timerEvent); } QueryDriver::~QueryDriver() { // NOTE: no need to close the wallet, let's leave it open for next time // we'll query it preventing too many annoying password prompts // if (theWallet != NULL && theWallet->isOpen()) { // Wallet::closeWallet(walletName, false); // } delete theWallet; } void QueryDriver::timerEvent(QTimerEvent *event) { QApplication::timerEvent(event); if (verbose) qDebug() << "timer event"; auto wl = Wallet::walletList(); if (wl.indexOf(walletName) == -1) { std::cout << i18n("Wallet %1 not found", walletName).toUtf8().constData() << std::endl; exit(1); } else { if (verbose) qDebug() << "standby opening wallet " << walletName; theWallet = Wallet::openWallet(walletName, desktop()->screen()->winId(), Wallet::Asynchronous); // theWallet = Wallet::openWallet(walletName, desktop()->screen()->winId()); connect(theWallet, SIGNAL(walletOpened(bool)), this, SLOT(walletOpened(bool))); } } void QueryDriver::setWalletName(const QString& name) { walletName = name; } void QueryDriver::setMode(Mode mode) { this->mode = mode; } void QueryDriver::walletOpened(bool success) { if (!success) { std::cout << i18n("Failed to open wallet %1. Aborting", walletName).toUtf8().constData() << std::endl; exit(2); } else { switch (mode) { case List: readEntries(); break; case Read: readValue(); break; case Write: writeValue(); break; default: Q_ASSERT(0); } } } void QueryDriver::readEntries() { theWallet = Wallet::openWallet(walletName, 0); if (entryFolder.isEmpty()) { auto fl = theWallet->folderList(); for (auto f: fl) { std::cout << f.toUtf8().constData() << std::endl; Q_ASSERT(theWallet->setFolder(f)); auto el = theWallet->entryList(); for (auto e: el) { std::cout << "\t" << e.toUtf8().constData() << std::endl; } } } else { if (!theWallet->setFolder(entryFolder)) { std::cout << i18n("The folder %1 does not exist!", entryFolder).toUtf8().constData() << std::endl; exit(4); } auto el = theWallet->entryList(); for (auto e: el) { std::cout << e.toUtf8().constData() << std::endl; } } quit(); } void QueryDriver::readValue() { if (verbose) qDebug() << "reading" << entryName << "from" << entryFolder << "from" << walletName; if (!theWallet->setFolder(entryFolder)) { std::cout << i18n("The folder %1 does not exist!", entryFolder).toUtf8().constData() << std::endl; exit(4); } Wallet::EntryType kind = theWallet->entryType(entryName); if (kind == Wallet::Password) { readPasswordValue(); } else if (kind == Wallet::Map) { readMapValue(); } else { std::cout << i18n("Failed to read entry %1 value from the %2 wallet.", entryName, walletName).toUtf8().constData() << std::endl; exit(4); } quit(); } void QueryDriver::readMapValue() { QMap map; int rc = theWallet->readMap(entryName, map); if (rc != 0) { std::cout << i18n("Failed to read entry %1 value from the %2 wallet", entryName, walletName).toUtf8().constData() << std::endl; exit(4); } QJsonObject json; for (auto e : map.keys()) { json.insert(e, QJsonValue::fromVariant(QVariant(map.value(e)))); } std::cout << QJsonDocument(json).toJson().constData() << std::endl; } void QueryDriver::readPasswordValue() { QString entryValue; int rc = theWallet->readPassword(entryName, entryValue); if (rc != 0) { std::cout << i18n("Failed to read entry %1 value from the %2 wallet", entryName, walletName).toUtf8().constData() << std::endl; exit(4); } QStringList el = entryValue.split(QStringLiteral("\n"), QString::SkipEmptyParts); for (auto e : el) { std::cout << e.toUtf8().constData() << std::endl; } } void QueryDriver::writeValue() { if (verbose) qDebug() << "writing" << entryName << "to" << entryFolder << "to" << walletName; theWallet->setFolder(entryFolder); QString passwordContents; for (std::string line; std::getline(std::cin, line); ) { if (!passwordContents.isEmpty()) passwordContents += '\n'; passwordContents += QString::fromStdString(line); if (!std::cin) break; } Wallet::EntryType kind = theWallet->entryType(entryName); if (kind == Wallet::Password) { if (verbose) qDebug() << "about to write" << passwordContents; int rc = theWallet->writePassword(entryName, passwordContents); if (rc != 0) { std::cout << i18n("Failed to write entry %1 value to %2 wallet", entryName, walletName).toUtf8().constData() << std::endl; exit(4); } } else if (kind == Wallet::Map) { const QJsonDocument json = QJsonDocument::fromJson(passwordContents.toLatin1()); if (!json.isNull()) { QJsonObject values = json.object(); QMap map; for (auto e : values.keys()) { map.insert(e, values.value(e).toString()); } if (verbose) qDebug() << "about to write" << map; int rc = theWallet->writeMap(entryName, map); if (rc != 0) { std::cout << i18n("Failed to write entry %1 value to %2 wallet", entryName, walletName).toUtf8().constData() << std::endl; exit(4); } } } quit(); } diff --git a/src/runtime/kwalletd/backend/backendpersisthandler.cpp b/src/runtime/kwalletd/backend/backendpersisthandler.cpp index eeacee2..e13654b 100644 --- a/src/runtime/kwalletd/backend/backendpersisthandler.cpp +++ b/src/runtime/kwalletd/backend/backendpersisthandler.cpp @@ -1,744 +1,744 @@ /** * This file is part of the KDE project * Copyright (C) 2013 Valentin Rusu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #ifdef HAVE_GPGMEPP #include #include #include #include #include #include #include #endif #include "backendpersisthandler.h" #include "kwalletbackend.h" #include "blowfish.h" #include "sha1.h" #include "cbc.h" #ifdef Q_OS_WIN #include #include #endif #define KWALLET_CIPHER_BLOWFISH_ECB 0 // this was the old KWALLET_CIPHER_BLOWFISH_CBC #define KWALLET_CIPHER_3DES_CBC 1 // unsupported #define KWALLET_CIPHER_GPG 2 #define KWALLET_CIPHER_BLOWFISH_CBC 3 #define KWALLET_HASH_SHA1 0 #define KWALLET_HASH_MD5 1 // unsupported #define KWALLET_HASH_PBKDF2_SHA512 2 // used when using kwallet with pam or since 4.13 version namespace KWallet { typedef char Digest[16]; static int getRandomBlock(QByteArray &randBlock) { #ifdef Q_OS_WIN //krazy:exclude=cpp // Use windows crypto API to get randomness on win32 // HACK: this should be done using qca HCRYPTPROV hProv; if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { return -1; // couldn't get random data } if (!CryptGenRandom(hProv, static_cast(randBlock.size()), (BYTE *)randBlock.data())) { return -3; // read error } // release the crypto context CryptReleaseContext(hProv, 0); return randBlock.size(); #else // First try /dev/urandom if (QFile::exists(QStringLiteral("/dev/urandom"))) { QFile devrand(QStringLiteral("/dev/urandom")); if (devrand.open(QIODevice::ReadOnly)) { int rc = devrand.read(randBlock.data(), randBlock.size()); if (rc != randBlock.size()) { return -3; // not enough data read } return 0; } } // If that failed, try /dev/random // FIXME: open in noblocking mode! if (QFile::exists(QStringLiteral("/dev/random"))) { QFile devrand(QStringLiteral("/dev/random")); if (devrand.open(QIODevice::ReadOnly)) { int rc = 0; int cnt = 0; do { int rc2 = devrand.read(randBlock.data() + rc, randBlock.size()); if (rc2 < 0) { return -3; // read error } rc += rc2; cnt++; if (cnt > randBlock.size()) { return -4; // reading forever?! } } while (rc < randBlock.size()); return 0; } } // EGD method QString randFilename = QString::fromLocal8Bit(qgetenv("RANDFILE")); if (!randFilename.isEmpty()) { if (QFile::exists(randFilename)) { QFile devrand(randFilename); if (devrand.open(QIODevice::ReadOnly)) { int rc = devrand.read(randBlock.data(), randBlock.size()); if (rc != randBlock.size()) { return -3; // not enough data read } return 0; } } } // Couldn't get any random data!! return -1; #endif } BackendPersistHandler *BackendPersistHandler::getPersistHandler(BackendCipherType cipherType) { switch (cipherType) { case BACKEND_CIPHER_BLOWFISH: return new BlowfishPersistHandler; #ifdef HAVE_GPGMEPP case BACKEND_CIPHER_GPG: return new GpgPersistHandler; #endif // HAVE_GPGMEPP default: Q_ASSERT(0); - return 0; + return nullptr; } } BackendPersistHandler *BackendPersistHandler::getPersistHandler(char magicBuf[12]) { if ((magicBuf[2] == KWALLET_CIPHER_BLOWFISH_ECB || magicBuf[2] == KWALLET_CIPHER_BLOWFISH_CBC) && (magicBuf[3] == KWALLET_HASH_SHA1 || magicBuf[3] == KWALLET_HASH_PBKDF2_SHA512)) { bool useECBforReading = magicBuf[2] == KWALLET_CIPHER_BLOWFISH_ECB; if (useECBforReading) { qDebug() << "this wallet uses ECB encryption. It'll be converted to CBC on next save."; } return new BlowfishPersistHandler(useECBforReading); } #ifdef HAVE_GPGMEPP if (magicBuf[2] == KWALLET_CIPHER_GPG && magicBuf[3] == 0) { return new GpgPersistHandler; } #endif // HAVE_GPGMEPP - return 0; // unknown cipher or hash + return nullptr; // unknown cipher or hash } int BlowfishPersistHandler::write(Backend *wb, QSaveFile &sf, QByteArray &version, WId) { assert(wb->_cipherType == BACKEND_CIPHER_BLOWFISH); if (_useECBforReading) { qDebug() << "This wallet used ECB and is now saved using CBC"; _useECBforReading = false; } version[2] = KWALLET_CIPHER_BLOWFISH_CBC; if (!wb->_useNewHash) { version[3] = KWALLET_HASH_SHA1; } else { version[3] = KWALLET_HASH_PBKDF2_SHA512;//Since 4.13 we always use PBKDF2_SHA512 } if (sf.write(version) != 4) { sf.cancelWriting(); return -4; // write error } // Holds the hashes we write out QByteArray hashes; QDataStream hashStream(&hashes, QIODevice::WriteOnly); QCryptographicHash md5(QCryptographicHash::Md5); hashStream << static_cast(wb->_entries.count()); // Holds decrypted data prior to encryption QByteArray decrypted; // FIXME: we should estimate the amount of data we will write in each // buffer and resize them approximately in order to avoid extra // resizes. // populate decrypted QDataStream dStream(&decrypted, QIODevice::WriteOnly); for (Backend::FolderMap::ConstIterator i = wb->_entries.constBegin(); i != wb->_entries.constEnd(); ++i) { dStream << i.key(); dStream << static_cast(i.value().count()); md5.reset(); md5.addData(i.key().toUtf8()); hashStream.writeRawData(md5.result().constData(), 16); hashStream << static_cast(i.value().count()); for (Backend::EntryMap::ConstIterator j = i.value().constBegin(); j != i.value().constEnd(); ++j) { dStream << j.key(); dStream << static_cast(j.value()->type()); dStream << j.value()->value(); md5.reset(); md5.addData(j.key().toUtf8()); hashStream.writeRawData(md5.result().constData(), 16); } } if (sf.write(hashes) != hashes.size()) { sf.cancelWriting(); return -4; // write error } // calculate the hash of the file SHA1 sha; BlowFish _bf; CipherBlockChain bf(&_bf); sha.process(decrypted.data(), decrypted.size()); // prepend and append the random data QByteArray wholeFile; long blksz = bf.blockSize(); long newsize = decrypted.size() + blksz + // encrypted block 4 + // file size 20; // size of the SHA hash int delta = (blksz - (newsize % blksz)); newsize += delta; wholeFile.resize(newsize); QByteArray randBlock; randBlock.resize(blksz + delta); if (getRandomBlock(randBlock) < 0) { sha.reset(); decrypted.fill(0); sf.cancelWriting(); return -3; // Fatal error: can't get random } for (int i = 0; i < blksz; i++) { wholeFile[i] = randBlock[i]; } for (int i = 0; i < 4; i++) { wholeFile[(int)(i + blksz)] = (decrypted.size() >> 8 * (3 - i)) & 0xff; } for (int i = 0; i < decrypted.size(); i++) { wholeFile[(int)(i + blksz + 4)] = decrypted[i]; } for (int i = 0; i < delta; i++) { wholeFile[(int)(i + blksz + 4 + decrypted.size())] = randBlock[(int)(i + blksz)]; } const char *hash = (const char *)sha.hash(); for (int i = 0; i < 20; i++) { wholeFile[(int)(newsize - 20 + i)] = hash[i]; } sha.reset(); decrypted.fill(0); // encrypt the data if (!bf.setKey(wb->_passhash.data(), wb->_passhash.size() * 8)) { wholeFile.fill(0); sf.cancelWriting(); return -2; // encrypt error } int rc = bf.encrypt(wholeFile.data(), wholeFile.size()); if (rc < 0) { wholeFile.fill(0); sf.cancelWriting(); return -2; // encrypt error } // write the file auto written = sf.write(wholeFile); if (written != wholeFile.size()) { wholeFile.fill(0); sf.cancelWriting(); return -4; // write error } if (!sf.commit()) { qDebug() << "WARNING: wallet sync to disk failed! QSaveFile status was " << sf.errorString(); wholeFile.fill(0); return -4; // write error } wholeFile.fill(0); return 0; } int BlowfishPersistHandler::read(Backend *wb, QFile &db, WId) { wb->_cipherType = BACKEND_CIPHER_BLOWFISH; wb->_hashes.clear(); // Read in the hashes QDataStream hds(&db); quint32 n; hds >> n; if (n > 0xffff) { // sanity check return -43; } for (size_t i = 0; i < n; ++i) { Digest d, d2; // judgment day MD5Digest ba; QMap >::iterator it; quint32 fsz; if (hds.atEnd()) { return -43; } hds.readRawData(d, 16); hds >> fsz; ba = MD5Digest(reinterpret_cast(d)); it = wb->_hashes.insert(ba, QList()); for (size_t j = 0; j < fsz; ++j) { hds.readRawData(d2, 16); ba = MD5Digest(d2); (*it).append(ba); } } // Read in the rest of the file. QByteArray encrypted = db.readAll(); assert(encrypted.size() < db.size()); BlowFish _bf; CipherBlockChain bf(&_bf, _useECBforReading); int blksz = bf.blockSize(); if ((encrypted.size() % blksz) != 0) { return -5; // invalid file structure } bf.setKey((void *)wb->_passhash.data(), wb->_passhash.size() * 8); if (!encrypted.data()) { wb->_passhash.fill(0); encrypted.fill(0); return -7; // file structure error } int rc = bf.decrypt(encrypted.data(), encrypted.size()); if (rc < 0) { wb->_passhash.fill(0); encrypted.fill(0); return -6; // decrypt error } const char *t = encrypted.data(); // strip the leading data t += blksz; // one block of random data // strip the file size off long fsize = 0; fsize |= (long(*t) << 24) & 0xff000000; t++; fsize |= (long(*t) << 16) & 0x00ff0000; t++; fsize |= (long(*t) << 8) & 0x0000ff00; t++; fsize |= long(*t) & 0x000000ff; t++; if (fsize < 0 || fsize > long(encrypted.size()) - blksz - 4) { qDebug() << "fsize: " << fsize << " encrypted.size(): " << encrypted.size() << " blksz: " << blksz; encrypted.fill(0); return -9; // file structure error. } // compute the hash ourself SHA1 sha; sha.process(t, fsize); const char *testhash = (const char *)sha.hash(); // compare hashes int sz = encrypted.size(); for (int i = 0; i < 20; i++) { if (testhash[i] != encrypted[sz - 20 + i]) { encrypted.fill(0); sha.reset(); return -8; // hash error. } } sha.reset(); // chop off the leading blksz+4 bytes QByteArray tmpenc(encrypted.data() + blksz + 4, fsize); encrypted = tmpenc; tmpenc.fill(0); // Load the data structures up QDataStream eStream(encrypted); while (!eStream.atEnd()) { QString folder; quint32 n; eStream >> folder; eStream >> n; // Force initialisation wb->_entries[folder].clear(); for (size_t i = 0; i < n; ++i) { QString key; KWallet::Wallet::EntryType et = KWallet::Wallet::Unknown; Entry *e = new Entry; eStream >> key; qint32 x = 0; // necessary to read properly eStream >> x; et = static_cast(x); switch (et) { case KWallet::Wallet::Password: case KWallet::Wallet::Stream: case KWallet::Wallet::Map: break; default: // Unknown entry delete e; continue; } QByteArray a; eStream >> a; e->setValue(a); e->setType(et); e->setKey(key); wb->_entries[folder][key] = e; } } wb->_open = true; encrypted.fill(0); return 0; } #ifdef HAVE_GPGMEPP GpgME::Error initGpgME() { GpgME::Error err; static bool alreadyInitialized = false; if (!alreadyInitialized) { GpgME::initializeLibrary(); err = GpgME::checkEngine(GpgME::OpenPGP); if (err) { qDebug() << "OpenPGP not supported!"; } alreadyInitialized = true; } return err; } int GpgPersistHandler::write(Backend *wb, QSaveFile &sf, QByteArray &version, WId w) { version[2] = KWALLET_CIPHER_GPG; version[3] = 0; if (sf.write(version) != 4) { sf.cancelWriting(); return -4; // write error } GpgME::Error err = initGpgME(); if (err) { qDebug() << "initGpgME returned " << err.code(); KMessageBox::errorWId(w, i18n("Error when attempting to initialize OpenPGP while attempting to save the wallet %1. Error code is %2. Please fix your system configuration, then try again.", wb->_name.toHtmlEscaped(), err.code())); sf.cancelWriting(); return -5; } std::shared_ptr< GpgME::Context > ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); if (0 == ctx) { qDebug() << "Cannot setup OpenPGP context!"; KMessageBox::errorWId(w, i18n("Error when attempting to initialize OpenPGP while attempting to save the wallet %1. Please fix your system configuration, then try again."), wb->_name.toHtmlEscaped()); return -6; } assert(wb->_cipherType == BACKEND_CIPHER_GPG); QByteArray hashes; QDataStream hashStream(&hashes, QIODevice::WriteOnly); QCryptographicHash md5(QCryptographicHash::Md5); hashStream << static_cast(wb->_entries.count()); QByteArray values; QDataStream valueStream(&values, QIODevice::WriteOnly); Backend::FolderMap::ConstIterator i = wb->_entries.constBegin(); Backend::FolderMap::ConstIterator ie = wb->_entries.constEnd(); for (; i != ie; ++i) { valueStream << i.key(); valueStream << static_cast(i.value().count()); md5.reset(); md5.addData(i.key().toUtf8()); hashStream.writeRawData(md5.result().constData(), 16); hashStream << static_cast(i.value().count()); Backend::EntryMap::ConstIterator j = i.value().constBegin(); Backend::EntryMap::ConstIterator je = i.value().constEnd(); for (; j != je; ++j) { valueStream << j.key(); valueStream << static_cast(j.value()->type()); valueStream << j.value()->value(); md5.reset(); md5.addData(j.key().toUtf8()); hashStream.writeRawData(md5.result().constData(), 16); } } QByteArray dataBuffer; QDataStream dataStream(&dataBuffer, QIODevice::WriteOnly); QString keyID(wb->_gpgKey.keyID()); dataStream << keyID; dataStream << hashes; dataStream << values; GpgME::Data decryptedData(dataBuffer.data(), dataBuffer.size(), false); GpgME::Data encryptedData; std::vector< GpgME::Key > keys; keys.push_back(wb->_gpgKey); GpgME::EncryptionResult res = ctx->encrypt(keys, decryptedData, encryptedData, GpgME::Context::None); if (res.error()) { int gpgerr = res.error().code(); KMessageBox::errorWId(w, i18n("Encryption error while attempting to save the wallet %1. Error code is %2 (%3). Please fix your system configuration, then try again. This error may occur if you are not using a full trust GPG key. Please ensure you have the secret key for the key you are using.", wb->_name.toHtmlEscaped(), gpgerr, gpgme_strerror(gpgerr))); qDebug() << "GpgME encryption error: " << res.error().code(); sf.cancelWriting(); return -7; } char buffer[4096]; ssize_t bytes = 0; encryptedData.seek(0, SEEK_SET); while (bytes = encryptedData.read(buffer, sizeof(buffer) / sizeof(buffer[0]))) { if (sf.write(buffer, bytes) != bytes) { KMessageBox::errorWId(w, i18n("File handling error while attempting to save the wallet %1. Error was %2. Please fix your system configuration, then try again.", wb->_name.toHtmlEscaped(), sf.errorString())); sf.cancelWriting(); return -4; // write error } } if (!sf.commit()) { qDebug() << "WARNING: wallet sync to disk failed! QSaveFile status was " << sf.errorString(); return -4; // write error } return 0; } int GpgPersistHandler::read(Backend *wb, QFile &sf, WId w) { GpgME::Error err = initGpgME(); if (err) { KMessageBox::errorWId(w, i18n("Error when attempting to initialize OpenPGP while attempting to open the wallet %1. Error code is %2. Please fix your system configuration, then try again.", wb->_name.toHtmlEscaped(), err.code())); return -1; } wb->_cipherType = BACKEND_CIPHER_GPG; wb->_hashes.clear(); // the remainder of the file is GPG encrypted. Let's decrypt it GpgME::Data encryptedData; char buffer[4096]; ssize_t bytes = 0; while (bytes = sf.read(buffer, sizeof(buffer) / sizeof(buffer[0]))) { encryptedData.write(buffer, bytes); } retry_label: std::shared_ptr< GpgME::Context > ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); if (0 == ctx) { KMessageBox::errorWId(w, i18n("Error when attempting to initialize OpenPGP while attempting to open the wallet %1. Please fix your system configuration, then try again.", wb->_name.toHtmlEscaped())); qDebug() << "Cannot setup OpenPGP context!"; return -1; } GpgME::Data decryptedData; encryptedData.seek(0, SEEK_SET); GpgME::DecryptionResult res = ctx->decrypt(encryptedData, decryptedData); if (res.error()) { qDebug() << "Error decrypting message: " << res.error().asString() << ", code " << res.error().code() << ", source " << res.error().source(); KGuiItem btnRetry(i18n("Retry")); // FIXME the logic here should be a little more elaborate; a dialog box should be used with "retry", "cancel", but also "troubleshoot" with options to show card status and to kill scdaemon int userChoice = KMessageBox::warningYesNoWId(w, i18n("Error when attempting to decrypt the wallet %1 using GPG. If you're using a SmartCard, please ensure it's inserted then try again.

GPG error was %2
", wb->_name.toHtmlEscaped(), res.error().asString()), i18n("kwalletd GPG backend"), btnRetry, KStandardGuiItem::cancel()); if (userChoice == KMessageBox::Yes) { decryptedData.seek(0, SEEK_SET); goto retry_label; } return -1; } decryptedData.seek(0, SEEK_SET); QByteArray dataBuffer; while (bytes = decryptedData.read(buffer, sizeof(buffer) / sizeof(buffer[0]))) { dataBuffer.append(buffer, bytes); } // load the wallet from the decrypted data QDataStream dataStream(dataBuffer); QString keyID; QByteArray hashes; QByteArray values; dataStream >> keyID; dataStream >> hashes; dataStream >> values; // locate the GPG key having the ID found inside the file. This will be needed later, when writing changes to disk. QDataStream fileStream(&sf); fileStream.unsetDevice(); qDebug() << "This wallet was encrypted using GPG key with ID " << keyID; ctx->setKeyListMode(GPGME_KEYLIST_MODE_LOCAL); std::vector< GpgME::Key > keys; err = ctx->startKeyListing(); while (!err) { GpgME::Key k = ctx->nextKey(err); if (err) { break; } if (keyID == k.keyID()) { qDebug() << "The key was found."; wb->_gpgKey = k; break; } } ctx->endKeyListing(); if (wb->_gpgKey.isNull()) { KMessageBox::errorWId(w, i18n("Error when attempting to open the wallet %1. The wallet was encrypted using the GPG Key ID %2 but this key was not found on your system.", wb->_name.toHtmlEscaped(), keyID)); return -1; } QDataStream hashStream(hashes); QDataStream valueStream(values); quint32 hashCount; hashStream >> hashCount; if (hashCount > 0xFFFF) { return -43; } quint32 folderCount = hashCount; while (hashCount--) { Digest d; hashStream.readRawData(d, 16); quint32 folderSize; hashStream >> folderSize; MD5Digest ba = MD5Digest(reinterpret_cast(d)); QMap >::iterator it = wb->_hashes.insert(ba, QList()); while (folderSize--) { Digest d2; hashStream.readRawData(d2, 16); ba = MD5Digest(d2); (*it).append(ba); } } while (folderCount--) { QString folder; valueStream >> folder; quint32 entryCount; valueStream >> entryCount; wb->_entries[folder].clear(); while (entryCount--) { KWallet::Wallet::EntryType et = KWallet::Wallet::Unknown; Entry *e = new Entry; QString key; valueStream >> key; qint32 x = 0; // necessary to read properly valueStream >> x; et = static_cast(x); switch (et) { case KWallet::Wallet::Password: case KWallet::Wallet::Stream: case KWallet::Wallet::Map: break; default: // Unknown entry delete e; continue; } QByteArray a; valueStream >> a; e->setValue(a); e->setType(et); e->setKey(key); wb->_entries[folder][key] = e; } } wb->_open = true; return 0; } #endif // HAVE_GPGMEPP } // namespace diff --git a/src/runtime/kwalletd/backend/blowfish.cc b/src/runtime/kwalletd/backend/blowfish.cc index 2cdea70..5d7f55c 100644 --- a/src/runtime/kwalletd/backend/blowfish.cc +++ b/src/runtime/kwalletd/backend/blowfish.cc @@ -1,256 +1,256 @@ /* This file is part of the KDE project Copyright (C) 2001 George Staikos This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // FIXME: should we unroll some loops? Optimization can be done here. /* Implementation of 16 rounds blowfish as described in: * _Applied_Cryptography_ (c) Bruce Schneier, 1996. */ #include "blowfish.h" #include #include #include #include "blowfishtables.h" // DO NOT INCLUDE THIS. IT BREAKS KWALLET. // We need to live with -Wundef until someone really figures out the problem. //#include // for Q_BYTE_ORDER and friends BlowFish::BlowFish() { _blksz = 8; - _key = 0L; + _key = nullptr; _init = false; } bool BlowFish::init() { // Initialize the sboxes for (int i = 0; i < 256; i++) { _S[0][i] = ks0[i]; _S[1][i] = ks1[i]; _S[2][i] = ks2[i]; _S[3][i] = ks3[i]; } uint32_t datal = 0; uint32_t datar = 0; uint32_t data = 0; int j = 0; // Update the sboxes and pbox. for (int i = 0; i < 18; i++) { data = 0; for (int k = 0; k < 4; ++k) { data = (data << 8) | ((unsigned char *)_key)[j++]; if (j >= _keylen / 8) { j = 0; } } _P[i] = P[i] ^ data; } for (int i = 0; i < 18; i += 2) { encipher(&datal, &datar); _P[i] = datal; _P[i + 1] = datar; } for (int j = 0; j < 4; j++) { for (int i = 0; i < 256; i += 2) { encipher(&datal, &datar); _S[j][i] = datal; _S[j][i + 1] = datar; } } // Nice code from gpg's implementation... // check to see if the key is weak and return error if so for (int i = 0; i < 255; i++) { for (int j = i + 1; j < 256; j++) { if ((_S[0][i] == _S[0][j]) || (_S[1][i] == _S[1][j]) || (_S[2][i] == _S[2][j]) || (_S[3][i] == _S[3][j])) { return false; } } } _init = true; return true; } BlowFish::~BlowFish() { delete[](unsigned char *)_key; - _key = 0L; + _key = nullptr; } int BlowFish::keyLen() const { return 448; } bool BlowFish::variableKeyLen() const { return true; } bool BlowFish::readyToGo() const { return _init; } bool BlowFish::setKey(void *key, int bitlength) { if (bitlength <= 0 || bitlength > 448 || bitlength % 8 != 0) { return false; } delete[](unsigned char *)_key; _key = new unsigned char[bitlength / 8]; memcpy(_key, key, bitlength / 8); _keylen = bitlength; return init(); } #if Q_BYTE_ORDER == Q_BIG_ENDIAN #define shuffle(x) do { \ uint32_t r = x; \ x = (r & 0xff000000) >> 24; \ x |= (r & 0x00ff0000) >> 8; \ x |= (r & 0x0000ff00) << 8; \ x |= (r & 0x000000ff) << 24; \ } while (0) #endif int BlowFish::encrypt(void *block, int len) { uint32_t *d = (uint32_t *)block; if (!_init || len % _blksz != 0) { return -1; } for (int i = 0; i < len / _blksz; i++) { #if Q_BYTE_ORDER == Q_BIG_ENDIAN shuffle(*d); shuffle(*(d + 1)); #endif encipher(d, d + 1); #if Q_BYTE_ORDER == Q_BIG_ENDIAN shuffle(*d); shuffle(*(d + 1)); #endif d += 2; } return len; } int BlowFish::decrypt(void *block, int len) { uint32_t *d = (uint32_t *)block; if (!_init || len % _blksz != 0) { return -1; } for (int i = 0; i < len / _blksz; i++) { #if Q_BYTE_ORDER == Q_BIG_ENDIAN shuffle(*d); shuffle(*(d + 1)); #endif decipher(d, d + 1); #if Q_BYTE_ORDER == Q_BIG_ENDIAN shuffle(*d); shuffle(*(d + 1)); #endif d += 2; } return len; } uint32_t BlowFish::F(uint32_t x) { unsigned short a, b, c, d; uint32_t y; d = x & 0x000000ff; x >>= 8; c = x & 0x000000ff; x >>= 8; b = x & 0x000000ff; x >>= 8; a = x & 0x000000ff; y = _S[0][a] + _S[1][b]; y ^= _S[2][c]; y += _S[3][d]; return y; } void BlowFish::encipher(uint32_t *xl, uint32_t *xr) { uint32_t Xl = *xl, Xr = *xr, temp; for (int i = 0; i < 16; ++i) { Xl ^= _P[i]; Xr ^= F(Xl); // Exchange temp = Xl; Xl = Xr; Xr = temp; } // Exchange temp = Xl; Xl = Xr; Xr = temp; Xr ^= _P[16]; Xl ^= _P[17]; *xl = Xl; *xr = Xr; } void BlowFish::decipher(uint32_t *xl, uint32_t *xr) { uint32_t Xl = *xl, Xr = *xr, temp; for (int i = 17; i > 1; --i) { Xl ^= _P[i]; Xr ^= F(Xl); // Exchange temp = Xl; Xl = Xr; Xr = temp; } // Exchange temp = Xl; Xl = Xr; Xr = temp; Xr ^= _P[1]; Xl ^= _P[0]; *xl = Xl; *xr = Xr; } diff --git a/src/runtime/kwalletd/backend/kwalletbackend.cc b/src/runtime/kwalletd/backend/kwalletbackend.cc index 8868090..5c09455 100644 --- a/src/runtime/kwalletd/backend/kwalletbackend.cc +++ b/src/runtime/kwalletd/backend/kwalletbackend.cc @@ -1,723 +1,723 @@ /* This file is part of the KDE project * * Copyright (C) 2001-2004 George Staikos * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kwalletbackend.h" #include #include #ifdef HAVE_GPGMEPP #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "blowfish.h" #include "sha1.h" #include "cbc.h" #include // quick fix to get random numbers on win32 #ifdef Q_OS_WIN //krazy:exclude=cpp #include #include #endif #define KWALLET_VERSION_MAJOR 0 #define KWALLET_VERSION_MINOR 1 using namespace KWallet; #define KWMAGIC "KWALLET\n\r\0\r\n" class Backend::BackendPrivate { }; // static void initKWalletDir() // { // KGlobal::dirs()->addResourceType("kwallet", 0, "share/apps/kwallet"); // } Backend::Backend(const QString &name, bool isPath) - : d(0), + : d(nullptr), _name(name), _useNewHash(false), _ref(0), _cipherType(KWallet::BACKEND_CIPHER_UNKNOWN) { // initKWalletDir(); if (isPath) { _path = name; } else { _path = getSaveLocation() + QDir::separator() + _name + ".kwl"; } _open = false; } Backend::~Backend() { if (_open) { close(); } delete d; } QString Backend::getSaveLocation() { QString writeLocation = QStandardPaths::writableLocation(QStandardPaths::DataLocation); if (writeLocation.right(1) == QLatin1String("5")) { // HACK // setApplicationName("kwalletd5") yields the path ~/.local/share/kwalletd5 for the location where to store wallets // that is not desirable, as the 5 is present in the data folder's name // this workaround getts the right ~/.local/share/kwalletd location writeLocation = writeLocation.left(writeLocation.length() -1); } QDir writeDir(writeLocation); if (!writeDir.exists()) { if (!writeDir.mkpath(writeLocation)) { qFatal("Cannot create wallet save location!"); } } // qDebug() << "Using saveLocation " + writeLocation; return writeLocation; } void Backend::setCipherType(BackendCipherType ct) { // changing cipher type on already initialed wallets is not permitted assert(_cipherType == KWallet::BACKEND_CIPHER_UNKNOWN); _cipherType = ct; } static int password2PBKDF2_SHA512(const QByteArray &password, QByteArray &hash, const QByteArray &salt) { if (!gcry_check_version("1.5.0")) { printf("libcrypt version is too old \n"); return GPG_ERR_USER_2; } gcry_error_t error; bool static gcry_secmem_init = false; if (!gcry_secmem_init) { error = gcry_control(GCRYCTL_INIT_SECMEM, 32768, 0); if (error != 0) { qWarning() << "Can't get secure memory:" << error; return error; } gcry_secmem_init = true; } gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); error = gcry_kdf_derive(password.constData(), password.size(), GCRY_KDF_PBKDF2, GCRY_MD_SHA512, salt.data(), salt.size(), PBKDF2_SHA512_ITERATIONS, PBKDF2_SHA512_KEYSIZE, hash.data()); return error; } // this should be SHA-512 for release probably static int password2hash(const QByteArray &password, QByteArray &hash) { SHA1 sha; int shasz = sha.size() / 8; assert(shasz >= 20); QByteArray block1(shasz, 0); sha.process(password.data(), qMin(password.size(), 16)); // To make brute force take longer for (int i = 0; i < 2000; i++) { memcpy(block1.data(), sha.hash(), shasz); sha.reset(); sha.process(block1.data(), shasz); } sha.reset(); if (password.size() > 16) { sha.process(password.data() + 16, qMin(password.size() - 16, 16)); QByteArray block2(shasz, 0); // To make brute force take longer for (int i = 0; i < 2000; i++) { memcpy(block2.data(), sha.hash(), shasz); sha.reset(); sha.process(block2.data(), shasz); } sha.reset(); if (password.size() > 32) { sha.process(password.data() + 32, qMin(password.size() - 32, 16)); QByteArray block3(shasz, 0); // To make brute force take longer for (int i = 0; i < 2000; i++) { memcpy(block3.data(), sha.hash(), shasz); sha.reset(); sha.process(block3.data(), shasz); } sha.reset(); if (password.size() > 48) { sha.process(password.data() + 48, password.size() - 48); QByteArray block4(shasz, 0); // To make brute force take longer for (int i = 0; i < 2000; i++) { memcpy(block4.data(), sha.hash(), shasz); sha.reset(); sha.process(block4.data(), shasz); } sha.reset(); // split 14/14/14/14 hash.resize(56); memcpy(hash.data(), block1.data(), 14); memcpy(hash.data() + 14, block2.data(), 14); memcpy(hash.data() + 28, block3.data(), 14); memcpy(hash.data() + 42, block4.data(), 14); block4.fill(0); } else { // split 20/20/16 hash.resize(56); memcpy(hash.data(), block1.data(), 20); memcpy(hash.data() + 20, block2.data(), 20); memcpy(hash.data() + 40, block3.data(), 16); } block3.fill(0); } else { // split 20/20 hash.resize(40); memcpy(hash.data(), block1.data(), 20); memcpy(hash.data() + 20, block2.data(), 20); } block2.fill(0); } else { // entirely block1 hash.resize(20); memcpy(hash.data(), block1.data(), 20); } block1.fill(0); return 0; } int Backend::deref() { if (--_ref < 0) { qDebug() << "refCount negative!"; _ref = 0; } return _ref; } bool Backend::exists(const QString &wallet) { QString saveLocation = getSaveLocation(); QString path = saveLocation + '/' + wallet + QLatin1String(".kwl"); // Note: 60 bytes is presently the minimum size of a wallet file. // Anything smaller is junk. return QFile::exists(path) && QFileInfo(path).size() >= 60; } QString Backend::openRCToString(int rc) { switch (rc) { case -255: return i18n("Already open."); case -2: return i18n("Error opening file."); case -3: return i18n("Not a wallet file."); case -4: return i18n("Unsupported file format revision."); case -41: return QStringLiteral("Unknown cipher or hash"); //FIXME: use i18n after string freeze case -42: return i18n("Unknown encryption scheme."); case -43: return i18n("Corrupt file?"); case -8: return i18n("Error validating wallet integrity. Possibly corrupted."); case -5: case -7: case -9: return i18n("Read error - possibly incorrect password."); case -6: return i18n("Decryption error."); default: return QString(); } } int Backend::open(const QByteArray &password, WId w) { if (_open) { return -255; // already open } setPassword(password); return openInternal(w); } #ifdef HAVE_GPGMEPP int Backend::open(const GpgME::Key &key) { if (_open) { return -255; // already open } _gpgKey = key; return openInternal(); } #endif // HAVE_GPGMEPP int Backend::openPreHashed(const QByteArray &passwordHash) { if (_open) { return -255; // already open } // check the password hash for correct size (currently fixed) if (passwordHash.size() != 20 && passwordHash.size() != 40 && passwordHash.size() != 56) { return -42; // unsupported encryption scheme } _passhash = passwordHash; _newPassHash = passwordHash; _useNewHash = true;//Only new hash is supported return openInternal(); } int Backend::openInternal(WId w) { // No wallet existed. Let's create it. // Note: 60 bytes is presently the minimum size of a wallet file. // Anything smaller is junk and should be deleted. if (!QFile::exists(_path) || QFileInfo(_path).size() < 60) { QFile newfile(_path); if (!newfile.open(QIODevice::ReadWrite)) { return -2; // error opening file } newfile.close(); _open = true; if (sync(w) != 0) { return -2; } } QFile db(_path); if (!db.open(QIODevice::ReadOnly)) { return -2; // error opening file } char magicBuf[KWMAGIC_LEN]; db.read(magicBuf, KWMAGIC_LEN); if (memcmp(magicBuf, KWMAGIC, KWMAGIC_LEN) != 0) { return -3; // bad magic } db.read(magicBuf, 4); // First byte is major version, second byte is minor version if (magicBuf[0] != KWALLET_VERSION_MAJOR) { return -4; // unknown version } //0 has been the MINOR version until 4.13, from that point we use it to upgrade the hash if (magicBuf[1] == 1) { qDebug() << "Wallet new enough, using new hash"; swapToNewHash(); } else if (magicBuf[1] != 0) { qDebug() << "Wallet is old, sad panda :("; return -4; // unknown version } BackendPersistHandler *phandler = BackendPersistHandler::getPersistHandler(magicBuf); - if (0 == phandler) { + if (nullptr == phandler) { return -41; // unknown cipher or hash } int result = phandler->read(this, db, w); delete phandler; return result; } void Backend::swapToNewHash() { //Runtime error happened and we can't use the new hash if (!_useNewHash) { qDebug() << "Runtime error on the new hash"; return; } _passhash.fill(0);//Making sure the old passhash is not around in memory _passhash = _newPassHash;//Use the new hash, means the wallet is modern enough } QByteArray Backend::createAndSaveSalt(const QString &path) const { QFile saltFile(path); saltFile.remove(); if (!saltFile.open(QIODevice::WriteOnly)) { return QByteArray(); } saltFile.setPermissions(QFile::ReadUser | QFile::WriteUser); char *randomData = (char *) gcry_random_bytes(PBKDF2_SHA512_SALTSIZE, GCRY_STRONG_RANDOM); QByteArray salt(randomData, PBKDF2_SHA512_SALTSIZE); free(randomData); if (saltFile.write(salt) != PBKDF2_SHA512_SALTSIZE) { return QByteArray(); } saltFile.close(); return salt; } int Backend::sync(WId w) { if (!_open) { return -255; // not open yet } if (!QFile::exists(_path)) { return -3; // File does not exist } QSaveFile sf(_path); if (!sf.open(QIODevice::WriteOnly | QIODevice::Unbuffered)) { return -1; // error opening file } sf.setPermissions(QFile::ReadUser | QFile::WriteUser); if (sf.write(KWMAGIC, KWMAGIC_LEN) != KWMAGIC_LEN) { sf.cancelWriting(); return -4; // write error } // Write the version number QByteArray version(4, 0); version[0] = KWALLET_VERSION_MAJOR; if (_useNewHash) { version[1] = KWALLET_VERSION_MINOR; //Use the sync to update the hash to PBKDF2_SHA512 swapToNewHash(); } else { version[1] = 0; //was KWALLET_VERSION_MINOR before the new hash } BackendPersistHandler *phandler = BackendPersistHandler::getPersistHandler(_cipherType); - if (0 == phandler) { + if (nullptr == phandler) { return -4; // write error } int rc = phandler->write(this, sf, version, w); if (rc < 0) { // Oops! wallet file sync filed! Display a notification about that // TODO: change kwalletd status flags, when status flags will be implemented KNotification *notification = new KNotification(QStringLiteral("syncFailed")); notification->setText(i18n("Failed to sync wallet %1 to disk. Error codes are:\nRC %2\nSF %3. Please file a BUG report using this information to bugs.kde.org").arg(_name).arg(rc).arg(sf.errorString())); notification->sendEvent(); } delete phandler; return rc; } int Backend::close(bool save) { // save if requested if (save) { int rc = sync(0); if (rc != 0) { return rc; } } // do the actual close for (FolderMap::ConstIterator i = _entries.constBegin(); i != _entries.constEnd(); ++i) { for (EntryMap::ConstIterator j = i.value().constBegin(); j != i.value().constEnd(); ++j) { delete j.value(); } } _entries.clear(); // empty the password hash _passhash.fill(0); _newPassHash.fill(0); _open = false; return 0; } const QString &Backend::walletName() const { return _name; } bool Backend::isOpen() const { return _open; } QStringList Backend::folderList() const { return _entries.keys(); } QStringList Backend::entryList() const { return _entries[_folder].keys(); } Entry *Backend::readEntry(const QString &key) { - Entry *rc = 0L; + Entry *rc = nullptr; if (_open && hasEntry(key)) { rc = _entries[_folder][key]; } return rc; } QList Backend::readEntryList(const QString &key) { QList rc; if (!_open) { return rc; } QRegExp re(key, Qt::CaseSensitive, QRegExp::Wildcard); const EntryMap &map = _entries[_folder]; for (EntryMap::ConstIterator i = map.begin(); i != map.end(); ++i) { if (re.exactMatch(i.key())) { rc.append(i.value()); } } return rc; } bool Backend::createFolder(const QString &f) { if (_entries.contains(f)) { return false; } _entries.insert(f, EntryMap()); QCryptographicHash folderMd5(QCryptographicHash::Md5); folderMd5.addData(f.toUtf8()); _hashes.insert(MD5Digest(folderMd5.result()), QList()); return true; } int Backend::renameEntry(const QString &oldName, const QString &newName) { EntryMap &emap = _entries[_folder]; EntryMap::Iterator oi = emap.find(oldName); EntryMap::Iterator ni = emap.find(newName); if (oi != emap.end() && ni == emap.end()) { Entry *e = oi.value(); emap.erase(oi); emap[newName] = e; QCryptographicHash folderMd5(QCryptographicHash::Md5); folderMd5.addData(_folder.toUtf8()); HashMap::iterator i = _hashes.find(MD5Digest(folderMd5.result())); if (i != _hashes.end()) { QCryptographicHash oldMd5(QCryptographicHash::Md5), newMd5(QCryptographicHash::Md5); oldMd5.addData(oldName.toUtf8()); newMd5.addData(newName.toUtf8()); i.value().removeAll(MD5Digest(oldMd5.result())); i.value().append(MD5Digest(newMd5.result())); } return 0; } return -1; } void Backend::writeEntry(Entry *e) { if (!_open) { return; } if (!hasEntry(e->key())) { _entries[_folder][e->key()] = new Entry; } _entries[_folder][e->key()]->copy(e); QCryptographicHash folderMd5(QCryptographicHash::Md5); folderMd5.addData(_folder.toUtf8()); HashMap::iterator i = _hashes.find(MD5Digest(folderMd5.result())); if (i != _hashes.end()) { QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(e->key().toUtf8()); i.value().append(MD5Digest(md5.result())); } } bool Backend::hasEntry(const QString &key) const { return _entries.contains(_folder) && _entries[_folder].contains(key); } bool Backend::removeEntry(const QString &key) { if (!_open) { return false; } FolderMap::Iterator fi = _entries.find(_folder); EntryMap::Iterator ei = fi.value().find(key); if (fi != _entries.end() && ei != fi.value().end()) { delete ei.value(); fi.value().erase(ei); QCryptographicHash folderMd5(QCryptographicHash::Md5); folderMd5.addData(_folder.toUtf8()); HashMap::iterator i = _hashes.find(MD5Digest(folderMd5.result())); if (i != _hashes.end()) { QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(key.toUtf8()); i.value().removeAll(MD5Digest(md5.result())); } return true; } return false; } bool Backend::removeFolder(const QString &f) { if (!_open) { return false; } FolderMap::Iterator fi = _entries.find(f); if (fi != _entries.end()) { if (_folder == f) { _folder.clear(); } for (EntryMap::Iterator ei = fi.value().begin(); ei != fi.value().end(); ++ei) { delete ei.value(); } _entries.erase(fi); QCryptographicHash folderMd5(QCryptographicHash::Md5); folderMd5.addData(f.toUtf8()); _hashes.remove(MD5Digest(folderMd5.result())); return true; } return false; } bool Backend::folderDoesNotExist(const QString &folder) const { QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(folder.toUtf8()); return !_hashes.contains(MD5Digest(md5.result())); } bool Backend::entryDoesNotExist(const QString &folder, const QString &entry) const { QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(folder.toUtf8()); HashMap::const_iterator i = _hashes.find(MD5Digest(md5.result())); if (i != _hashes.end()) { md5.reset(); md5.addData(entry.toUtf8()); return !i.value().contains(MD5Digest(md5.result())); } return true; } void Backend::setPassword(const QByteArray &password) { _passhash.fill(0); // empty just in case BlowFish _bf; CipherBlockChain bf(&_bf); _passhash.resize(bf.keyLen() / 8); _newPassHash.resize(bf.keyLen() / 8); _newPassHash.fill(0); password2hash(password, _passhash); QByteArray salt; QFile saltFile(getSaveLocation() + QDir::separator() + _name + ".salt"); if (!saltFile.exists() || saltFile.size() == 0) { salt = createAndSaveSalt(saltFile.fileName()); } else { if (!saltFile.open(QIODevice::ReadOnly)) { salt = createAndSaveSalt(saltFile.fileName()); } else { salt = saltFile.readAll(); } } if (!salt.isEmpty() && password2PBKDF2_SHA512(password, _newPassHash, salt) == 0) { qDebug() << "Setting useNewHash to true"; _useNewHash = true; } } #ifdef HAVE_GPGMEPP const GpgME::Key &Backend::gpgKey() const { return _gpgKey; } #endif diff --git a/src/runtime/kwalletd/backend/sha1.cc b/src/runtime/kwalletd/backend/sha1.cc index cc754bf..ba265c3 100644 --- a/src/runtime/kwalletd/backend/sha1.cc +++ b/src/runtime/kwalletd/backend/sha1.cc @@ -1,338 +1,338 @@ /* This file is part of the KDE project Copyright (C) 2001 George Staikos Based heavily on SHA1 code from GPG 1.0.3 (C) 1998 FSF This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sha1.h" #include // DO NOT INCLUDE THIS. IT BREAKS KWALLET. // We need to live with -Wundef until someone really figures out the problem. //#include // for Q_BYTE_ORDER and friends #include #ifdef HAVE_SYS_BITYPES_H #include /* For uintXX_t on Tru64 */ #endif #ifdef HAVE_STDINT_H #include #endif #include // FIXME: this can be optimized to one instruction on most cpus. #define rol(x,y) ((x << y) | (x >> (32-y))) #define K1 0x5a827999L #define K2 0x6ed9eba1L #define K3 0x8f1bbcdcL #define K4 0xca62c1d6L #define F1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) #define F2(x,y,z) ( x ^ y ^ z ) #define F3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) #define F4(x,y,z) ( x ^ y ^ z ) #define M(i) ( tm = x[i&0x0f] ^ x[(i-14)&0x0f] \ ^ x[(i-8)&0x0f] ^ x[(i-3)&0x0f] \ , (x[i&0x0f] = (tm << 1) | (tm >> 31))) #define R(a,b,c,d,e,f,k,m) do { e += rol(a, 5) \ + f(b, c, d) \ + k \ + m; \ b = rol(b, 30); \ } while(0) SHA1::SHA1() { _hashlen = 160; _init = false; reset(); } int SHA1::reset() { _h0 = 0x67452301; _h1 = 0xefcdab89; _h2 = 0x98badcfe; _h3 = 0x10325476; _h4 = 0xc3d2e1f0; _nblocks = 0; _count = 0; memset(_buf, 0, 56); // clear the buffer _init = true; return 0; } int SHA1::size() const { return _hashlen; } SHA1::~SHA1() { } void SHA1::transform(void *data) { unsigned int a, b, c, d, e, tm; unsigned int x[16]; unsigned char *_data = (unsigned char *)data; a = _h0; b = _h1; c = _h2; d = _h3; e = _h4; #if Q_BYTE_ORDER == Q_BIG_ENDIAN memcpy(x, _data, 64); #else int i; unsigned char *p2; for (i = 0, p2 = (unsigned char *)x; i < 16; i++, p2 += 4) { p2[3] = *_data++; p2[2] = *_data++; p2[1] = *_data++; p2[0] = *_data++; } #endif R(a, b, c, d, e, F1, K1, x[ 0]); R(e, a, b, c, d, F1, K1, x[ 1]); R(d, e, a, b, c, F1, K1, x[ 2]); R(c, d, e, a, b, F1, K1, x[ 3]); R(b, c, d, e, a, F1, K1, x[ 4]); R(a, b, c, d, e, F1, K1, x[ 5]); R(e, a, b, c, d, F1, K1, x[ 6]); R(d, e, a, b, c, F1, K1, x[ 7]); R(c, d, e, a, b, F1, K1, x[ 8]); R(b, c, d, e, a, F1, K1, x[ 9]); R(a, b, c, d, e, F1, K1, x[10]); R(e, a, b, c, d, F1, K1, x[11]); R(d, e, a, b, c, F1, K1, x[12]); R(c, d, e, a, b, F1, K1, x[13]); R(b, c, d, e, a, F1, K1, x[14]); R(a, b, c, d, e, F1, K1, x[15]); R(e, a, b, c, d, F1, K1, M(16)); R(d, e, a, b, c, F1, K1, M(17)); R(c, d, e, a, b, F1, K1, M(18)); R(b, c, d, e, a, F1, K1, M(19)); R(a, b, c, d, e, F2, K2, M(20)); R(e, a, b, c, d, F2, K2, M(21)); R(d, e, a, b, c, F2, K2, M(22)); R(c, d, e, a, b, F2, K2, M(23)); R(b, c, d, e, a, F2, K2, M(24)); R(a, b, c, d, e, F2, K2, M(25)); R(e, a, b, c, d, F2, K2, M(26)); R(d, e, a, b, c, F2, K2, M(27)); R(c, d, e, a, b, F2, K2, M(28)); R(b, c, d, e, a, F2, K2, M(29)); R(a, b, c, d, e, F2, K2, M(30)); R(e, a, b, c, d, F2, K2, M(31)); R(d, e, a, b, c, F2, K2, M(32)); R(c, d, e, a, b, F2, K2, M(33)); R(b, c, d, e, a, F2, K2, M(34)); R(a, b, c, d, e, F2, K2, M(35)); R(e, a, b, c, d, F2, K2, M(36)); R(d, e, a, b, c, F2, K2, M(37)); R(c, d, e, a, b, F2, K2, M(38)); R(b, c, d, e, a, F2, K2, M(39)); R(a, b, c, d, e, F3, K3, M(40)); R(e, a, b, c, d, F3, K3, M(41)); R(d, e, a, b, c, F3, K3, M(42)); R(c, d, e, a, b, F3, K3, M(43)); R(b, c, d, e, a, F3, K3, M(44)); R(a, b, c, d, e, F3, K3, M(45)); R(e, a, b, c, d, F3, K3, M(46)); R(d, e, a, b, c, F3, K3, M(47)); R(c, d, e, a, b, F3, K3, M(48)); R(b, c, d, e, a, F3, K3, M(49)); R(a, b, c, d, e, F3, K3, M(50)); R(e, a, b, c, d, F3, K3, M(51)); R(d, e, a, b, c, F3, K3, M(52)); R(c, d, e, a, b, F3, K3, M(53)); R(b, c, d, e, a, F3, K3, M(54)); R(a, b, c, d, e, F3, K3, M(55)); R(e, a, b, c, d, F3, K3, M(56)); R(d, e, a, b, c, F3, K3, M(57)); R(c, d, e, a, b, F3, K3, M(58)); R(b, c, d, e, a, F3, K3, M(59)); R(a, b, c, d, e, F4, K4, M(60)); R(e, a, b, c, d, F4, K4, M(61)); R(d, e, a, b, c, F4, K4, M(62)); R(c, d, e, a, b, F4, K4, M(63)); R(b, c, d, e, a, F4, K4, M(64)); R(a, b, c, d, e, F4, K4, M(65)); R(e, a, b, c, d, F4, K4, M(66)); R(d, e, a, b, c, F4, K4, M(67)); R(c, d, e, a, b, F4, K4, M(68)); R(b, c, d, e, a, F4, K4, M(69)); R(a, b, c, d, e, F4, K4, M(70)); R(e, a, b, c, d, F4, K4, M(71)); R(d, e, a, b, c, F4, K4, M(72)); R(c, d, e, a, b, F4, K4, M(73)); R(b, c, d, e, a, F4, K4, M(74)); R(a, b, c, d, e, F4, K4, M(75)); R(e, a, b, c, d, F4, K4, M(76)); R(d, e, a, b, c, F4, K4, M(77)); R(c, d, e, a, b, F4, K4, M(78)); R(b, c, d, e, a, F4, K4, M(79)); _h0 += a; _h1 += b; _h2 += c; _h3 += d; _h4 += e; } bool SHA1::readyToGo() const { return _init; } int SHA1::process(const void *block, int len) { if (!_init) { return -1; } unsigned char *_block = (unsigned char *)block; int cnt = 0; // Flush the buffer before proceeding if (_count == 64) { transform(_buf); _count = 0; _nblocks++; } if (!_block) { return 0; } if (_count) { for (; len && _count < 64; --len, ++cnt) { _buf[_count++] = *_block++; } - process(0, 0); // flush the buffer if necessary + process(nullptr, 0); // flush the buffer if necessary if (!len) { return cnt; } } while (len >= 64) { transform(_block); _count = 0; _nblocks++; len -= 64; cnt += 64; _block += 64; } for (; len && _count < 64; --len, ++cnt) { _buf[_count++] = *_block++; } return cnt; } const unsigned char *SHA1::hash() { unsigned int t, msb, lsb; unsigned char *p; if (!_init) { return (unsigned char *)_buf; } - process(0, 0); + process(nullptr, 0); msb = 0; t = _nblocks; if ((lsb = t << 6) < t) { msb++; } msb += t >> 26; t = lsb; if ((lsb = t + _count) < t) { msb++; } t = lsb; if ((lsb = t << 3) < t) { msb++; } msb += t >> 29; _buf[_count++] = 0x80; if (_count < 56) { while (_count < 56) { _buf[_count++] = 0; } } else { while (_count < 64) { _buf[_count++] = 0; } - process(0, 0); + process(nullptr, 0); memset(_buf, 0, 56); } _buf[56] = msb >> 24; _buf[57] = msb >> 16; _buf[58] = msb >> 8; _buf[59] = msb; _buf[60] = lsb >> 24; _buf[61] = lsb >> 16; _buf[62] = lsb >> 8; _buf[63] = lsb; transform(_buf); p = _buf; #if Q_BYTE_ORDER == Q_BIG_ENDIAN #define X(a) do { *(uint32_t *)p = _h##a; p += 4; } while (0) #else #define X(a) do { *p++ = _h##a >> 24; *p++ = _h##a >> 16; \ *p++ = _h##a >> 8; *p++ = _h##a; } while (0) #endif X(0); X(1); X(2); X(3); X(4); #undef X _init = false; return (unsigned char *)_buf; } diff --git a/src/runtime/kwalletd/kbetterthankdialog.h b/src/runtime/kwalletd/kbetterthankdialog.h index 74bfba6..f5e1d99 100644 --- a/src/runtime/kwalletd/kbetterthankdialog.h +++ b/src/runtime/kwalletd/kbetterthankdialog.h @@ -1,46 +1,46 @@ /* This file is part of the KDE libraries Copyright (C) 2004 George Staikos This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KBETTERTHANKDIALOG_H #define KBETTERTHANKDIALOG_H #include #include "ui_kbetterthankdialogbase.h" class KBetterThanKDialog : public QDialog, private Ui_KBetterThanKDialogBase { Q_OBJECT public: - KBetterThanKDialog(QWidget *parent = 0); + KBetterThanKDialog(QWidget *parent = nullptr); void init(); void setLabel(const QString &label); protected Q_SLOTS: void accept() Q_DECL_OVERRIDE; void reject() Q_DECL_OVERRIDE; private Q_SLOTS: void allowOnceClicked(); void allowAlwaysClicked(); void denyClicked(); void denyForeverClicked(); }; #endif diff --git a/src/runtime/kwalletd/ktimeout.h b/src/runtime/kwalletd/ktimeout.h index e9322c9..149c923 100644 --- a/src/runtime/kwalletd/ktimeout.h +++ b/src/runtime/kwalletd/ktimeout.h @@ -1,52 +1,52 @@ /* This file is part of the KDE libraries Copyright (c) 2003 George Staikos This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KTIMEOUT_H_ #define _KTIMEOUT_H_ #include #include // @internal class KTimeout : public QObject { Q_OBJECT public: - KTimeout(QObject *parent = 0); + KTimeout(QObject *parent = nullptr); ~KTimeout(); Q_SIGNALS: void timedOut(int id); public Q_SLOTS: void resetTimer(int id, int timeout); void addTimer(int id, int timeout); void removeTimer(int id); void clear(); protected: void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE; private: QHash _timers; }; #endif diff --git a/src/runtime/kwalletd/kwalletd.cpp b/src/runtime/kwalletd/kwalletd.cpp index bb3f5f5..18ef9fa 100644 --- a/src/runtime/kwalletd/kwalletd.cpp +++ b/src/runtime/kwalletd/kwalletd.cpp @@ -1,1914 +1,1914 @@ /* This file is part of the KDE libraries Copyright (c) 2002-2004 George Staikos Copyright (c) 2008 Michael Leupold This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kwalletd.h" #include "kbetterthankdialog.h" #include "kwalletwizard.h" #ifdef HAVE_GPGMEPP #include "knewwalletdialog.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GPGMEPP #include #endif #include #include #include #include #include #include #include #include #include #include "kwalletadaptor.h" class KWalletTransaction { public: explicit KWalletTransaction(QDBusConnection conn) : tType(Unknown) , cancelled(false) , tId(nextTransactionId) , res(-1) , connection(conn) { nextTransactionId++; // make sure the id is never < 0 as that's used for the // error conditions. if (nextTransactionId < 0) { nextTransactionId = 0; } } ~KWalletTransaction() {} enum Type { Unknown, Open, ChangePassword, OpenFail, CloseCancelled }; Type tType; QString appid; qlonglong wId; QString wallet; QString service; bool cancelled; // set true if the client dies before open bool modal; bool isPath; int tId; // transaction id int res; QDBusMessage message; QDBusConnection connection; private: static int nextTransactionId; }; int KWalletTransaction::nextTransactionId = 0; KWalletD::KWalletD() - : QObject(0) + : QObject(nullptr) , _failed(0) , _syncTime(5000) - , _curtrans(0) + , _curtrans(nullptr) , _useGpg(false) { #ifdef HAVE_GPGMEPP _useGpg = true; #endif - srand(time(0)); + srand(time(nullptr)); _showingFailureNotify = false; _closeIdle = false; _idleTime = 0; connect( &_closeTimers, SIGNAL(timedOut(int)), this, SLOT(timedOutClose(int))); connect( &_syncTimers, SIGNAL(timedOut(int)), this, SLOT(timedOutSync(int))); (void)new KWalletAdaptor(this); // register services QDBusConnection::sessionBus().registerService( QStringLiteral("org.kde.kwalletd5")); QDBusConnection::sessionBus().registerObject( QStringLiteral("/modules/kwalletd5"), this); #ifdef Q_WS_X11 screensaver = 0; #endif reconfigure(); // KGlobal::dirs()->addResourceType("kwallet", 0, "share/apps/kwallet"); _dw = new KDirWatch(this); _dw->setObjectName(QStringLiteral("KWallet Directory Watcher")); // _dw->addDir(KGlobal::dirs()->saveLocation("kwallet")); _dw->addDir(KWallet::Backend::getSaveLocation()); _dw->startScan(true); connect(_dw, SIGNAL(dirty(QString)), this, SLOT(emitWalletListDirty())); connect(_dw, &KDirWatch::deleted, this, &KWalletD::emitWalletListDirty); _serviceWatcher.setWatchMode(QDBusServiceWatcher::WatchForOwnerChange); connect(&_serviceWatcher, SIGNAL(serviceOwnerChanged(QString, QString, QString)), this, SLOT(slotServiceOwnerChanged(QString, QString, QString))); } KWalletD::~KWalletD() { #ifdef Q_WS_X11 delete screensaver; screensaver = 0; #endif closeAllWallets(); qDeleteAll(_transactions); } #ifdef Q_WS_X11 void KWalletD::connectToScreenSaver() { screensaver = new QDBusInterface("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver"); if (!screensaver->isValid()) { qDebug() << "Service org.freedesktop.ScreenSaver not found. Retrying in 10 seconds..."; // keep attempting every 10 seconds QTimer::singleShot(10000, this, SLOT(connectToScreenSaver())); } else { connect(screensaver, SIGNAL(ActiveChanged(bool)), SLOT(screenSaverChanged(bool))); qDebug() << "connected to screen saver service."; } } #endif int KWalletD::generateHandle() { int rc; // ASSUMPTION: RAND_MAX is fairly large. do { rc = rand(); } while (_wallets.contains(rc) || rc == 0); return rc; } QPair KWalletD::findWallet( const QString& walletName) const { Wallets::const_iterator it = _wallets.constBegin(); const Wallets::const_iterator end = _wallets.constEnd(); for (; it != end; ++it) { if (it.value()->walletName() == walletName) { return qMakePair(it.key(), it.value()); } } - return qMakePair(-1, static_cast(0)); + return qMakePair(-1, static_cast(nullptr)); } bool KWalletD::_processing = false; void KWalletD::processTransactions() { if (_processing) { return; } _processing = true; // Process remaining transactions while (!_transactions.isEmpty()) { _curtrans = _transactions.takeFirst(); int res; assert(_curtrans->tType != KWalletTransaction::Unknown); switch (_curtrans->tType) { case KWalletTransaction::Open: res = doTransactionOpen(_curtrans->appid, _curtrans->wallet, _curtrans->isPath, _curtrans->wId, _curtrans->modal, _curtrans->service); // multiple requests from the same client // should not produce multiple password // dialogs on a failure if (res < 0) { QList::iterator it; for (it = _transactions.begin(); it != _transactions.end(); ++it) { KWalletTransaction* x = *it; if (_curtrans->appid == x->appid && x->tType == KWalletTransaction::Open && x->wallet == _curtrans->wallet && x->wId == _curtrans->wId) { x->tType = KWalletTransaction::OpenFail; } } } else if (_curtrans->cancelled) { // the wallet opened successfully but the application // opening exited/crashed while the dialog was still shown. KWalletTransaction* _xact = new KWalletTransaction(_curtrans->connection); _xact->tType = KWalletTransaction::CloseCancelled; _xact->appid = _curtrans->appid; _xact->wallet = _curtrans->wallet; _xact->service = _curtrans->service; _transactions.append(_xact); } // emit the AsyncOpened signal as a reply _curtrans->res = res; emit walletAsyncOpened(_curtrans->tId, res); break; case KWalletTransaction::OpenFail: // emit the AsyncOpened signal with an invalid handle _curtrans->res = -1; emit walletAsyncOpened(_curtrans->tId, -1); break; case KWalletTransaction::ChangePassword: doTransactionChangePassword( _curtrans->appid, _curtrans->wallet, _curtrans->wId); break; case KWalletTransaction::CloseCancelled: doTransactionOpenCancelled( _curtrans->appid, _curtrans->wallet, _curtrans->service); break; case KWalletTransaction::Unknown: break; default: break; } // send delayed dbus message reply to the caller if (_curtrans->message.type() != QDBusMessage::InvalidMessage) { if (_curtrans->connection.isConnected()) { QDBusMessage reply = _curtrans->message.createReply(); reply << _curtrans->res; _curtrans->connection.send(reply); } } delete _curtrans; - _curtrans = 0; + _curtrans = nullptr; } _processing = false; } int KWalletD::openPath( const QString& path, qlonglong wId, const QString& appid) { int tId = openPathAsync(path, wId, appid, false); if (tId < 0) { return tId; } // NOTE the real return value will be sent by the dbusmessage delayed // reply return 0; // wait for the open-transaction to be processed // KWalletOpenLoop loop(this); // return loop.waitForAsyncOpen(tId); } int KWalletD::open(const QString& wallet, qlonglong wId, const QString& appid) { if (!_enabled) { // guard return -1; } if (!QRegExp(QString::fromLatin1("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\." "\\+\\_\\s]+$")).exactMatch(wallet)) { return -1; } KWalletTransaction* xact = new KWalletTransaction(connection()); _transactions.append(xact); message().setDelayedReply(true); xact->message = message(); xact->appid = appid; xact->wallet = wallet; xact->wId = wId; xact->modal = true; // mark dialogs as modal, the app has blocking wait xact->tType = KWalletTransaction::Open; xact->isPath = false; QTimer::singleShot(0, this, SLOT(processTransactions())); checkActiveDialog(); // NOTE the real return value will be sent by the dbusmessage delayed // reply return 0; } int KWalletD::openAsync(const QString& wallet, qlonglong wId, const QString& appid, bool handleSession) { if (!_enabled) { // guard return -1; } if (!QRegExp(QString::fromLatin1("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\." "\\+\\_\\s]+$")).exactMatch(wallet)) { return -1; } KWalletTransaction* xact = new KWalletTransaction(connection()); _transactions.append(xact); xact->appid = appid; xact->wallet = wallet; xact->wId = wId; xact->modal = true; // mark dialogs as modal, the app has blocking wait xact->tType = KWalletTransaction::Open; xact->isPath = false; if (handleSession) { qDebug() << "openAsync for " << message().service(); _serviceWatcher.setConnection(connection()); _serviceWatcher.addWatchedService(message().service()); xact->service = message().service(); } QTimer::singleShot(0, this, SLOT(processTransactions())); checkActiveDialog(); // opening is in progress. return the transaction number return xact->tId; } int KWalletD::openPathAsync(const QString& path, qlonglong wId, const QString& appid, bool handleSession) { if (!_enabled) { // gaurd return -1; } KWalletTransaction* xact = new KWalletTransaction(connection()); _transactions.append(xact); xact->appid = appid; xact->wallet = path; xact->wId = wId; xact->modal = true; xact->tType = KWalletTransaction::Open; xact->isPath = true; if (handleSession) { qDebug() << "openPathAsync " << message().service(); _serviceWatcher.setConnection(connection()); _serviceWatcher.addWatchedService(message().service()); xact->service = message().service(); } QTimer::singleShot(0, this, SLOT(processTransactions())); checkActiveDialog(); // opening is in progress. return the transaction number return xact->tId; } // Sets up a dialog that will be shown by kwallet. void KWalletD::setupDialog( QWidget* dialog, WId wId, const QString& appid, bool modal) { if (wId != 0) { KWindowSystem::setMainWindow( dialog, wId); // correct, set dialog parent } else { if (appid.isEmpty()) { qWarning() << "Using kwallet without parent window!"; } else { qWarning() << "Application '" << appid << "' using kwallet without parent window!"; } // allow dialog activation even if it interrupts, better than trying // hacks // with keeping the dialog on top or on all desktops // KF5 FIXME what should we use now instead of this: // kapp->updateUserTimestamp(); } if (modal) { KWindowSystem::setState(dialog->winId(), NET::Modal); } else { KWindowSystem::clearState(dialog->winId(), NET::Modal); } activeDialog = dialog; } // If there's a dialog already open and another application tries some // operation that'd lead to // opening a dialog, that application will be blocked by this dialog. A proper // solution would // be to set the second application's window also as a parent for the active // dialog, so that // KWin properly handles focus changes and so on, but there's currently no // support for multiple // dialog parents. In the absence of this support, we use all kinds of bad // hacks to make // sure the user doesn't overlook the active dialog. void KWalletD::checkActiveDialog() { if (!activeDialog) { return; } // KF5 FIXME what should we use now instead of this: // kapp->updateUserTimestamp(); activeDialog->show(); WId window = activeDialog->winId(); KWindowSystem::setState(window, NET::KeepAbove); KWindowSystem::setOnAllDesktops(window, true); KWindowSystem::forceActiveWindow(window); KWindowSystem::raiseWindow(window); } int KWalletD::doTransactionOpen(const QString& appid, const QString& wallet, bool isPath, qlonglong wId, bool modal, const QString& service) { if (_firstUse && !isPath) { // if the user specifies a wallet name, the use it as the default // wallet name if (wallet != KWallet::Wallet::LocalWallet()) { KConfig kwalletrc(QStringLiteral("kwalletrc")); KConfigGroup cfg(&kwalletrc, "Wallet"); cfg.writeEntry("Default Wallet", wallet); } if (wallets().contains(KWallet::Wallet::LocalWallet())) { KConfig kwalletrc(QStringLiteral("kwalletrc")); KConfigGroup cfg(&kwalletrc, "Wallet"); _firstUse = false; cfg.writeEntry("First Use", false); } // else { // // First use wizard // // TODO GPG adjust new smartcard options gathered by // the wizard // QPointer wiz = new KWalletWizard(0); // wiz->setWindowTitle(i18n("KDE Wallet Service")); // setupDialog(wiz, (WId)wId, appid, modal); // int rc = wiz->exec(); // if (rc == QDialog::Accepted && wiz) { // bool useWallet = wiz->field("useWallet").toBool(); // KConfig kwalletrc("kwalletrc"); // KConfigGroup cfg(&kwalletrc, "Wallet"); // cfg.writeEntry("First Use", false); // cfg.writeEntry("Enabled", useWallet); // cfg.writeEntry("Close When Idle", // wiz->field("closeWhenIdle").toBool()); // cfg.writeEntry("Use One Wallet", // !wiz->field("networkWallet").toBool()); // cfg.sync(); // reconfigure(); // // if (!useWallet) { // delete wiz; // return -1; // } // // // Create the wallet // // TODO GPG select the correct wallet type upon // cretion (GPG or blowfish based) // KWallet::Backend *b = new // KWallet::Backend(KWallet::Wallet::LocalWallet()); // #ifdef HAVE_GPGMEPP // if (wiz->field("useBlowfish").toBool()) { // b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH); // #endif // QString pass = wiz->field("pass1").toString(); // QByteArray p(pass.toUtf8(), pass.length()); // b->open(p); // p.fill(0); // #ifdef HAVE_GPGMEPP // } else { // assert(wiz->field("useGpg").toBool()); // b->setCipherType(KWallet::BACKEND_CIPHER_GPG); // b->open(wiz->gpgKey()); // } // #endif // b->createFolder(KWallet::Wallet::PasswordFolder()); // b->createFolder(KWallet::Wallet::FormDataFolder()); // b->close(true); // delete b; // delete wiz; // } else { // delete wiz; // return -1; // } // } } int rc = internalOpen(appid, wallet, isPath, WId(wId), modal, service); return rc; } int KWalletD::internalOpen(const QString& appid, const QString& wallet, bool isPath, WId w, bool modal, const QString& service) { bool brandNew = false; QString thisApp; if (appid.isEmpty()) { thisApp = QStringLiteral("KDE System"); } else { thisApp = appid; } if (implicitDeny(wallet, thisApp)) { return -1; } QPair walletInfo = findWallet(wallet); int rc = walletInfo.first; if (rc == -1) { if (_wallets.count() > 20) { qDebug() << "Too many wallets open."; return -1; } KWallet::Backend* b = new KWallet::Backend(wallet, isPath); QString password; bool emptyPass = false; if ((isPath && QFile::exists(wallet)) || (!isPath && KWallet::Backend::exists(wallet))) { // this open attempt will set wallet type from the file header, // even if password is needed int pwless = b->open(QByteArray(), w); #ifdef HAVE_GPGMEPP assert(b->cipherType() != KWallet::BACKEND_CIPHER_UNKNOWN); if (b->cipherType() == KWallet::BACKEND_CIPHER_GPG) { // GPG based wallets do not prompt for password here. Instead, // GPG should already have popped pinentry utility for wallet // decryption if (!b->isOpen()) { // for some reason, GPG operation failed delete b; return -1; } emptyPass = true; } else { #endif if (0 != pwless || !b->isOpen()) { if (pwless == 0) { // release, start anew delete b; b = new KWallet::Backend(wallet, isPath); } KPasswordDialog* kpd = new KPasswordDialog(); if (appid.isEmpty()) { kpd->setPrompt( i18n("KDE has requested to open the wallet " "'%1'. Please enter the password for " "this wallet below.", wallet.toHtmlEscaped())); } else { kpd->setPrompt(i18n( "The application '%1' has requested " "to open the wallet '%2'. Please enter " "the password for this wallet below.", appid.toHtmlEscaped(), wallet.toHtmlEscaped())); } brandNew = false; // don't use KStdGuiItem::open() here which has trailing // ellipsis! // KF5 FIXME what should we use now instead of this: // kpd->setButtonGuiItem(KDialog::Ok,KGuiItem( // i18n( "&Open" ), "wallet-open")); kpd->setWindowTitle(i18n("KDE Wallet Service")); kpd->setPixmap( KIconLoader::global()->loadIcon(QStringLiteral("kwalletmanager"), KIconLoader::Desktop, KIconLoader::SizeHuge)); if (w != KWindowSystem::activeWindow() && w != 0L) { // If the dialog is modal to a minimized window it // might not be visible // (but still blocking the calling application). // Notify the user about // the request to open the wallet. KNotification* notification = new KNotification( QStringLiteral("needsPassword"), kpd, KNotification::Persistent | KNotification::CloseWhenWidgetActivated); QStringList actions; if (appid.isEmpty()) { notification->setText( i18n("An application has requested to open a " "wallet (%1).", wallet.toHtmlEscaped())); actions.append( i18nc("Text of a button for switching to the " "(unnamed) application " "requesting a password", "Switch there")); } else { notification->setText( i18n("%1 has requested to open a " "wallet (%2).", appid.toHtmlEscaped(), wallet.toHtmlEscaped())); actions.append( i18nc("Text of a button for switching to the " "application requesting " "a password", "Switch to %1", appid.toHtmlEscaped())); } notification->setActions(actions); connect(notification, SIGNAL(action1Activated()), this, SLOT(activatePasswordDialog())); notification->sendEvent(); } while (!b->isOpen()) { setupDialog(kpd, w, appid, modal); if (kpd->exec() == QDialog::Accepted) { password = kpd->password(); int rc = b->open(password.toUtf8()); if (!b->isOpen()) { kpd->setPrompt(i18n( "Error opening the wallet " "'%1'. Please try again.
(Error code %2: %3)
", wallet.toHtmlEscaped(), rc, KWallet::Backend::openRCToString(rc))); kpd->setPassword(QLatin1String("")); } } else { break; } } delete kpd; } else { emptyPass = true; } #ifdef HAVE_GPGMEPP } #endif } else { brandNew = true; #ifdef HAVE_GPGMEPP // prompt the user for the new wallet format here KWallet::BackendCipherType newWalletType = KWallet::BACKEND_CIPHER_UNKNOWN; std::shared_ptr newWalletDlg( new KWallet::KNewWalletDialog( appid, wallet, QWidget::find(w))); GpgME::Key gpgKey; setupDialog(newWalletDlg.get(), (WId)w, appid, true); if (newWalletDlg->exec() == QDialog::Accepted) { newWalletType = newWalletDlg->isBlowfish() ? KWallet::BACKEND_CIPHER_BLOWFISH : KWallet::BACKEND_CIPHER_GPG; gpgKey = newWalletDlg->gpgKey(); } else { // user cancelled the dialog box delete b; return -1; } if (newWalletType == KWallet::BACKEND_CIPHER_GPG) { b->setCipherType(newWalletType); b->open(gpgKey); } else if (newWalletType == KWallet::BACKEND_CIPHER_BLOWFISH) { #endif // HAVE_GPGMEPP b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH); KNewPasswordDialog* kpd = new KNewPasswordDialog(); KColorScheme colorScheme(QPalette::Active, KColorScheme::View); kpd->setBackgroundWarningColor(colorScheme.background(KColorScheme::NegativeBackground).color()); if (wallet == KWallet::Wallet::LocalWallet() || wallet == KWallet::Wallet::NetworkWallet()) { // Auto create these wallets. if (appid.isEmpty()) { kpd->setPrompt( i18n("KDE has requested to open the wallet. This " "is used to store sensitive data in a " "secure fashion. Please enter a password to " "use with this wallet or click cancel to " "deny the application's request.")); } else { kpd->setPrompt( i18n("The application '%1' has " "requested to open the KDE wallet. This is " "used to store sensitive data in a secure " "fashion. Please enter a password to use " "with this wallet or click cancel to deny " "the application's request.", appid.toHtmlEscaped())); } } else { if (appid.length() == 0) { kpd->setPrompt( i18n("KDE has requested to create a new " "wallet named '%1'. Please choose a " "password for this wallet, or cancel to " "deny the application's request.", wallet.toHtmlEscaped())); } else { kpd->setPrompt(i18n( "The application '%1' has requested " "to create a new wallet named '%2'. " "Please choose a password for this wallet, or " "cancel to deny the application's request.", appid.toHtmlEscaped(), wallet.toHtmlEscaped())); } } kpd->setWindowTitle(i18n("KDE Wallet Service")); // KF5 FIXME what should we use now instead of this: // kpd->setButtonGuiItem(KDialog::Ok,KGuiItem(i18n("C&reate"),"document-new")); kpd->setPixmap( KIconLoader::global()->loadIcon(QStringLiteral("kwalletmanager"), KIconLoader::Desktop, KIconLoader::SizeHuge)); while (!b->isOpen()) { setupDialog(kpd, w, appid, modal); if (kpd->exec() == QDialog::Accepted) { password = kpd->password(); int rc = b->open(password.toUtf8()); if (!b->isOpen()) { kpd->setPrompt( i18n("Error opening the wallet " "'%1'. Please try again.
(Error code %2: %3)
", wallet.toHtmlEscaped(), rc, KWallet::Backend::openRCToString(rc))); } } else { break; } } delete kpd; #ifdef HAVE_GPGMEPP } #endif } if ((b->cipherType() == KWallet::BACKEND_CIPHER_BLOWFISH) && !emptyPass && (password.isNull() || !b->isOpen())) { delete b; return -1; } if (emptyPass && !isAuthorizedApp(appid, wallet, w)) { delete b; return -1; } _wallets.insert(rc = generateHandle(), b); _sessions.addSession(appid, service, rc); _syncTimers.addTimer(rc, _syncTime); if (brandNew) { createFolder(rc, KWallet::Wallet::PasswordFolder(), appid); createFolder(rc, KWallet::Wallet::FormDataFolder(), appid); } b->ref(); if (_closeIdle) { _closeTimers.addTimer(rc, _idleTime); } if (brandNew) { emit walletCreated(wallet); } emit walletOpened(wallet); if (_wallets.count() == 1 && _launchManager) { KToolInvocation::startServiceByDesktopName( QStringLiteral("kwalletmanager-kwalletd")); } } else { // prematurely add a reference so that the wallet does not close while // the // authorization dialog is being shown. walletInfo.second->ref(); bool isAuthorized = _sessions.hasSession(appid, rc) || isAuthorizedApp(appid, wallet, w); // as the wallet might have been forcefully closed, find it again to // make sure it's // still available (isAuthorizedApp might show a dialog). walletInfo = findWallet(wallet); if (!isAuthorized) { if (walletInfo.first != -1) { walletInfo.second->deref(); // check if the wallet should be closed now. internalClose(walletInfo.second, walletInfo.first, false); } return -1; } else { if (walletInfo.first != -1) { _sessions.addSession(appid, service, rc); } else { // wallet was forcefully closed. return -1; } } } return rc; } bool KWalletD::isAuthorizedApp( const QString& appid, const QString& wallet, WId w) { if (!_openPrompt) { return true; } int response = 0; QString thisApp; if (appid.isEmpty()) { thisApp = QStringLiteral("KDE System"); } else { thisApp = appid; } if (!implicitAllow(wallet, thisApp)) { KConfigGroup cfg = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Allow"); if (!cfg.isEntryImmutable(wallet)) { KBetterThanKDialog* dialog = new KBetterThanKDialog; dialog->setWindowTitle(i18n("KDE Wallet Service")); if (appid.isEmpty()) { dialog->setLabel(i18n("KDE has requested access to the " "open wallet '%1'.", wallet.toHtmlEscaped())); } else { dialog->setLabel( i18n("The application '%1' has requested " "access to the open wallet '%2'.", appid.toHtmlEscaped(), wallet.toHtmlEscaped())); } setupDialog(dialog, w, appid, false); response = dialog->exec(); delete dialog; } } if (response == 0 || response == 1) { if (response == 1) { KConfigGroup cfg = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Allow"); QStringList apps = cfg.readEntry(wallet, QStringList()); if (!apps.contains(thisApp)) { if (cfg.isEntryImmutable(wallet)) { return false; } apps += thisApp; _implicitAllowMap[wallet] += thisApp; cfg.writeEntry(wallet, apps); cfg.sync(); } } } else if (response == 3) { KConfigGroup cfg = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Deny"); QStringList apps = cfg.readEntry(wallet, QStringList()); if (!apps.contains(thisApp)) { apps += thisApp; _implicitDenyMap[wallet] += thisApp; cfg.writeEntry(wallet, apps); cfg.sync(); } return false; } else { return false; } return true; } int KWalletD::deleteWallet(const QString& wallet) { int result = -1; QString path = KWallet::Backend::getSaveLocation() + "/" + wallet + ".kwl"; QString pathSalt = KWallet::Backend::getSaveLocation() + "/" + wallet + ".salt"; if (QFile::exists(path)) { const QPair walletInfo = findWallet(wallet); internalClose(walletInfo.second, walletInfo.first, true); QFile::remove(path); emit walletDeleted(wallet); // also delete access control entries KConfigGroup cfgAllow = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Allow"); cfgAllow.deleteEntry(wallet); KConfigGroup cfgDeny = KSharedConfig::openConfig(QStringLiteral("kwalletrc"))->group("Auto Deny"); cfgDeny.deleteEntry(wallet); if (QFile::exists(pathSalt)) { QFile::remove(pathSalt); } result = 0; } return result; } void KWalletD::changePassword( const QString& wallet, qlonglong wId, const QString& appid) { KWalletTransaction* xact = new KWalletTransaction(connection()); message().setDelayedReply(true); xact->message = message(); // TODO GPG this shouldn't be allowed on a GPG managed wallet; a warning // should be displayed about this xact->appid = appid; xact->wallet = wallet; xact->wId = wId; xact->modal = false; xact->tType = KWalletTransaction::ChangePassword; _transactions.append(xact); QTimer::singleShot(0, this, SLOT(processTransactions())); checkActiveDialog(); checkActiveDialog(); } void KWalletD::initiateSync(int handle) { // add a timer and reset it right away _syncTimers.addTimer(handle, _syncTime); _syncTimers.resetTimer(handle, _syncTime); } void KWalletD::doTransactionChangePassword( const QString& appid, const QString& wallet, qlonglong wId) { const QPair walletInfo = findWallet(wallet); int handle = walletInfo.first; KWallet::Backend* w = walletInfo.second; bool reclose = false; if (!w) { handle = doTransactionOpen(appid, wallet, false, wId, false, QLatin1String("")); if (-1 == handle) { KMessageBox::sorryWId((WId)wId, i18n("Unable to open wallet. The wallet must be opened in " "order to change the password."), i18n("KDE Wallet Service")); return; } w = _wallets.value(handle); reclose = true; } assert(w); #ifdef HAVE_GPGMEPP if (w->cipherType() == KWallet::BACKEND_CIPHER_GPG) { QString keyID = w->gpgKey().shortKeyID(); assert(!keyID.isNull()); KMessageBox::errorWId( (WId)wId, i18n("The %1 wallet is encrypted using GPG " "key %2. Please use GPG tools (such " "as kleopatra) to change the passphrase " "associated to that key.", wallet.toHtmlEscaped(), keyID)); } else { #endif QPointer kpd = new KNewPasswordDialog(); kpd->setPrompt(i18n("Please choose a new password for the wallet " "'%1'.", wallet.toHtmlEscaped())); kpd->setWindowTitle(i18n("KDE Wallet Service")); kpd->setAllowEmptyPasswords(true); KColorScheme colorScheme(QPalette::Active, KColorScheme::View); kpd->setBackgroundWarningColor(colorScheme.background(KColorScheme::NegativeBackground).color()); setupDialog(kpd, (WId)wId, appid, false); if (kpd->exec() == QDialog::Accepted && kpd) { QString p = kpd->password(); if (!p.isNull()) { w->setPassword(p.toUtf8()); int rc = w->close(true); if (rc < 0) { KMessageBox::sorryWId((WId)wId, i18n("Error re-encrypting the wallet. Password was " "not changed."), i18n("KDE Wallet Service")); reclose = true; } else { rc = w->open(p.toUtf8()); if (rc < 0) { KMessageBox::sorryWId((WId)wId, i18n("Error reopening the wallet. Data may be " "lost."), i18n("KDE Wallet Service")); reclose = true; } } } } delete kpd; #ifdef HAVE_GPGMEPP } #endif if (reclose) { internalClose(w, handle, true); } } int KWalletD::close(const QString& wallet, bool force) { const QPair walletInfo = findWallet(wallet); int handle = walletInfo.first; KWallet::Backend* w = walletInfo.second; return internalClose(w, handle, force); } int KWalletD::internalClose(KWallet::Backend* const w, const int handle, const bool force, const bool saveBeforeClose) { if (w) { const QString& wallet = w->walletName(); if ((w->refCount() == 0 && !_leaveOpen) || force) { // this is only a safety measure. sessions should be gone already. _sessions.removeAllSessions(handle); if (_closeIdle) { _closeTimers.removeTimer(handle); } _syncTimers.removeTimer(handle); _wallets.remove(handle); w->close(saveBeforeClose); doCloseSignals(handle, wallet); delete w; return 0; } return 1; } return -1; } int KWalletD::close(int handle, bool force, const QString& appid) { KWallet::Backend* w = _wallets.value(handle); if (w) { if (_sessions.hasSession(appid, handle)) { // remove one handle for the application bool removed = _sessions.removeSession(appid, message().service(), handle); // alternatively try sessionless if (removed || _sessions.removeSession(appid, QLatin1String(""), handle)) { w->deref(); } return internalClose(w, handle, force); } return 1; // not closed, handle unknown } return -1; // not open to begin with, or other error } bool KWalletD::isOpen(const QString& wallet) { const QPair walletInfo = findWallet(wallet); - return walletInfo.second != 0; + return walletInfo.second != nullptr; } bool KWalletD::isOpen(int handle) { if (handle == 0) { return false; } KWallet::Backend* rc = _wallets.value(handle); - if (rc == 0 && ++_failed > 5) { + if (rc == nullptr && ++_failed > 5) { _failed = 0; QTimer::singleShot(0, this, SLOT(notifyFailures())); } - else if (rc != 0) { + else if (rc != nullptr) { _failed = 0; } - return rc != 0; + return rc != nullptr; } QStringList KWalletD::wallets() const { QString path = KWallet::Backend::getSaveLocation(); QDir dir(path, QStringLiteral("*.kwl")); QStringList rc; dir.setFilter(QDir::Files | QDir::Hidden); foreach (const QFileInfo& fi, dir.entryInfoList()) { QString fn = fi.fileName(); if (fn.endsWith(QLatin1String(".kwl"))) { fn.truncate(fn.length() - 4); } rc += fn; } return rc; } void KWalletD::sync(int handle, const QString& appid) { KWallet::Backend* b; // get the wallet and check if we have a password for it (safety measure) if ((b = getWallet(appid, handle))) { QString wallet = b->walletName(); b->sync(0); } } void KWalletD::timedOutSync(int handle) { _syncTimers.removeTimer(handle); if (_wallets.contains(handle) && _wallets[handle]) { _wallets[handle]->sync(0); } else { qDebug("wallet not found for sync!"); } } void KWalletD::doTransactionOpenCancelled( const QString& appid, const QString& wallet, const QString& service) { // there will only be one session left to remove - all others // have already been removed in slotServiceOwnerChanged and all // transactions for opening new sessions have been deleted. if (!_sessions.hasSession(appid)) { return; } const QPair walletInfo = findWallet(wallet); int handle = walletInfo.first; KWallet::Backend* b = walletInfo.second; if (handle != -1 && b) { b->deref(); internalClose(b, handle, false); } // close the session in case the wallet hasn't been closed yet _sessions.removeSession(appid, service, handle); } QStringList KWalletD::folderList(int handle, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { return b->folderList(); } return QStringList(); } bool KWalletD::hasFolder(int handle, const QString& f, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { return b->hasFolder(f); } return false; } bool KWalletD::removeFolder( int handle, const QString& f, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { bool rc = b->removeFolder(f); initiateSync(handle); emit folderListUpdated(b->walletName()); return rc; } return false; } bool KWalletD::createFolder( int handle, const QString& f, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { bool rc = b->createFolder(f); initiateSync(handle); emit folderListUpdated(b->walletName()); return rc; } return false; } QByteArray KWalletD::readMap(int handle, const QString& folder, const QString& key, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); KWallet::Entry* e = b->readEntry(key); if (e && e->type() == KWallet::Wallet::Map) { return e->map(); } } return QByteArray(); } QVariantMap KWalletD::readMapList(int handle, const QString& folder, const QString& key, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); QVariantMap rc; foreach (KWallet::Entry* entry, b->readEntryList(key)) { if (entry->type() == KWallet::Wallet::Map) { rc.insert(entry->key(), entry->map()); } } return rc; } return QVariantMap(); } QByteArray KWalletD::readEntry(int handle, const QString& folder, const QString& key, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); KWallet::Entry* e = b->readEntry(key); if (e) { return e->value(); } } return QByteArray(); } QVariantMap KWalletD::readEntryList(int handle, const QString& folder, const QString& key, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); QVariantMap rc; foreach (KWallet::Entry* entry, b->readEntryList(key)) { rc.insert(entry->key(), entry->value()); } return rc; } return QVariantMap(); } QStringList KWalletD::entryList( int handle, const QString& folder, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); return b->entryList(); } return QStringList(); } QString KWalletD::readPassword(int handle, const QString& folder, const QString& key, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); KWallet::Entry* e = b->readEntry(key); if (e && e->type() == KWallet::Wallet::Password) { return e->password(); } } return QString(); } QVariantMap KWalletD::readPasswordList(int handle, const QString& folder, const QString& key, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); QVariantMap rc; foreach (KWallet::Entry* entry, b->readEntryList(key)) { if (entry->type() == KWallet::Wallet::Password) { rc.insert(entry->key(), entry->password()); } } return rc; } return QVariantMap(); } int KWalletD::writeMap(int handle, const QString& folder, const QString& key, const QByteArray& value, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); KWallet::Entry e; e.setKey(key); e.setValue(value); e.setType(KWallet::Wallet::Map); b->writeEntry(&e); initiateSync(handle); emitFolderUpdated(b->walletName(), folder); return 0; } return -1; } int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, const QByteArray& value, int entryType, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); KWallet::Entry e; e.setKey(key); e.setValue(value); e.setType(KWallet::Wallet::EntryType(entryType)); b->writeEntry(&e); initiateSync(handle); emitFolderUpdated(b->walletName(), folder); return 0; } return -1; } int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, const QByteArray& value, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); KWallet::Entry e; e.setKey(key); e.setValue(value); e.setType(KWallet::Wallet::Stream); b->writeEntry(&e); initiateSync(handle); emitFolderUpdated(b->walletName(), folder); return 0; } return -1; } int KWalletD::writePassword(int handle, const QString& folder, const QString& key, const QString& value, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); KWallet::Entry e; e.setKey(key); e.setValue(value); e.setType(KWallet::Wallet::Password); b->writeEntry(&e); initiateSync(handle); emitFolderUpdated(b->walletName(), folder); return 0; } return -1; } int KWalletD::entryType(int handle, const QString& folder, const QString& key, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { if (!b->hasFolder(folder)) { return KWallet::Wallet::Unknown; } b->setFolder(folder); if (b->hasEntry(key)) { return b->readEntry(key)->type(); } } return KWallet::Wallet::Unknown; } bool KWalletD::hasEntry(int handle, const QString& folder, const QString& key, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { if (!b->hasFolder(folder)) { return false; } b->setFolder(folder); return b->hasEntry(key); } return false; } int KWalletD::removeEntry(int handle, const QString& folder, const QString& key, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { if (!b->hasFolder(folder)) { return 0; } b->setFolder(folder); bool rc = b->removeEntry(key); initiateSync(handle); emitFolderUpdated(b->walletName(), folder); return rc ? 0 : -3; } return -1; } void KWalletD::slotServiceOwnerChanged( const QString& name, const QString& oldOwner, const QString& newOwner) { Q_UNUSED(name); qDebug() << "slotServiceOwnerChanged " << name << ", " << oldOwner << ", " << newOwner; if (!newOwner.isEmpty()) { return; // no application exit, don't care. } // as we don't have the application id we have to cycle // all sessions. As an application can basically open wallets // with several appids, we can't stop if we found one. QString service(oldOwner); QList sessremove(_sessions.findSessions(service)); - KWallet::Backend* b = 0; + KWallet::Backend* b = nullptr; // check all sessions for wallets to close Q_FOREACH (const KWalletAppHandlePair& s, sessremove) { b = getWallet(s.first, s.second); if (b) { b->deref(); internalClose(b, s.second, false); } } // remove all the sessions in case they aren't gone yet Q_FOREACH (const KWalletAppHandlePair& s, sessremove) { _sessions.removeSession(s.first, service, s.second); } // cancel all open-transactions still running for the service QList::iterator tit; for (tit = _transactions.begin(); tit != _transactions.end(); ++tit) { if ((*tit)->tType == KWalletTransaction::Open && (*tit)->service == oldOwner) { delete (*tit); - *tit = 0; + *tit = nullptr; } } - _transactions.removeAll(0); + _transactions.removeAll(nullptr); // if there's currently an open-transaction being handled, // mark it as cancelled. if (_curtrans && _curtrans->tType == KWalletTransaction::Open && _curtrans->service == oldOwner) { qDebug() << "Cancelling current transaction!"; _curtrans->cancelled = true; } _serviceWatcher.removeWatchedService(oldOwner); } KWallet::Backend* KWalletD::getWallet(const QString& appid, int handle) { if (handle == 0) { - return 0L; + return nullptr; } KWallet::Backend* w = _wallets.value(handle); if (w) { // the handle is valid if (_sessions.hasSession(appid, handle)) { // the app owns this handle _failed = 0; if (_closeIdle) { _closeTimers.resetTimer(handle, _idleTime); } return w; } } if (++_failed > 5) { _failed = 0; QTimer::singleShot(0, this, SLOT(notifyFailures())); } - return 0L; + return nullptr; } void KWalletD::notifyFailures() { if (!_showingFailureNotify) { _showingFailureNotify = true; - KMessageBox::information(0, + KMessageBox::information(nullptr, i18n("There have been repeated failed attempts to gain access to " "a wallet. An application may be misbehaving."), i18n("KDE Wallet Service")); _showingFailureNotify = false; } } void KWalletD::doCloseSignals(int handle, const QString& wallet) { emit walletClosed(handle); emit walletClosed(wallet); if (_wallets.isEmpty()) { emit allWalletsClosed(); } } int KWalletD::renameEntry(int handle, const QString& folder, const QString& oldName, const QString& newName, const QString& appid) { KWallet::Backend* b; if ((b = getWallet(appid, handle))) { b->setFolder(folder); int rc = b->renameEntry(oldName, newName); initiateSync(handle); emitFolderUpdated(b->walletName(), folder); return rc; } return -1; } QStringList KWalletD::users(const QString& wallet) const { const QPair walletInfo = findWallet(wallet); return _sessions.getApplications(walletInfo.first); } bool KWalletD::disconnectApplication( const QString& wallet, const QString& application) { const QPair walletInfo = findWallet(wallet); int handle = walletInfo.first; KWallet::Backend* backend = walletInfo.second; if (handle != -1 && _sessions.hasSession(application, handle)) { int removed = _sessions.removeAllSessions(application, handle); for (int i = 0; i < removed; ++i) { backend->deref(); } internalClose(backend, handle, false); emit applicationDisconnected(wallet, application); return true; } return false; } void KWalletD::emitFolderUpdated(const QString& wallet, const QString& folder) { emit folderUpdated(wallet, folder); } void KWalletD::emitWalletListDirty() { const QStringList walletsInDisk = wallets(); foreach (auto i, _wallets.values()) { if (!walletsInDisk.contains(i->walletName())) { internalClose(i, _wallets.key(i), true, false); } } emit walletListDirty(); } void KWalletD::reconfigure() { KConfig cfg(QStringLiteral("kwalletrc")); KConfigGroup walletGroup(&cfg, "Wallet"); _firstUse = walletGroup.readEntry("First Use", true); _enabled = walletGroup.readEntry("Enabled", true); _launchManager = walletGroup.readEntry("Launch Manager", false); _leaveOpen = walletGroup.readEntry("Leave Open", true); bool idleSave = _closeIdle; _closeIdle = walletGroup.readEntry("Close When Idle", false); _openPrompt = walletGroup.readEntry("Prompt on Open", false); int timeSave = _idleTime; // in minutes! _idleTime = walletGroup.readEntry("Idle Timeout", 10) * 60 * 1000; #ifdef Q_WS_X11 if (walletGroup.readEntry("Close on Screensaver", false)) { // BUG 254273 : if kwalletd starts before the screen saver, then the // connection fails and kwalletd never receives it's notifications // To fix this, we use a timer and perform periodic connection // attempts until connection succeeds QTimer::singleShot(0, this, SLOT(connectToScreenSaver())); } else { if (screensaver && screensaver->isValid()) { screensaver->disconnect(SIGNAL(ActiveChanged(bool)), this, SLOT(screenSaverChanged(bool))); delete screensaver; screensaver = 0; } } #endif // Handle idle changes if (_closeIdle) { if (_idleTime != timeSave) { // Timer length changed Wallets::const_iterator it = _wallets.constBegin(); const Wallets::const_iterator end = _wallets.constEnd(); for (; it != end; ++it) { _closeTimers.resetTimer(it.key(), _idleTime); } } if (!idleSave) { // add timers for all the wallets Wallets::const_iterator it = _wallets.constBegin(); const Wallets::const_iterator end = _wallets.constEnd(); for (; it != end; ++it) { _closeTimers.addTimer(it.key(), _idleTime); } } } else { _closeTimers.clear(); } // Update the implicit allow stuff _implicitAllowMap.clear(); const KConfigGroup autoAllowGroup(&cfg, "Auto Allow"); QStringList entries = autoAllowGroup.entryMap().keys(); for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) { _implicitAllowMap[*i] = autoAllowGroup.readEntry(*i, QStringList()); } // Update the implicit allow stuff _implicitDenyMap.clear(); const KConfigGroup autoDenyGroup(&cfg, "Auto Deny"); entries = autoDenyGroup.entryMap().keys(); for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) { _implicitDenyMap[*i] = autoDenyGroup.readEntry(*i, QStringList()); } // Update if wallet was enabled/disabled if (!_enabled) { // close all wallets while (!_wallets.isEmpty()) { Wallets::const_iterator it = _wallets.constBegin(); internalClose(it.value(), it.key(), true); } QApplication::exit(0); } } bool KWalletD::isEnabled() const { return _enabled; } bool KWalletD::folderDoesNotExist( const QString& wallet, const QString& folder) { if (!wallets().contains(wallet)) { return true; } const QPair walletInfo = findWallet(wallet); if (walletInfo.second) { return walletInfo.second->folderDoesNotExist(folder); } KWallet::Backend* b = new KWallet::Backend(wallet); b->open(QByteArray()); bool rc = b->folderDoesNotExist(folder); delete b; return rc; } bool KWalletD::keyDoesNotExist( const QString& wallet, const QString& folder, const QString& key) { if (!wallets().contains(wallet)) { return true; } const QPair walletInfo = findWallet(wallet); if (walletInfo.second) { return walletInfo.second->entryDoesNotExist(folder, key); } KWallet::Backend* b = new KWallet::Backend(wallet); b->open(QByteArray()); bool rc = b->entryDoesNotExist(folder, key); delete b; return rc; } bool KWalletD::implicitAllow(const QString& wallet, const QString& app) { return _implicitAllowMap[wallet].contains(app); } bool KWalletD::implicitDeny(const QString& wallet, const QString& app) { return _implicitDenyMap[wallet].contains(app); } void KWalletD::timedOutClose(int id) { KWallet::Backend* w = _wallets.value(id); if (w) { internalClose(w, id, true); } } void KWalletD::closeAllWallets() { Wallets walletsCopy = _wallets; Wallets::const_iterator it = walletsCopy.constBegin(); const Wallets::const_iterator end = walletsCopy.constEnd(); for (; it != end; ++it) { internalClose(it.value(), it.key(), true); } walletsCopy.clear(); // All of this should be basically noop. Let's just be safe. _wallets.clear(); } QString KWalletD::networkWallet() { return KWallet::Wallet::NetworkWallet(); } QString KWalletD::localWallet() { return KWallet::Wallet::LocalWallet(); } void KWalletD::screenSaverChanged(bool s) { if (s) { closeAllWallets(); } } void KWalletD::activatePasswordDialog() { checkActiveDialog(); } int KWalletD::pamOpen( const QString& wallet, const QByteArray& passwordHash, int sessionTimeout) { if (_processing) { return -1; } if (!QRegExp(QString::fromLatin1("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\." "\\+\\_\\s]+$")).exactMatch(wallet)) { return -1; } // check if the wallet is already open QPair walletInfo = findWallet(wallet); int rc = walletInfo.first; if (rc != -1) { return rc; // Wallet already opened, return handle } if (_wallets.count() > 20) { return -1; } - KWallet::Backend* b = 0; + KWallet::Backend* b = nullptr; // If the wallet we want to open does not exists. create it and set pam // hash if (!wallets().contains(wallet)) { b = new KWallet::Backend(wallet); b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH); } else { b = new KWallet::Backend(wallet); } int openrc = b->openPreHashed(passwordHash); if (openrc != 0 || !b->isOpen()) { delete b; return openrc; } // opening the wallet was successful int handle = generateHandle(); _wallets.insert(handle, b); _syncTimers.addTimer(handle, _syncTime); // don't reference the wallet or add a session so it // can be reclosed easily. if (sessionTimeout > 0) { _closeTimers.addTimer(handle, sessionTimeout); } else if (_closeIdle) { _closeTimers.addTimer(handle, _idleTime); } emit walletOpened(wallet); if (_wallets.count() == 1 && _launchManager) { KToolInvocation::startServiceByDesktopName(QStringLiteral("kwalletmanager-kwalletd")); } return handle; } // vim: tw=220:ts=4 diff --git a/src/runtime/kwalletd/kwalletsessionstore.cpp b/src/runtime/kwalletd/kwalletsessionstore.cpp index de4925c..6755892 100644 --- a/src/runtime/kwalletd/kwalletsessionstore.cpp +++ b/src/runtime/kwalletd/kwalletsessionstore.cpp @@ -1,173 +1,173 @@ /* This file is part of the KDE Wallet Daemon Copyright (c) 2008 Michael Leupold This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kwalletsessionstore.h" class KWalletSessionStore::Session { public: QString m_service; // client dbus service (or empty) int m_handle; // backend handle }; KWalletSessionStore::KWalletSessionStore() { } KWalletSessionStore::~KWalletSessionStore() { Q_FOREACH (const QList &l, m_sessions) { qDeleteAll(l); } } void KWalletSessionStore::addSession(const QString &appid, const QString &service, int handle) { Session *sess = new Session(); sess->m_service = service; sess->m_handle = handle; m_sessions[appid].append(sess); } bool KWalletSessionStore::hasSession(const QString &appid, int handle) const { if (!m_sessions.contains(appid)) { return false; } else if (handle == -1) { return true; } QList::const_iterator it; QList::const_iterator end = m_sessions[appid].constEnd(); for (it = m_sessions[appid].constBegin(); it != end; ++it) { Q_ASSERT(*it); if ((*it)->m_handle == handle) { return true; } } return false; } QList KWalletSessionStore::findSessions(const QString &service) const { QList rc; QList sessionKeys(m_sessions.keys()); Q_FOREACH (const QString &appid, sessionKeys) { Q_FOREACH (const Session *sess, m_sessions[appid]) { Q_ASSERT(sess); if (sess->m_service == service) { rc.append(qMakePair(appid, sess->m_handle)); } } } return rc; } bool KWalletSessionStore::removeSession(const QString &appid, const QString &service, int handle) { if (!m_sessions.contains(appid)) { return false; } QList::const_iterator it; QList::const_iterator end = m_sessions[appid].constEnd(); for (it = m_sessions[appid].constBegin(); it != end; ++it) { Q_ASSERT(*it); if ((*it)->m_service == service && (*it)->m_handle == handle) { Session *sess = *it; m_sessions[appid].removeAll(sess); delete sess; if (m_sessions[appid].isEmpty()) { m_sessions.remove(appid); } return true; } } return false; } int KWalletSessionStore::removeAllSessions(const QString &appid, int handle) { if (!m_sessions.contains(appid)) { return false; } QList::iterator it; QList::iterator end = m_sessions[appid].end(); for (it = m_sessions[appid].begin(); it != end; ++it) { Q_ASSERT(*it); if ((*it)->m_handle == handle) { delete *it; - *it = 0; + *it = nullptr; } } - int removed = m_sessions[appid].removeAll(0); + int removed = m_sessions[appid].removeAll(nullptr); if (m_sessions[appid].isEmpty()) { m_sessions.remove(appid); } return removed; } int KWalletSessionStore::removeAllSessions(int handle) { QList appremove; int numrem = 0; QList sessionKeys(m_sessions.keys()); Q_FOREACH (const QString &appid, sessionKeys) { QList::iterator it; QList::iterator end = m_sessions[appid].end(); for (it = m_sessions[appid].begin(); it != end; ++it) { Q_ASSERT(*it); if ((*it)->m_handle == handle) { delete *it; - *it = 0; + *it = nullptr; numrem++; } } // remove all zeroed sessions - m_sessions[appid].removeAll(0); + m_sessions[appid].removeAll(nullptr); if (m_sessions[appid].isEmpty()) { appremove.append(appid); } } // now remove all applications without sessions Q_FOREACH (const QString &appid, appremove) { m_sessions.remove(appid); } return numrem; } QStringList KWalletSessionStore::getApplications(int handle) const { QStringList rc; Q_FOREACH (const QString &appid, m_sessions.uniqueKeys()) { if (hasSession(appid, handle)) { rc.append(appid); } } return rc; } diff --git a/src/runtime/kwalletd/kwalletwizard.h b/src/runtime/kwalletd/kwalletwizard.h index 1618948..f9e35a6 100644 --- a/src/runtime/kwalletd/kwalletwizard.h +++ b/src/runtime/kwalletd/kwalletwizard.h @@ -1,70 +1,70 @@ /* This file is part of the KDE libraries Copyright (C) 2004 George Staikos This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KWALLETWIZARD_H #define KWALLETWIZARD_H #include #ifdef HAVE_GPGMEPP #include #endif class PageGpgKey; class PagePassword; class PageIntro; class KWalletWizard : public QWizard { Q_OBJECT public: enum WizardType { Basic, Advanced }; static const int PageIntroId = 0; static const int PagePasswordId = 1; #ifdef HAVE_GPGMEPP static const int PageGpgKeyId = 2; #endif static const int PageOptionsId = 3; static const int PageExplanationId = 4; - KWalletWizard(QWidget *parent = 0); + KWalletWizard(QWidget *parent = nullptr); WizardType wizardType() const; #ifdef HAVE_GPGMEPP GpgME::Key gpgKey() const; #endif // HAVE_GPGMEPP protected: void initializePage(int id) Q_DECL_OVERRIDE; protected Q_SLOTS: void passwordPageUpdate(); private: PageIntro *m_pageIntro; PagePassword *m_pagePasswd; #ifdef HAVE_GPGMEPP PageGpgKey *m_pageGpgKey; #endif }; #endif diff --git a/src/runtime/kwalletd/main.cpp b/src/runtime/kwalletd/main.cpp index 90c60d8..ff96208 100644 --- a/src/runtime/kwalletd/main.cpp +++ b/src/runtime/kwalletd/main.cpp @@ -1,232 +1,232 @@ /** * This file is part of the KDE project * Copyright (C) 2008 Michael Leupold * Copyright (C) 2014 Alex Fiestas * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include "backend/kwalletbackend.h" //For the hash size #include "kwalletd.h" #include "kwalletd_version.h" #include "migrationagent.h" #ifndef Q_OS_WIN #include #include #include #include #define BSIZE 1000 static int pipefd = 0; static int socketfd = 0; #endif static bool isWalletEnabled() { KConfig cfg(QStringLiteral("kwalletrc")); KConfigGroup walletGroup(&cfg, "Wallet"); return walletGroup.readEntry("Enabled", true); } #ifndef Q_OS_WIN //Waits until the PAM_MODULE sends the hash static char *waitForHash() { printf("kwalletd5: Waiting for hash on %d-\n", pipefd); int totalRead = 0; int readBytes = 0; int attempts = 0; char *buf = (char*)malloc(sizeof(char) * PBKDF2_SHA512_KEYSIZE); memset(buf, '\0', PBKDF2_SHA512_KEYSIZE); while(totalRead != PBKDF2_SHA512_KEYSIZE) { readBytes = read(pipefd, buf + totalRead, PBKDF2_SHA512_KEYSIZE - totalRead); if (readBytes == -1 || attempts > 5) { free(buf); - return NULL; + return nullptr; } totalRead += readBytes; ++attempts; } close(pipefd); return buf; } //Waits until startkde sends the environment variables static int waitForEnvironment() { printf("kwalletd5: waitingForEnvironment on: %d\n", socketfd); int s2; struct sockaddr_un remote; socklen_t t = sizeof(remote); if ((s2 = accept(socketfd, (struct sockaddr *)&remote, &t)) == -1) { fprintf(stdout, "kwalletd5: Couldn't accept incoming connection\n"); return -1; } printf("kwalletd5: client connected\n"); char str[BSIZE] = {'\0'}; int chop = 0; FILE *s3 = fdopen(dup(s2), "r"); while(!feof(s3)) { if (fgets(str, BSIZE, s3)) { chop = strlen(str) - 1; if (str[chop] == '\n') { str[chop] = '\0'; } putenv(strdup(str)); } } fclose(s3); printf("kwalletd5: client disconnected\n"); close(socketfd); return 1; } char* checkPamModule(int argc, char **argv) { printf("kwalletd5: Checking for pam module\n"); - char *hash = NULL; + char *hash = nullptr; int x = 1; for (; x < argc; ++x) { if (strcmp(argv[x], "--pam-login") != 0) { continue; } printf("kwalletd5: Got pam-login param\n"); - argv[x] = NULL; + argv[x] = nullptr; x++; //We need at least 2 extra arguments after --pam-login if (x + 1 > argc) { printf("kwalletd5: Invalid arguments (less than needed)\n"); - return NULL; + return nullptr; } //first socket for the hash, comes from a pipe pipefd = atoi(argv[x]); - argv[x] = NULL; + argv[x] = nullptr; x++; //second socket for environment, comes from a localsocket socketfd = atoi(argv[x]); - argv[x] = NULL; + argv[x] = nullptr; break; } if (!pipefd || !socketfd) { printf("Lacking a socket, pipe: %d, env:%d\n", pipefd, socketfd); - return NULL; + return nullptr; } hash = waitForHash(); - if (hash == NULL || waitForEnvironment() == -1) { + if (hash == nullptr || waitForEnvironment() == -1) { printf("kwalletd5: Hash or environment not received\n"); free(hash); - return NULL; + return nullptr; } return hash; } #endif #ifdef HAVE_KF5INIT extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) #else int main(int argc, char **argv) #endif { - char *hash = NULL; + char *hash = nullptr; #ifndef Q_OS_WIN if (getenv("PAM_KWALLET5_LOGIN")) { hash = checkPamModule(argc, argv); } #endif QApplication app(argc, argv); // this kwalletd5 program should be able to start with KDE4's kwalletd // using kwalletd name would prevent KDBusService unique instance to initialize // so we setApplicationName("kwalletd5") app.setApplicationName(QStringLiteral("kwalletd5")); app.setApplicationDisplayName(i18n("KDE Wallet Service")); app.setOrganizationDomain(QStringLiteral("kde.org")); app.setApplicationVersion(KWALLETD_VERSION_STRING); KAboutData aboutdata(I18N_NOOP("kwalletd"), i18n("KDE Wallet Service"), KWALLETD_VERSION_STRING, i18n("KDE Wallet Service"), KAboutLicense::LGPL, i18n("(C) 2002-2013, The KDE Developers")); aboutdata.addAuthor(i18n("Valentin Rusu"), i18n("Maintainer, GPG backend support"), QStringLiteral("kde@rusu.info")); aboutdata.addAuthor(i18n("Michael Leupold"), i18n("Former Maintainer"), QStringLiteral("lemma@confuego.org")); aboutdata.addAuthor(i18n("George Staikos"), i18n("Former maintainer"), QStringLiteral("staikos@kde.org")); aboutdata.addAuthor(i18n("Thiago Maceira"), i18n("D-Bus Interface"), QStringLiteral("thiago@kde.org")); KWalletD walletd; MigrationAgent migrationAgent(&walletd, hash); KDBusService dbusUniqueInstance(KDBusService::Unique); // NOTE: the command should be parsed only after KDBusService instantiation QCommandLineParser cmdParser; aboutdata.setupCommandLine(&cmdParser); cmdParser.process(app); aboutdata.setProgramIconName(QStringLiteral("kwalletmanager")); app.setQuitOnLastWindowClosed(false); auto disableSessionManagement = [](QSessionManager &sm) { sm.setRestartHint(QSessionManager::RestartNever); }; QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement); QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement); // check if kwallet is disabled if (!isWalletEnabled()) { qDebug() << "kwalletd is disabled!"; return (0); } qDebug() << "kwalletd5 started"; #ifndef Q_OS_WIN if (hash) { QByteArray passHash(hash, PBKDF2_SHA512_KEYSIZE); int wallet = walletd.pamOpen(KWallet::Wallet::LocalWallet(), passHash, 0); if (wallet < 0) { qWarning() << "Wallet failed to get opened by PAM, error code is" << wallet; } else { qDebug() << "Wallet opened by PAM"; } free(hash); } #endif return app.exec(); } diff --git a/src/runtime/kwalletd/migrationagent.cpp b/src/runtime/kwalletd/migrationagent.cpp index 287d78b..f3da947 100644 --- a/src/runtime/kwalletd/migrationagent.cpp +++ b/src/runtime/kwalletd/migrationagent.cpp @@ -1,259 +1,259 @@ /* This file is part of the KDE Frameworks Copyright (c) 2014 Valentin Rusu This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "migrationagent.h" #include "migrationwizard.h" #include "kwalletd.h" #include #include #include #include #include #include #define SERVICE_KWALLETD4 "org.kde.kwalletd" #define ENTRY_ALREADY_MIGRATED "alreadyMigrated" #define ENTRY_SHOW_MIGRATION_WIZARD "showMigrationWizard" MigrationAgent::MigrationAgent(KWalletD* kd, const char *hash) : _kf5_daemon(kd) - , _kde4_daemon(0) + , _kde4_daemon(nullptr) , _pam_hash(hash) { QTimer::singleShot(100, this, SLOT(migrateWallets())); } void MigrationAgent::migrateWallets() { // the migration is done in several steps : // is the setting "alreadyMigrated" present and true ? // if yes, then stop here // if not, then is kwalletd present (the daemon from KDE4) ? // if not, then stop here // if yes, then start the migration wizard // if the migration wizard returns without error // create "alreadyMigrated=true" setting qDebug() << "Migration agent starting..."; if (!isAlreadyMigrated()) { if (connectOldDaemon()) { if (!isEmptyOldWallet()) { if (isMigrationWizardOk()) { setAlreadyMigrated(); } else { qDebug() << "Migration wizard returned an error or has been canceled. The migration agent will resume upon next daemon start"; } } else { qDebug() << "Old wallet is empty. No need to migrate."; setAlreadyMigrated(); } } else { qDebug() << "KDE4 kwalletd not present, stopping migration agent"; } } else { qDebug() << "old wallets were already migrated"; } qDebug() << "Migration agent stop."; } bool MigrationAgent::isAlreadyMigrated() { KConfig kwalletrc(QStringLiteral("kwalletrc")); KConfigGroup cfg(&kwalletrc, "Migration"); return cfg.readEntry(ENTRY_ALREADY_MIGRATED, false); } void MigrationAgent::setAlreadyMigrated() { KConfig kwalletrc(QStringLiteral("kwalletrc")); KConfigGroup cfg(&kwalletrc, "Migration"); cfg.writeEntry(ENTRY_ALREADY_MIGRATED, true); } bool MigrationAgent::connectOldDaemon() { // the old daemon may not have been started, so attempt start it // NOTE we provide a "fake" org.kde.kwalletd.service file - see the project structure // however, this thing is a HACK as we cannot tell here, in KF5, user's KDE4 install prefix // the provided .service file assumes /usr/bin QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); if (!bus->isServiceRegistered(QStringLiteral(SERVICE_KWALLETD4))) { qDebug() << "kwalletd not started. Attempting start..."; QDBusReply reply = bus->startService(SERVICE_KWALLETD4); if (!reply.isValid()) { qDebug() << "Couldn't start kwalletd: " << reply.error(); return false; } if (!bus->isServiceRegistered(QStringLiteral(SERVICE_KWALLETD4))) { qDebug() << "The kwalletd service is still not registered after start attemtp. Aborting migration"; return false; } else { qDebug() << "The kwalletd service has been registered"; } } _kde4_daemon = new org::kde::KWallet(QLatin1String(SERVICE_KWALLETD4), QStringLiteral("/modules/kwalletd"), QDBusConnection::sessionBus()); return _kde4_daemon->isValid(); } bool MigrationAgent::isMigrationWizardOk() { bool ok = false; // The migration wizard would no longer been shown by default. // see BUG 351056 // NOTE if user wants to show the migration wizard, then he should add the // following setting to the kwalletrc: // [Migration] // showMigrationWizard=true KConfig kwalletrc(QStringLiteral("kwalletrc")); KConfigGroup cfg(&kwalletrc, "Migration"); bool showMigrationWizard = cfg.readEntry(ENTRY_SHOW_MIGRATION_WIZARD, false); if (showMigrationWizard) { MigrationWizard *wizard = new MigrationWizard(this); int result = wizard->exec(); if (QDialog::Accepted == result) { // the user either migrated the wallets, or choose not to be prompted again ok = true; } } else { if (performMigration(0, true)) { ok = true; } else { qDebug() << "Migration failed."; } } return ok; } void MigrationAgent::emitProgressMessage(const QString &msg) { emit progressMessage(msg); } class MigrationException { public: MigrationException(const QString &msg): _msg(msg) {} QString _msg; }; template R invokeAndCheck(MigrationAgent *migrationAgent, F f, QString errorMsg) { QDBusPendingReply reply = f(); reply.waitForFinished(); QApplication::processEvents(); // keep UI responsive to show progress messages if (!reply.isValid()) { migrationAgent->emitProgressMessage(errorMsg); throw MigrationException(errorMsg); } return reply.value(); } bool MigrationAgent::isEmptyOldWallet() const { QStringList wallets; try { wallets = invokeAndCheck( const_cast(this), [this] { return _kde4_daemon->wallets(); }, i18n("Cannot read old wallet list. Aborting.")); } catch (MigrationException ex) { return true; } return wallets.length() == 0; } bool MigrationAgent::performMigration(WId wid, bool withoutWizard) { auto appId = i18n("KDE Wallet Migration Agent"); try { QStringList wallets = invokeAndCheck( this, [this] { return _kde4_daemon->wallets(); }, i18n("Cannot read old wallet list. Aborting.")); foreach (const QString &wallet, wallets) { emit progressMessage(i18n("Migrating wallet: %1", wallet)); emit progressMessage(i18n("* Creating KF5 wallet: %1", wallet)); int handle5 = -1; if (withoutWizard && (_pam_hash != nullptr)) { // see BUG 351056 for why this hacky code // If the user has several wallets, all the values will be // merged into the single LocalWallet handle5 = _kf5_daemon->pamOpen(KWallet::Wallet::LocalWallet(), _pam_hash, 0); } else { handle5 = _kf5_daemon->internalOpen(appId, wallet, false, 0, true, QString()); } if (handle5 <0) { emit progressMessage(i18n("ERROR when attempting new wallet creation. Aborting.")); return false; } int handle4 = invokeAndCheck( this, [this, wallet, wid, appId] { return _kde4_daemon->open(wallet, wid, appId); }, i18n("Cannot open KDE4 wallet named: %1", wallet)); emit progressMessage(i18n("* Opened KDE4 wallet: %1", wallet)); const QStringList folders = invokeAndCheck( this, [this, handle4, appId] { return _kde4_daemon->folderList(handle4, appId); }, i18n("Cannot retrieve folder list. Aborting.")); foreach (const QString &folder, folders) { emit progressMessage(i18n("* Migrating folder %1", folder)); QStringList entries = invokeAndCheck( this, [this, handle4, folder, appId] { return _kde4_daemon->entryList(handle4, folder, appId); }, i18n("Cannot retrieve folder %1 entries. Aborting.", folder)); foreach (const QString &key, entries) { int entryType = invokeAndCheck( this, [this, handle4, folder, key, appId] { return _kde4_daemon->entryType(handle4, folder, key, appId); }, i18n("Cannot retrieve key %1 info. Aborting.", key)); // handle the case where the entries are already there if (_kf5_daemon->hasEntry(handle5, folder, key, appId)) { emit progressMessage(i18n("* SKIPPING entry %1 in folder %2 as it seems already migrated", key, folder)); } else { QByteArray entry = invokeAndCheck( this, [this, handle4, folder, key, appId] { return _kde4_daemon->readEntry(handle4, folder, key, appId); }, i18n("Cannot retrieve key %1 data. Aborting.", key)); if ( _kf5_daemon->writeEntry(handle5, folder, key, entry, entryType, appId) != 0 ) { auto msg = i18n("Cannot write entry %1 in the new wallet. Aborting.", key); emit progressMessage(msg); throw MigrationException(msg); } } } } //_kde4_daemon->close(handle4, false, appId); //_kf5_daemon->close(handle5, true, appId); _kf5_daemon->sync(handle5, appId); emit progressMessage(i18n("DONE migrating wallet\n")); } } catch (MigrationException ex) { return false; } return true; } diff --git a/src/runtime/kwalletd/migrationwizard.cpp b/src/runtime/kwalletd/migrationwizard.cpp index 1cd8a61..ef59fb3 100644 --- a/src/runtime/kwalletd/migrationwizard.cpp +++ b/src/runtime/kwalletd/migrationwizard.cpp @@ -1,108 +1,108 @@ /* * This file is part of the KDE Frameworks * * Copyright (c) 2014 Valentin Rusu * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "migrationwizard.h" #include "ui_migrationwizard1.h" #include "ui_migrationwizard2.h" #include "migrationagent.h" class MigrationPage1 : public QWizardPage { public: MigrationPage1(QWidget *wizard) : QWizardPage(wizard) { _ui.setupUi(this); connect(_ui._optionNo, SIGNAL(toggled(bool)), wizard, SLOT(page1Updated())); connect(_ui._optionNotInterested, SIGNAL(toggled(bool)), wizard, SLOT(page1Updated())); connect(_ui._optionYes, SIGNAL(toggled(bool)), wizard, SLOT(page1Updated())); connect(_ui._optionNo, &QRadioButton::toggled, this, &QWizardPage::completeChanged); } bool isComplete() const Q_DECL_OVERRIDE { return !_ui._optionNo->isChecked(); } Ui::MigrationPage1 _ui; }; class MigrationPage2 : public QWizardPage { public: MigrationPage2(MigrationWizard* wizard) : QWizardPage(wizard) , _agent(wizard->agent()) , _migrationCompleted(false) { _ui.setupUi(this); } void initializePage() Q_DECL_OVERRIDE { connect(_agent, SIGNAL(progressMessage(QString)), _ui._report, SLOT(append(QString))); _migrationCompleted = _agent->performMigration(winId(), false); emit completeChanged(); } bool isComplete() const Q_DECL_OVERRIDE { return _migrationCompleted; } Ui::MigrationPage2 _ui; MigrationAgent *_agent; bool _migrationCompleted; }; -MigrationPage1 *page1 = NULL; -MigrationPage2 *page2 = NULL; +MigrationPage1 *page1 = nullptr; +MigrationPage2 *page2 = nullptr; MigrationWizard::MigrationWizard(MigrationAgent *agent) : _agent(agent) { setOption(HaveFinishButtonOnEarlyPages); page1 = new MigrationPage1(this); addPage(page1); page2 = new MigrationPage2(this); addPage(page2); } MigrationWizard::~MigrationWizard() { delete page1; delete page2; } void MigrationWizard::page1Updated() { if (page1->_ui._optionYes->isChecked()) { page1->setFinalPage(false); button(NextButton)->setEnabled(true); } if (page1->_ui._optionNo->isChecked()) { page1->setFinalPage(true); button(NextButton)->setEnabled(false); } if (page1->_ui._optionNotInterested->isChecked()) { page1->setFinalPage(true); button(NextButton)->setEnabled(false); } } diff --git a/tests/KWallet/kwallettest.cpp b/tests/KWallet/kwallettest.cpp index 6833501..e326f6d 100644 --- a/tests/KWallet/kwallettest.cpp +++ b/tests/KWallet/kwallettest.cpp @@ -1,120 +1,120 @@ /* This file is part of the KDE Libraries Copyright (C) 2007 Thomas McGuire (thomas.mcguire@gmx.net) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kwallettest.h" #include #include #include #include using namespace KWallet; void KWalletTest::init() { if (!qEnvironmentVariableIsSet("DISPLAY")) { QSKIP("$DISPLAY is not set. These tests cannot be done without a graphical system."); } } void KWalletTest::testWallet() { QString testWallet = QStringLiteral("kdewallet"); QString testFolder = QStringLiteral("wallettestfolder"); QString testKeys[] = { QStringLiteral("testKey"), QStringLiteral("account-302948"), QStringLiteral("\\"), QStringLiteral("/abc"), QStringLiteral("a@b.c") }; QByteArray testValues[] = { "test", "@(!ยง\"%&", "", ".test", "\\" }; int numTests = 5; // open Wallet *wallet = Wallet::openWallet(testWallet, 0, Wallet::Synchronous); - if (wallet == 0) { + if (wallet == nullptr) { qDebug() << "Couldn't open the wallet. Maybe the wallet daemon is not running?"; } - QVERIFY2(wallet != 0, "openWallet failed!"); + QVERIFY2(wallet != nullptr, "openWallet failed!"); QVERIFY2(Wallet::isOpen(testWallet), "opwnWallet succeeded but the wallet !isOpen"); // create folder wallet->createFolder(testFolder); QVERIFY2(wallet->hasFolder(testFolder), "Failed to create testFolder"); wallet->setFolder(testFolder); QVERIFY2(wallet->currentFolder() == testFolder, "Failed to set current testFolder"); QVERIFY2(wallet->folderList().contains(testFolder), "The wallet does not contain freshly created testFolder"); // write & read many entries for (int i = 0; i < numTests; i++) { wallet->writeEntry(testKeys[i], testValues[i]); QVERIFY2(wallet->hasEntry(testKeys[i]), "hasEntry failed!"); QByteArray readEntry; wallet->readEntry(testKeys[i], readEntry); QVERIFY2(readEntry == testValues[i], "readEntry failed!"); } // close wallet->sync(); Wallet::closeWallet(QStringLiteral("kdewallet"), true); QVERIFY2(!Wallet::isOpen("kdewallet"), "wallet is still opened after close call!"); // test for key - closed wallet for (int i = 0; i < 5; i++) { QVERIFY2(!Wallet::keyDoesNotExist(testWallet, testFolder, testKeys[i]), "keyDoesNotExist(1) failed"); QVERIFY2(Wallet::keyDoesNotExist(testWallet, testFolder, "madeUpKey"), "keyDoesNotExist(2) failed"); QVERIFY2(Wallet::keyDoesNotExist(testWallet, "madeUpFolderName", "madeUpKey"), "keyDoesNotExist(3) failed"); QVERIFY2(Wallet::keyDoesNotExist(testWallet, "madeUpFolderName", testKeys[i]), "keyDoesNotExist(4) failed"); } // open wallet = Wallet::openWallet(testWallet, 0, Wallet::Synchronous); - QVERIFY2(wallet != 0, "openWallet failed"); + QVERIFY2(wallet != nullptr, "openWallet failed"); QVERIFY2(Wallet::isOpen(testWallet), "openWallet succeeded but the wallet !isOpen (2)"); // set folder QVERIFY2(wallet->hasFolder(testFolder), "The wallet do not have testFolder!"); wallet->setFolder(testFolder); QVERIFY2(wallet->currentFolder() == testFolder, "Failed to set current folder"); // test for key - opened wallet for (int i = 0; i < numTests; i++) { QVERIFY2(!Wallet::keyDoesNotExist(testWallet, testFolder, testKeys[i]), "keyDoesNotExist(1) failed"); QVERIFY2(Wallet::keyDoesNotExist(testWallet, testFolder, "madeUpKey"), "keyDoesNotExist(2) failed"); QVERIFY2(Wallet::keyDoesNotExist(testWallet, "madeUpFolderName", "madeUpKey"), "keyDoesNotExist(3) failed"); QVERIFY2(Wallet::keyDoesNotExist(testWallet, "madeUpFolderName", testKeys[i]), "keyDoesNotExist(4) failed"); } // read many keys for (int i = 0; i < numTests; i++) { QByteArray readEntry; wallet->readEntry(testKeys[i], readEntry); QVERIFY2(readEntry == testValues[i], "Test value after read many keys failed!"); } // delete folder wallet->removeFolder(testFolder); QVERIFY2(!wallet->hasFolder(testFolder), "Failed to delete the testFolder"); // close Wallet::closeWallet(QStringLiteral("kdewallet"), true); QVERIFY2(!Wallet::isOpen("kdewallet"), "Failed to close wallet"); } QTEST_GUILESS_MAIN(KWalletTest) diff --git a/tests/kwalletd/kwalletasync.cpp b/tests/kwalletd/kwalletasync.cpp index 6fe698b..2e9962b 100644 --- a/tests/kwalletd/kwalletasync.cpp +++ b/tests/kwalletd/kwalletasync.cpp @@ -1,56 +1,56 @@ #include #include #include #include #include #include #include #include #include #include #include #include "kwalletasync.h" #include "kwallettest.h" static QTextStream _out(stdout, QIODevice::WriteOnly); void KWalletAsyncTest::init() { if (!qEnvironmentVariableIsSet("DISPLAY")) { QSKIP("$DISPLAY is not set. These tests cannot be done without a graphical system."); } } void KWalletAsyncTest::openWallet() { _out << "About to ask for wallet async" << endl; // we have no wallet: ask for one. KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, KWallet::Wallet::Asynchronous); - QVERIFY(wallet != 0); + QVERIFY(wallet != nullptr); WalletReceiver r; QVERIFY(r.connect(wallet, SIGNAL(walletOpened(bool)), SLOT(walletOpened(bool)))); _out << "About to start 30 second event loop" << endl; QTimer::singleShot(30000, qApp, SLOT(quit())); int ret = qApp->exec(); if (ret == 0) { _out << "Timed out!" << endl; } else { _out << "Success!" << endl; } QVERIFY2(ret == 1, "Timeout when waiting for wallet open"); } void WalletReceiver::walletOpened(bool got) { _out << "Got async wallet: " << got << endl; qApp->exit(1); } QTEST_GUILESS_MAIN(KWalletAsyncTest) diff --git a/tests/kwalletd/kwalletboth.cpp b/tests/kwalletd/kwalletboth.cpp index d916a92..914e21a 100644 --- a/tests/kwalletd/kwalletboth.cpp +++ b/tests/kwalletd/kwalletboth.cpp @@ -1,78 +1,78 @@ #include #include #include #include #include #include #include #include #include #include #include #include "kwalletboth.h" #include "kwallettest.h" static QTextStream _out(stdout, QIODevice::WriteOnly); void KWalletBothTest::init() { if (!qEnvironmentVariableIsSet("DISPLAY")) { QSKIP("$DISPLAY is not set. These tests cannot be done without a graphical system."); } } void KWalletBothTest::openWallet() { _out << "About to ask for wallet async" << endl; // we have no wallet: ask for one. KWallet::Wallet *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, KWallet::Wallet::Asynchronous); - QVERIFY(wallet != 0); + QVERIFY(wallet != nullptr); WalletReceiver r; r.connect(wallet, SIGNAL(walletOpened(bool)), SLOT(walletOpened(bool))); _out << "About to ask for wallet sync" << endl; wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, KWallet::Wallet::Synchronous); - QVERIFY(wallet != 0); + QVERIFY(wallet != nullptr); - _out << "Got sync wallet: " << (wallet != 0) << endl; + _out << "Got sync wallet: " << (wallet != nullptr) << endl; _out << "About to start 30 second event loop" << endl; QTimer::singleShot(30000, qApp, SLOT(quit())); int ret = qApp->exec(); if (ret == 0) { _out << "Timed out!" << endl; } else { _out << "Success!" << endl; } QMap p; ret = wallet->readPasswordList(QStringLiteral("*"), p); _out << "readPasswordList returned: " << ret << endl; _out << "readPasswordList returned " << p.keys().count() << " entries" << endl; QMap > q; ret = wallet->readMapList(QStringLiteral("*"), q); _out << "readMapList returned: " << ret << endl; _out << "readMapList returned " << q.keys().count() << " entries" << endl; QMap s; ret = wallet->readEntryList(QStringLiteral("*"), s); _out << "readEntryList returned: " << ret << endl; _out << "readEntryList returned " << s.keys().count() << " entries" << endl; delete wallet; } void WalletReceiver::walletOpened(bool got) { _out << "Got async wallet: " << got << endl; qApp->exit(1); } QTEST_GUILESS_MAIN(KWalletBothTest) diff --git a/tests/kwalletd/kwalletmany.cpp b/tests/kwalletd/kwalletmany.cpp index 7805eb2..f50a90e 100644 --- a/tests/kwalletd/kwalletmany.cpp +++ b/tests/kwalletd/kwalletmany.cpp @@ -1,83 +1,83 @@ /* This file is part of the KDE libraries Copyright (c) 2008 Michael Leupold This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "kwalletmany.h" #define NUMWALLETS 10 using namespace KWallet; static QTextStream _out(stdout, QIODevice::WriteOnly); KWalletMany::KWalletMany() : QObject(), _pending(10) { } KWalletMany::~KWalletMany() { } void KWalletMany::init() { if (!qEnvironmentVariableIsSet("DISPLAY")) { QSKIP("$DISPLAY is not set. These tests cannot be done without a graphical system."); } } void KWalletMany::walletOpened(bool open) { _out << "Got async wallet: " << (open) << endl; --_pending; } void KWalletMany::openWallet() { // open plenty of wallets in synchronous and asynchronous mode for (int i = 0; i < NUMWALLETS; ++i) { // request asynchronous wallet _out << "About to ask for wallet async" << endl; Wallet *wallet; wallet = Wallet::openWallet(Wallet::NetworkWallet(), 0, Wallet::Asynchronous); - QVERIFY(wallet != 0); + QVERIFY(wallet != nullptr); connect(wallet, SIGNAL(walletOpened(bool)), SLOT(walletOpened(bool))); _wallets.append(wallet); } // wait for 30s to receive the wallet opened replies from kwalletd QTRY_VERIFY_WITH_TIMEOUT(_pending == 0, 30000); while (!_wallets.isEmpty()) { delete _wallets.takeFirst(); } QApplication::quit(); } QTEST_GUILESS_MAIN(KWalletMany) diff --git a/tests/kwalletd/kwalletpath.cpp b/tests/kwalletd/kwalletpath.cpp index 32506db..bd61435 100644 --- a/tests/kwalletd/kwalletpath.cpp +++ b/tests/kwalletd/kwalletpath.cpp @@ -1,39 +1,39 @@ #include #include #include #include #include #include #include #include #include #include #include "kwalletpath.h" static QTextStream _out(stdout, QIODevice::WriteOnly); void KWalletPathTest::init() { if (!qEnvironmentVariableIsSet("DISPLAY")) { QSKIP("$DISPLAY is not set. These tests cannot be done without a graphical system."); } } void KWalletPathTest::openWallet() { _out << "About to ask for wallet /tmp/test.kwl sync" << endl; KWallet::Wallet *wallet = KWallet::Wallet::openWallet(QStringLiteral("/tmp/test.kwl"), 0, KWallet::Wallet::Path); - QVERIFY(wallet != 0); + QVERIFY(wallet != nullptr); - _out << "Got path wallet: " << (wallet != 0) << endl; + _out << "Got path wallet: " << (wallet != nullptr) << endl; if (wallet) { _out << "Closing wallet" << endl; delete wallet; } } QTEST_GUILESS_MAIN(KWalletPathTest) diff --git a/tests/kwalletd/kwalletsync.cpp b/tests/kwalletd/kwalletsync.cpp index c96f0ac..62d8a89 100644 --- a/tests/kwalletd/kwalletsync.cpp +++ b/tests/kwalletd/kwalletsync.cpp @@ -1,35 +1,35 @@ #include #include #include #include #include #include #include #include #include #include #include "kwalletsync.h" static QTextStream _out(stdout, QIODevice::WriteOnly); void KWalletSyncTest::init() { if (!qEnvironmentVariableIsSet("DISPLAY")) { QSKIP("$DISPLAY is not set. These tests cannot be done without a graphical system."); } } void KWalletSyncTest::openWallet() { _out << "About to ask for wallet sync" << endl; KWallet::Wallet *w = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, KWallet::Wallet::Synchronous); - QVERIFY(w != 0); + QVERIFY(w != nullptr); - _out << "Got sync wallet: " << (w != 0) << endl; + _out << "Got sync wallet: " << (w != nullptr) << endl; } QTEST_GUILESS_MAIN(KWalletSyncTest)