mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-23 18:32:50 +00:00
1670 lines
48 KiB
C++
1670 lines
48 KiB
C++
// -*- indent-tabs-mode: t; tab-width: 4; c-basic-offset: 4; -*-
|
|
/*
|
|
This file is part of the KDE libraries
|
|
|
|
Copyright (c) 2002-2004 George Staikos <staikos@kde.org>
|
|
Copyright (c) 2008 Michael Leupold <lemma@confuego.org>
|
|
|
|
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_QGPGME
|
|
#include "knewwalletdialog.h"
|
|
#endif
|
|
|
|
#include <kuniqueapplication.h>
|
|
#include <ktoolinvocation.h>
|
|
#include <kconfig.h>
|
|
#include <kconfiggroup.h>
|
|
#include <kdebug.h>
|
|
#include <kdirwatch.h>
|
|
#include <kglobal.h>
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <kpassworddialog.h>
|
|
#include <knewpassworddialog.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kwalletentry.h>
|
|
#include <kicon.h>
|
|
#include <kwindowsystem.h>
|
|
#include <kpluginfactory.h>
|
|
#include <kpluginloader.h>
|
|
#include <KNotification>
|
|
#ifdef HAVE_QGPGME
|
|
#include <gpgme++/key.h>
|
|
#endif
|
|
|
|
#include <QtCore/QDir>
|
|
#include <QTextDocument> // Qt::escape
|
|
#include <QtCore/QRegExp>
|
|
#include <QtCore/QTimer>
|
|
#include <QtCore/QEventLoop>
|
|
|
|
#include <assert.h>
|
|
|
|
#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), _failed(0), _syncTime(5000), _curtrans(0), _useGpg(false) {
|
|
#ifdef HAVE_QGPGME
|
|
_useGpg = true;
|
|
#endif
|
|
|
|
srand(time(0));
|
|
_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(QLatin1String("org.kde.kwalletd"));
|
|
QDBusConnection::sessionBus().registerObject(QLatin1String("/modules/kwalletd"), this);
|
|
|
|
#ifdef Q_WS_X11
|
|
screensaver = 0;
|
|
#endif
|
|
|
|
reconfigure();
|
|
KGlobal::dirs()->addResourceType("kwallet", 0, "share/apps/kwallet");
|
|
_dw = new KDirWatch(this );
|
|
_dw->setObjectName( QLatin1String( "KWallet Directory Watcher" ) );
|
|
_dw->addDir(KGlobal::dirs()->saveLocation("kwallet"));
|
|
_dw->startScan(true);
|
|
connect(_dw, SIGNAL(dirty(const QString&)), this, SLOT(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);
|
|
}
|
|
|
|
void KWalletD::connectToScreenSaver()
|
|
{
|
|
#ifdef Q_WS_X11
|
|
screensaver = new QDBusInterface("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver");
|
|
if (!screensaver->isValid()) {
|
|
kDebug() << "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)));
|
|
kDebug() << "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<int, KWallet::Backend*> 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<KWallet::Backend*>(0));
|
|
}
|
|
|
|
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<KWalletTransaction *>::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;
|
|
}
|
|
|
|
_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("^[\\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("^[\\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) {
|
|
kDebug() << "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) {
|
|
kDebug() << "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())
|
|
kWarning() << "Using kwallet without parent window!";
|
|
else
|
|
kWarning() << "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
|
|
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. Hopefully to be done in KDE4, for now just use all kinds of bad hacks to make
|
|
// sure the user doesn't overlook the active dialog.
|
|
void KWalletD::checkActiveDialog() {
|
|
if( !activeDialog )
|
|
return;
|
|
|
|
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 && !wallets().contains(KWallet::Wallet::LocalWallet()) && !isPath) {
|
|
// First use wizard
|
|
// TODO GPG adjust new smartcard options gathered by the wizard
|
|
QPointer<KWalletWizard> 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_QGPGME
|
|
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_QGPGME
|
|
} 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;
|
|
}
|
|
} else if (_firstUse && !isPath) {
|
|
KConfig kwalletrc("kwalletrc");
|
|
KConfigGroup cfg(&kwalletrc, "Wallet");
|
|
_firstUse = false;
|
|
cfg.writeEntry("First Use", false);
|
|
}
|
|
|
|
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 = "KDE System";
|
|
} else {
|
|
thisApp = appid;
|
|
}
|
|
|
|
if (implicitDeny(wallet, thisApp)) {
|
|
return -1;
|
|
}
|
|
|
|
QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
|
|
int rc = walletInfo.first;
|
|
if (rc == -1) {
|
|
if (_wallets.count() > 20) {
|
|
kDebug() << "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_QGPGME
|
|
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("<qt>KDE has requested to open the wallet '<b>%1</b>'. Please enter the password for this wallet below.</qt>", Qt::escape(wallet)));
|
|
} else {
|
|
kpd->setPrompt(i18n("<qt>The application '<b>%1</b>' has requested to open the wallet '<b>%2</b>'. Please enter the password for this wallet below.</qt>", Qt::escape(appid), Qt::escape(wallet)));
|
|
}
|
|
brandNew = false;
|
|
// don't use KStdGuiItem::open() here which has trailing ellipsis!
|
|
kpd->setButtonGuiItem(KDialog::Ok,KGuiItem( i18n( "&Open" ), "wallet-open"));
|
|
kpd->setCaption(i18n("KDE Wallet Service"));
|
|
kpd->setPixmap(KIcon("kwalletmanager").pixmap(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("needsPassword", kpd,
|
|
KNotification::Persistent |
|
|
KNotification::CloseWhenWidgetActivated);
|
|
QStringList actions(i18nc("Text of a button to ignore the open-wallet notification", "Ignore"));
|
|
if (appid.isEmpty()) {
|
|
notification->setText(i18n("<b>KDE</b> has requested to open a wallet (%1).",
|
|
Qt::escape(wallet)));
|
|
actions.append(i18nc("Text of a button for switching to the (unnamed) application "
|
|
"requesting a password", "Switch there"));
|
|
} else {
|
|
notification->setText(i18n("<b>%1</b> has requested to open a wallet (%2).",
|
|
Qt::escape(appid), Qt::escape(wallet)));
|
|
actions.append(i18nc("Text of a button for switching to the application requesting "
|
|
"a password", "Switch to %1", Qt::escape(appid)));
|
|
}
|
|
notification->setActions(actions);
|
|
connect(notification, SIGNAL(action1Activated()),
|
|
notification, SLOT(close()));
|
|
connect(notification, SIGNAL(action2Activated()),
|
|
this, SLOT(activatePasswordDialog()));
|
|
notification->sendEvent();
|
|
}
|
|
while (!b->isOpen()) {
|
|
setupDialog( kpd, w, appid, modal );
|
|
if (kpd->exec() == KDialog::Accepted) {
|
|
password = kpd->password();
|
|
int rc = b->open(password.toUtf8());
|
|
if (!b->isOpen()) {
|
|
kpd->setPrompt(i18n("<qt>Error opening the wallet '<b>%1</b>'. Please try again.<br />(Error code %2: %3)</qt>", Qt::escape(wallet), rc, KWallet::Backend::openRCToString(rc)));
|
|
kpd->setPassword("");
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
delete kpd;
|
|
} else {
|
|
emptyPass = true;
|
|
}
|
|
#ifdef HAVE_QGPGME
|
|
}
|
|
#endif
|
|
} else {
|
|
brandNew = true;
|
|
#ifdef HAVE_QGPGME
|
|
// prompt the user for the new wallet format here
|
|
KWallet::BackendCipherType newWalletType = KWallet::BACKEND_CIPHER_UNKNOWN;
|
|
|
|
boost::shared_ptr<KWallet::KNewWalletDialog> 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_QGPGME
|
|
b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH);
|
|
KNewPasswordDialog *kpd = new KNewPasswordDialog();
|
|
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("<qt>The application '<b>%1</b>' has requested to open the KDE wallet. This is used to store sensitive data in a secure fashion. Please enter a password to use with this wallet or click cancel to deny the application's request.</qt>", Qt::escape(appid)));
|
|
}
|
|
} else {
|
|
if (appid.length() == 0) {
|
|
kpd->setPrompt(i18n("<qt>KDE has requested to create a new wallet named '<b>%1</b>'. Please choose a password for this wallet, or cancel to deny the application's request.</qt>", Qt::escape(wallet)));
|
|
} else {
|
|
kpd->setPrompt(i18n("<qt>The application '<b>%1</b>' has requested to create a new wallet named '<b>%2</b>'. Please choose a password for this wallet, or cancel to deny the application's request.</qt>", Qt::escape(appid), Qt::escape(wallet)));
|
|
}
|
|
}
|
|
kpd->setCaption(i18n("KDE Wallet Service"));
|
|
kpd->setButtonGuiItem(KDialog::Ok,KGuiItem(i18n("C&reate"),"document-new"));
|
|
kpd->setPixmap(KIcon("kwalletmanager").pixmap(96, 96));
|
|
while (!b->isOpen()) {
|
|
setupDialog( kpd, w, appid, modal );
|
|
if (kpd->exec() == KDialog::Accepted) {
|
|
password = kpd->password();
|
|
int rc = b->open(password.toUtf8());
|
|
if (!b->isOpen()) {
|
|
kpd->setPrompt(i18n("<qt>Error opening the wallet '<b>%1</b>'. Please try again.<br />(Error code %2: %3)</qt>", Qt::escape(wallet), rc, KWallet::Backend::openRCToString(rc)));
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
delete kpd;
|
|
#ifdef HAVE_QGPGME
|
|
}
|
|
#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("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 = "KDE System";
|
|
} else {
|
|
thisApp = appid;
|
|
}
|
|
|
|
if (!implicitAllow(wallet, thisApp)) {
|
|
KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc")->group("Auto Allow");
|
|
if (!cfg.isEntryImmutable(wallet)) {
|
|
KBetterThanKDialog *dialog = new KBetterThanKDialog;
|
|
dialog->setWindowTitle(i18n("KDE Wallet Service"));
|
|
if (appid.isEmpty()) {
|
|
dialog->setLabel(i18n("<qt>KDE has requested access to the open wallet '<b>%1</b>'.</qt>", Qt::escape(wallet)));
|
|
} else {
|
|
dialog->setLabel(i18n("<qt>The application '<b>%1</b>' has requested access to the open wallet '<b>%2</b>'.</qt>", Qt::escape(QString(appid)), Qt::escape(wallet)));
|
|
}
|
|
setupDialog( dialog, w, appid, false );
|
|
response = dialog->exec();
|
|
delete dialog;
|
|
}
|
|
}
|
|
|
|
if (response == 0 || response == 1) {
|
|
if (response == 1) {
|
|
KConfigGroup cfg = KSharedConfig::openConfig("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("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 = KGlobal::dirs()->saveLocation("kwallet") + QDir::separator() + wallet + ".kwl";
|
|
|
|
if (QFile::exists(path)) {
|
|
const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
|
|
internalClose(walletInfo.second, walletInfo.first, true);
|
|
QFile::remove(path);
|
|
emit walletDeleted(wallet);
|
|
// also delete access control entries
|
|
KConfigGroup cfgAllow = KSharedConfig::openConfig("kwalletrc")->group("Auto Allow");
|
|
cfgAllow.deleteEntry(wallet);
|
|
|
|
KConfigGroup cfgDeny = KSharedConfig::openConfig("kwalletrc")->group("Auto Deny");
|
|
cfgDeny.deleteEntry(wallet);
|
|
|
|
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<int, KWallet::Backend*> walletInfo = findWallet(wallet);
|
|
int handle = walletInfo.first;
|
|
KWallet::Backend* w = walletInfo.second;
|
|
|
|
bool reclose = false;
|
|
if (!w) {
|
|
handle = doTransactionOpen(appid, wallet, false, wId, false, "");
|
|
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_QGPGME
|
|
if (w->cipherType() == KWallet::BACKEND_CIPHER_GPG) {
|
|
QString keyID = w->gpgKey().shortKeyID();
|
|
assert(!keyID.isNull());
|
|
KMessageBox::errorWId((WId)wId, i18n("<qt>The <b>%1</b> wallet is encrypted using GPG key <b>%2</b>. Please use <b>GPG</b> tools (such as <b>kleopatra</b>) to change the passphrase associated to that key.</qt>", Qt::escape(wallet), keyID));
|
|
} else {
|
|
#endif
|
|
QPointer<KNewPasswordDialog> kpd = new KNewPasswordDialog();
|
|
kpd->setPrompt(i18n("<qt>Please choose a new password for the wallet '<b>%1</b>'.</qt>", Qt::escape(wallet)));
|
|
kpd->setCaption(i18n("KDE Wallet Service"));
|
|
kpd->setAllowEmptyPasswords(true);
|
|
setupDialog( kpd, (WId)wId, appid, false );
|
|
if (kpd->exec() == KDialog::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_QGPGME
|
|
}
|
|
#endif
|
|
|
|
if (reclose) {
|
|
internalClose(w, handle, true);
|
|
}
|
|
}
|
|
|
|
|
|
int KWalletD::close(const QString& wallet, bool force) {
|
|
const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
|
|
int handle = walletInfo.first;
|
|
KWallet::Backend* w = walletInfo.second;
|
|
|
|
return internalClose(w, handle, force);
|
|
}
|
|
|
|
|
|
int KWalletD::internalClose(KWallet::Backend *w, int handle, bool force) {
|
|
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(true);
|
|
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, "", 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<int, KWallet::Backend*> walletInfo = findWallet(wallet);
|
|
return walletInfo.second != 0;
|
|
}
|
|
|
|
|
|
bool KWalletD::isOpen(int handle) {
|
|
if (handle == 0) {
|
|
return false;
|
|
}
|
|
|
|
KWallet::Backend *rc = _wallets.value(handle);
|
|
|
|
if (rc == 0 && ++_failed > 5) {
|
|
_failed = 0;
|
|
QTimer::singleShot(0, this, SLOT(notifyFailures()));
|
|
} else if (rc != 0) {
|
|
_failed = 0;
|
|
}
|
|
|
|
return rc != 0;
|
|
}
|
|
|
|
|
|
QStringList KWalletD::wallets() const {
|
|
QString path = KGlobal::dirs()->saveLocation("kwallet");
|
|
QDir dir(path, "*.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);
|
|
}
|
|
}
|
|
|
|
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<int, KWallet::Backend*> 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->value();
|
|
}
|
|
}
|
|
|
|
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->value());
|
|
}
|
|
}
|
|
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);
|
|
kDebug() << "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<KWalletAppHandlePair> sessremove(_sessions.findSessions(service));
|
|
KWallet::Backend *b = 0;
|
|
|
|
// 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<KWalletTransaction*>::iterator tit;
|
|
for (tit = _transactions.begin(); tit != _transactions.end(); ++tit) {
|
|
if ((*tit)->tType == KWalletTransaction::Open && (*tit)->service == oldOwner) {
|
|
delete (*tit);
|
|
*tit = 0;
|
|
}
|
|
}
|
|
_transactions.removeAll(0);
|
|
|
|
// if there's currently an open-transaction being handled,
|
|
// mark it as cancelled.
|
|
if (_curtrans && _curtrans->tType == KWalletTransaction::Open &&
|
|
_curtrans->service == oldOwner) {
|
|
kDebug() << "Cancelling current transaction!";
|
|
_curtrans->cancelled = true;
|
|
}
|
|
_serviceWatcher.removeWatchedService(oldOwner);
|
|
}
|
|
|
|
KWallet::Backend *KWalletD::getWallet(const QString& appid, int handle) {
|
|
if (handle == 0) {
|
|
return 0L;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
void KWalletD::notifyFailures() {
|
|
if (!_showingFailureNotify) {
|
|
_showingFailureNotify = true;
|
|
KMessageBox::information(0, i18n("There have been repeated failed attempts to gain access to a wallet. An application may be misbehaving."), i18n("KDE Wallet Service"));
|
|
_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<int,KWallet::Backend*> walletInfo = findWallet(wallet);
|
|
return _sessions.getApplications(walletInfo.first);
|
|
}
|
|
|
|
|
|
bool KWalletD::disconnectApplication(const QString& wallet, const QString& application) {
|
|
const QPair<int, KWallet::Backend*> 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() {
|
|
emit walletListDirty();
|
|
}
|
|
|
|
|
|
void KWalletD::reconfigure() {
|
|
KConfig cfg("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", false);
|
|
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);
|
|
}
|
|
KUniqueApplication::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<int, KWallet::Backend*> 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<int, KWallet::Backend*> 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("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\.\\+\\_\\s]+$").exactMatch(wallet)) {
|
|
return -1;
|
|
}
|
|
|
|
// check if the wallet is already open
|
|
QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet);
|
|
int rc = walletInfo.first;
|
|
if (rc != -1) {
|
|
return rc;//Wallet already opened, return handle
|
|
}
|
|
|
|
KWallet::Backend *b = 0;
|
|
//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);
|
|
}
|
|
|
|
if (_wallets.count() > 20) {
|
|
return -1;
|
|
}
|
|
|
|
int openrc = b->openPreHashed(passwordHash);
|
|
if (openrc != 0 || !b->isOpen()) {
|
|
return -1;
|
|
}
|
|
|
|
// 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("kwalletmanager-kwalletd");
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
#include "moc_kwalletd.cpp"
|