1 | // -*- indent-tabs-mode: t; tab-width: 4; c-basic-offset: 4; -*- |
2 | /* |
3 | This file is part of the KDE libraries |
4 | |
5 | Copyright (c) 2002-2004 George Staikos <staikos@kde.org> |
6 | Copyright (c) 2008 Michael Leupold <lemma@confuego.org> |
7 | |
8 | This library is free software; you can redistribute it and/or |
9 | modify it under the terms of the GNU Library General Public |
10 | License as published by the Free Software Foundation; either |
11 | version 2 of the License, or (at your option) any later version. |
12 | |
13 | This library is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | Library General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU Library General Public License |
19 | along with this library; see the file COPYING.LIB. If not, write to |
20 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 | Boston, MA 02110-1301, USA. |
22 | |
23 | */ |
24 | |
25 | #include "kwalletd.h" |
26 | |
27 | #include "kbetterthankdialog.h" |
28 | #include "kwalletwizard.h" |
29 | |
30 | #ifdef HAVE_QGPGME |
31 | #include "knewwalletdialog.h" |
32 | #endif |
33 | |
34 | #include <kuniqueapplication.h> |
35 | #include <ktoolinvocation.h> |
36 | #include <kconfig.h> |
37 | #include <kconfiggroup.h> |
38 | #include <kdebug.h> |
39 | #include <kdirwatch.h> |
40 | #include <kglobal.h> |
41 | #include <klocale.h> |
42 | #include <kmessagebox.h> |
43 | #include <kpassworddialog.h> |
44 | #include <knewpassworddialog.h> |
45 | #include <kstandarddirs.h> |
46 | #include <kwalletentry.h> |
47 | #include <kicon.h> |
48 | #include <kwindowsystem.h> |
49 | #include <kpluginfactory.h> |
50 | #include <kpluginloader.h> |
51 | #include <KNotification> |
52 | #ifdef HAVE_QGPGME |
53 | #include <gpgme++/key.h> |
54 | #endif |
55 | |
56 | #include <QtCore/QDir> |
57 | #include <QTextDocument> // Qt::escape |
58 | #include <QtCore/QRegExp> |
59 | #include <QtCore/QTimer> |
60 | #include <QtCore/QEventLoop> |
61 | |
62 | #include <assert.h> |
63 | |
64 | #include "kwalletadaptor.h" |
65 | |
66 | class KWalletTransaction { |
67 | |
68 | public: |
69 | explicit KWalletTransaction(QDBusConnection conn) |
70 | : tType(Unknown), cancelled(false), tId(nextTransactionId), res(-1), connection(conn) |
71 | { |
72 | nextTransactionId++; |
73 | // make sure the id is never < 0 as that's used for the |
74 | // error conditions. |
75 | if (nextTransactionId < 0) { |
76 | nextTransactionId = 0; |
77 | } |
78 | } |
79 | |
80 | ~KWalletTransaction() { |
81 | } |
82 | |
83 | enum Type { |
84 | Unknown, |
85 | Open, |
86 | ChangePassword, |
87 | OpenFail, |
88 | CloseCancelled |
89 | }; |
90 | Type tType; |
91 | QString appid; |
92 | qlonglong wId; |
93 | QString wallet; |
94 | QString service; |
95 | bool cancelled; // set true if the client dies before open |
96 | bool modal; |
97 | bool isPath; |
98 | int tId; // transaction id |
99 | int res; |
100 | QDBusMessage message; |
101 | QDBusConnection connection; |
102 | |
103 | private: |
104 | static int nextTransactionId; |
105 | }; |
106 | |
107 | int KWalletTransaction::nextTransactionId = 0; |
108 | |
109 | KWalletD::KWalletD() |
110 | : QObject(0), _failed(0), _syncTime(5000), _curtrans(0), _useGpg(false) { |
111 | #ifdef HAVE_QGPGME |
112 | _useGpg = true; |
113 | #endif |
114 | |
115 | srand(time(0)); |
116 | _showingFailureNotify = false; |
117 | _closeIdle = false; |
118 | _idleTime = 0; |
119 | connect(&_closeTimers, SIGNAL(timedOut(int)), this, SLOT(timedOutClose(int))); |
120 | connect(&_syncTimers, SIGNAL(timedOut(int)), this, SLOT(timedOutSync(int))); |
121 | |
122 | (void)new KWalletAdaptor(this); |
123 | // register services |
124 | QDBusConnection::sessionBus().registerService(QLatin1String("org.kde.kwalletd" )); |
125 | QDBusConnection::sessionBus().registerObject(QLatin1String("/modules/kwalletd" ), this); |
126 | |
127 | #ifdef Q_WS_X11 |
128 | screensaver = 0; |
129 | #endif |
130 | |
131 | reconfigure(); |
132 | KGlobal::dirs()->addResourceType("kwallet" , 0, "share/apps/kwallet" ); |
133 | _dw = new KDirWatch(this ); |
134 | _dw->setObjectName( QLatin1String( "KWallet Directory Watcher" ) ); |
135 | _dw->addDir(KGlobal::dirs()->saveLocation("kwallet" )); |
136 | _dw->startScan(true); |
137 | connect(_dw, SIGNAL(dirty(const QString&)), this, SLOT(emitWalletListDirty())); |
138 | |
139 | _serviceWatcher.setWatchMode( QDBusServiceWatcher::WatchForOwnerChange ); |
140 | connect(&_serviceWatcher, SIGNAL(serviceOwnerChanged(QString,QString,QString)), this, SLOT(slotServiceOwnerChanged(QString,QString,QString))); |
141 | } |
142 | |
143 | |
144 | KWalletD::~KWalletD() { |
145 | #ifdef Q_WS_X11 |
146 | delete screensaver; |
147 | screensaver = 0; |
148 | #endif |
149 | closeAllWallets(); |
150 | qDeleteAll(_transactions); |
151 | } |
152 | |
153 | #ifdef Q_WS_X11 |
154 | void KWalletD::connectToScreenSaver() |
155 | { |
156 | screensaver = new QDBusInterface("org.freedesktop.ScreenSaver" , "/ScreenSaver" , "org.freedesktop.ScreenSaver" ); |
157 | if (!screensaver->isValid()) { |
158 | kDebug() << "Service org.freedesktop.ScreenSaver not found. Retrying in 10 seconds..." ; |
159 | // keep attempting every 10 seconds |
160 | QTimer::singleShot(10000, this, SLOT(connectToScreenSaver())); |
161 | } else { |
162 | connect(screensaver, SIGNAL(ActiveChanged(bool)), SLOT(screenSaverChanged(bool))); |
163 | kDebug() << "connected to screen saver service." ; |
164 | } |
165 | } |
166 | #endif |
167 | |
168 | int KWalletD::generateHandle() { |
169 | int rc; |
170 | |
171 | // ASSUMPTION: RAND_MAX is fairly large. |
172 | do { |
173 | rc = rand(); |
174 | } while (_wallets.contains(rc) || rc == 0); |
175 | |
176 | return rc; |
177 | } |
178 | |
179 | QPair<int, KWallet::Backend*> KWalletD::findWallet(const QString& walletName) const |
180 | { |
181 | Wallets::const_iterator it = _wallets.constBegin(); |
182 | const Wallets::const_iterator end = _wallets.constEnd(); |
183 | for (; it != end; ++it) { |
184 | if (it.value()->walletName() == walletName) { |
185 | return qMakePair(it.key(), it.value()); |
186 | } |
187 | } |
188 | return qMakePair(-1, static_cast<KWallet::Backend*>(0)); |
189 | } |
190 | |
191 | bool KWalletD::_processing = false; |
192 | |
193 | void KWalletD::processTransactions() { |
194 | if (_processing) { |
195 | return; |
196 | } |
197 | |
198 | _processing = true; |
199 | |
200 | // Process remaining transactions |
201 | while (!_transactions.isEmpty()) { |
202 | _curtrans = _transactions.takeFirst(); |
203 | int res; |
204 | |
205 | assert(_curtrans->tType != KWalletTransaction::Unknown); |
206 | |
207 | switch (_curtrans->tType) { |
208 | case KWalletTransaction::Open: |
209 | res = doTransactionOpen(_curtrans->appid, _curtrans->wallet, _curtrans->isPath, |
210 | _curtrans->wId, _curtrans->modal, _curtrans->service); |
211 | |
212 | // multiple requests from the same client |
213 | // should not produce multiple password |
214 | // dialogs on a failure |
215 | if (res < 0) { |
216 | QList<KWalletTransaction *>::iterator it; |
217 | for (it = _transactions.begin(); it != _transactions.end(); ++it) { |
218 | KWalletTransaction *x = *it; |
219 | if (_curtrans->appid == x->appid && x->tType == KWalletTransaction::Open |
220 | && x->wallet == _curtrans->wallet && x->wId == _curtrans->wId) { |
221 | x->tType = KWalletTransaction::OpenFail; |
222 | } |
223 | } |
224 | } else if (_curtrans->cancelled) { |
225 | // the wallet opened successfully but the application |
226 | // opening exited/crashed while the dialog was still shown. |
227 | KWalletTransaction *_xact = new KWalletTransaction(_curtrans->connection); |
228 | _xact->tType = KWalletTransaction::CloseCancelled; |
229 | _xact->appid = _curtrans->appid; |
230 | _xact->wallet = _curtrans->wallet; |
231 | _xact->service = _curtrans->service; |
232 | _transactions.append(_xact); |
233 | } |
234 | |
235 | // emit the AsyncOpened signal as a reply |
236 | _curtrans->res = res; |
237 | emit walletAsyncOpened(_curtrans->tId, res); |
238 | break; |
239 | |
240 | case KWalletTransaction::OpenFail: |
241 | // emit the AsyncOpened signal with an invalid handle |
242 | _curtrans->res = -1; |
243 | emit walletAsyncOpened(_curtrans->tId, -1); |
244 | break; |
245 | |
246 | case KWalletTransaction::ChangePassword: |
247 | doTransactionChangePassword(_curtrans->appid, _curtrans->wallet, _curtrans->wId); |
248 | break; |
249 | |
250 | case KWalletTransaction::CloseCancelled: |
251 | doTransactionOpenCancelled(_curtrans->appid, _curtrans->wallet, |
252 | _curtrans->service); |
253 | break; |
254 | |
255 | case KWalletTransaction::Unknown: |
256 | break; |
257 | default: |
258 | break; |
259 | } |
260 | |
261 | // send delayed dbus message reply to the caller |
262 | if (_curtrans->message.type() != QDBusMessage::InvalidMessage) { |
263 | if (_curtrans->connection.isConnected()) { |
264 | QDBusMessage reply = _curtrans->message.createReply(); |
265 | reply << _curtrans->res; |
266 | _curtrans->connection.send(reply); |
267 | } |
268 | } |
269 | |
270 | delete _curtrans; |
271 | _curtrans = 0; |
272 | } |
273 | |
274 | _processing = false; |
275 | } |
276 | |
277 | int KWalletD::openPath(const QString& path, qlonglong wId, const QString& appid) { |
278 | int tId = openPathAsync(path, wId, appid, false); |
279 | if (tId < 0) { |
280 | return tId; |
281 | } |
282 | |
283 | // NOTE the real return value will be sent by the dbusmessage delayed reply |
284 | return 0; |
285 | // wait for the open-transaction to be processed |
286 | // KWalletOpenLoop loop(this); |
287 | // return loop.waitForAsyncOpen(tId); |
288 | } |
289 | |
290 | int KWalletD::open(const QString& wallet, qlonglong wId, const QString& appid) { |
291 | if (!_enabled) { // guard |
292 | return -1; |
293 | } |
294 | |
295 | if (!QRegExp("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\.\\+\\_\\s]+$" ).exactMatch(wallet)) { |
296 | return -1; |
297 | } |
298 | |
299 | KWalletTransaction *xact = new KWalletTransaction(connection()); |
300 | _transactions.append(xact); |
301 | |
302 | message().setDelayedReply(true); |
303 | xact->message = message(); |
304 | |
305 | xact->appid = appid; |
306 | xact->wallet = wallet; |
307 | xact->wId = wId; |
308 | xact->modal = true; // mark dialogs as modal, the app has blocking wait |
309 | xact->tType = KWalletTransaction::Open; |
310 | xact->isPath = false; |
311 | |
312 | QTimer::singleShot(0, this, SLOT(processTransactions())); |
313 | checkActiveDialog(); |
314 | // NOTE the real return value will be sent by the dbusmessage delayed reply |
315 | return 0; |
316 | } |
317 | |
318 | int KWalletD::openAsync(const QString& wallet, qlonglong wId, const QString& appid, |
319 | bool handleSession) { |
320 | if (!_enabled) { // guard |
321 | return -1; |
322 | } |
323 | |
324 | if (!QRegExp("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\.\\+\\_\\s]+$" ).exactMatch(wallet)) { |
325 | return -1; |
326 | } |
327 | |
328 | KWalletTransaction *xact = new KWalletTransaction(connection()); |
329 | _transactions.append(xact); |
330 | |
331 | xact->appid = appid; |
332 | xact->wallet = wallet; |
333 | xact->wId = wId; |
334 | xact->modal = true; // mark dialogs as modal, the app has blocking wait |
335 | xact->tType = KWalletTransaction::Open; |
336 | xact->isPath = false; |
337 | if (handleSession) { |
338 | kDebug() << "openAsync for " << message().service(); |
339 | _serviceWatcher.setConnection(connection()); |
340 | _serviceWatcher.addWatchedService(message().service()); |
341 | xact->service = message().service(); |
342 | } |
343 | QTimer::singleShot(0, this, SLOT(processTransactions())); |
344 | checkActiveDialog(); |
345 | // opening is in progress. return the transaction number |
346 | return xact->tId; |
347 | } |
348 | |
349 | int KWalletD::openPathAsync(const QString& path, qlonglong wId, const QString& appid, |
350 | bool handleSession) { |
351 | if (!_enabled) { // gaurd |
352 | return -1; |
353 | } |
354 | |
355 | KWalletTransaction *xact = new KWalletTransaction(connection()); |
356 | _transactions.append(xact); |
357 | |
358 | xact->appid = appid; |
359 | xact->wallet = path; |
360 | xact->wId = wId; |
361 | xact->modal = true; |
362 | xact->tType = KWalletTransaction::Open; |
363 | xact->isPath = true; |
364 | if (handleSession) { |
365 | kDebug() << "openPathAsync " << message().service(); |
366 | _serviceWatcher.setConnection(connection()); |
367 | _serviceWatcher.addWatchedService(message().service()); |
368 | xact->service = message().service(); |
369 | } |
370 | QTimer::singleShot(0, this, SLOT(processTransactions())); |
371 | checkActiveDialog(); |
372 | // opening is in progress. return the transaction number |
373 | return xact->tId; |
374 | } |
375 | |
376 | // Sets up a dialog that will be shown by kwallet. |
377 | void KWalletD::setupDialog( QWidget* dialog, WId wId, const QString& appid, bool modal ) { |
378 | if( wId != 0 ) |
379 | KWindowSystem::setMainWindow( dialog, wId ); // correct, set dialog parent |
380 | else { |
381 | if( appid.isEmpty()) |
382 | kWarning() << "Using kwallet without parent window!" ; |
383 | else |
384 | kWarning() << "Application '" << appid << "' using kwallet without parent window!" ; |
385 | // allow dialog activation even if it interrupts, better than trying hacks |
386 | // with keeping the dialog on top or on all desktops |
387 | kapp->updateUserTimestamp(); |
388 | } |
389 | if( modal ) |
390 | KWindowSystem::setState( dialog->winId(), NET::Modal ); |
391 | else |
392 | KWindowSystem::clearState( dialog->winId(), NET::Modal ); |
393 | activeDialog = dialog; |
394 | } |
395 | |
396 | // If there's a dialog already open and another application tries some operation that'd lead to |
397 | // opening a dialog, that application will be blocked by this dialog. A proper solution would |
398 | // be to set the second application's window also as a parent for the active dialog, so that |
399 | // KWin properly handles focus changes and so on, but there's currently no support for multiple |
400 | // dialog parents. Hopefully to be done in KDE4, for now just use all kinds of bad hacks to make |
401 | // sure the user doesn't overlook the active dialog. |
402 | void KWalletD::checkActiveDialog() { |
403 | if( !activeDialog ) |
404 | return; |
405 | |
406 | kapp->updateUserTimestamp(); |
407 | |
408 | activeDialog->show(); |
409 | |
410 | WId window = activeDialog->winId(); |
411 | KWindowSystem::setState( window, NET::KeepAbove ); |
412 | KWindowSystem::setOnAllDesktops( window, true ); |
413 | KWindowSystem::forceActiveWindow( window ); |
414 | KWindowSystem::raiseWindow( window ); |
415 | } |
416 | |
417 | |
418 | int KWalletD::doTransactionOpen(const QString& appid, const QString& wallet, bool isPath, |
419 | qlonglong wId, bool modal, const QString& service) { |
420 | if (_firstUse && !wallets().contains(KWallet::Wallet::LocalWallet()) && !isPath) { |
421 | // First use wizard |
422 | // TODO GPG adjust new smartcard options gathered by the wizard |
423 | QPointer<KWalletWizard> wiz = new KWalletWizard(0); |
424 | wiz->setWindowTitle(i18n("KDE Wallet Service" )); |
425 | setupDialog( wiz, (WId)wId, appid, modal ); |
426 | int rc = wiz->exec(); |
427 | if (rc == QDialog::Accepted && wiz) { |
428 | bool useWallet = wiz->field("useWallet" ).toBool(); |
429 | KConfig kwalletrc("kwalletrc" ); |
430 | KConfigGroup cfg(&kwalletrc, "Wallet" ); |
431 | cfg.writeEntry("First Use" , false); |
432 | cfg.writeEntry("Enabled" , useWallet); |
433 | cfg.writeEntry("Close When Idle" , wiz->field("closeWhenIdle" ).toBool()); |
434 | cfg.writeEntry("Use One Wallet" , !wiz->field("networkWallet" ).toBool()); |
435 | cfg.sync(); |
436 | reconfigure(); |
437 | |
438 | if (!useWallet) { |
439 | delete wiz; |
440 | return -1; |
441 | } |
442 | |
443 | // Create the wallet |
444 | // TODO GPG select the correct wallet type upon cretion (GPG or blowfish based) |
445 | KWallet::Backend *b = new KWallet::Backend(KWallet::Wallet::LocalWallet()); |
446 | #ifdef HAVE_QGPGME |
447 | if (wiz->field("useBlowfish" ).toBool()) { |
448 | b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH); |
449 | #endif |
450 | QString pass = wiz->field("pass1" ).toString(); |
451 | QByteArray p(pass.toUtf8(), pass.length()); |
452 | b->open(p); |
453 | p.fill(0); |
454 | #ifdef HAVE_QGPGME |
455 | } else { |
456 | assert(wiz->field("useGpg" ).toBool()); |
457 | b->setCipherType(KWallet::BACKEND_CIPHER_GPG); |
458 | b->open(wiz->gpgKey()); |
459 | } |
460 | #endif |
461 | b->createFolder(KWallet::Wallet::PasswordFolder()); |
462 | b->createFolder(KWallet::Wallet::FormDataFolder()); |
463 | b->close(true); |
464 | delete b; |
465 | delete wiz; |
466 | } else { |
467 | delete wiz; |
468 | return -1; |
469 | } |
470 | } else if (_firstUse && !isPath) { |
471 | KConfig kwalletrc("kwalletrc" ); |
472 | KConfigGroup cfg(&kwalletrc, "Wallet" ); |
473 | _firstUse = false; |
474 | cfg.writeEntry("First Use" , false); |
475 | } |
476 | |
477 | int rc = internalOpen(appid, wallet, isPath, WId(wId), modal, service); |
478 | return rc; |
479 | } |
480 | |
481 | |
482 | int KWalletD::internalOpen(const QString& appid, const QString& wallet, bool isPath, WId w, |
483 | bool modal, const QString& service) { |
484 | bool brandNew = false; |
485 | |
486 | QString thisApp; |
487 | if (appid.isEmpty()) { |
488 | thisApp = "KDE System" ; |
489 | } else { |
490 | thisApp = appid; |
491 | } |
492 | |
493 | if (implicitDeny(wallet, thisApp)) { |
494 | return -1; |
495 | } |
496 | |
497 | QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
498 | int rc = walletInfo.first; |
499 | if (rc == -1) { |
500 | if (_wallets.count() > 20) { |
501 | kDebug() << "Too many wallets open." ; |
502 | return -1; |
503 | } |
504 | |
505 | KWallet::Backend *b = new KWallet::Backend(wallet, isPath); |
506 | QString password; |
507 | bool emptyPass = false; |
508 | if ((isPath && QFile::exists(wallet)) || (!isPath && KWallet::Backend::exists(wallet))) { |
509 | // this open attempt will set wallet type from the file header, even if password is needed |
510 | int pwless = b->open(QByteArray(), w); |
511 | #ifdef HAVE_QGPGME |
512 | assert(b->cipherType() != KWallet::BACKEND_CIPHER_UNKNOWN); |
513 | if (b->cipherType() == KWallet::BACKEND_CIPHER_GPG) { |
514 | // GPG based wallets do not prompt for password here. Instead, GPG should already have popped pinentry utility for wallet decryption |
515 | if (!b->isOpen()){ |
516 | // for some reason, GPG operation failed |
517 | delete b; |
518 | return -1; |
519 | } |
520 | emptyPass = true; |
521 | } else { |
522 | #endif |
523 | if (0 != pwless || !b->isOpen()) { |
524 | if (pwless == 0) { |
525 | // release, start anew |
526 | delete b; |
527 | b = new KWallet::Backend(wallet, isPath); |
528 | } |
529 | KPasswordDialog *kpd = new KPasswordDialog(); |
530 | if (appid.isEmpty()) { |
531 | 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))); |
532 | } else { |
533 | 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))); |
534 | } |
535 | brandNew = false; |
536 | // don't use KStdGuiItem::open() here which has trailing ellipsis! |
537 | kpd->setButtonGuiItem(KDialog::Ok,KGuiItem( i18n( "&Open" ), "wallet-open" )); |
538 | kpd->setCaption(i18n("KDE Wallet Service" )); |
539 | kpd->setPixmap(KIcon("kwalletmanager" ).pixmap(KIconLoader::SizeHuge)); |
540 | if (w != KWindowSystem::activeWindow() && w != 0L) { |
541 | // If the dialog is modal to a minimized window it might not be visible |
542 | // (but still blocking the calling application). Notify the user about |
543 | // the request to open the wallet. |
544 | KNotification *notification = new KNotification("needsPassword" , kpd, |
545 | KNotification::Persistent | |
546 | KNotification::CloseWhenWidgetActivated); |
547 | QStringList actions(i18nc("Text of a button to ignore the open-wallet notification" , "Ignore" )); |
548 | if (appid.isEmpty()) { |
549 | notification->setText(i18n("<b>KDE</b> has requested to open a wallet (%1)." , |
550 | Qt::escape(wallet))); |
551 | actions.append(i18nc("Text of a button for switching to the (unnamed) application " |
552 | "requesting a password" , "Switch there" )); |
553 | } else { |
554 | notification->setText(i18n("<b>%1</b> has requested to open a wallet (%2)." , |
555 | Qt::escape(appid), Qt::escape(wallet))); |
556 | actions.append(i18nc("Text of a button for switching to the application requesting " |
557 | "a password" , "Switch to %1" , Qt::escape(appid))); |
558 | } |
559 | notification->setActions(actions); |
560 | connect(notification, SIGNAL(action1Activated()), |
561 | notification, SLOT(close())); |
562 | connect(notification, SIGNAL(action2Activated()), |
563 | this, SLOT(activatePasswordDialog())); |
564 | notification->sendEvent(); |
565 | } |
566 | while (!b->isOpen()) { |
567 | setupDialog( kpd, w, appid, modal ); |
568 | if (kpd->exec() == KDialog::Accepted) { |
569 | password = kpd->password(); |
570 | int rc = b->open(password.toUtf8()); |
571 | if (!b->isOpen()) { |
572 | 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))); |
573 | kpd->setPassword("" ); |
574 | } |
575 | } else { |
576 | break; |
577 | } |
578 | } |
579 | delete kpd; |
580 | } else { |
581 | emptyPass = true; |
582 | } |
583 | #ifdef HAVE_QGPGME |
584 | } |
585 | #endif |
586 | } else { |
587 | brandNew = true; |
588 | #ifdef HAVE_QGPGME |
589 | // prompt the user for the new wallet format here |
590 | KWallet::BackendCipherType newWalletType = KWallet::BACKEND_CIPHER_UNKNOWN; |
591 | |
592 | boost::shared_ptr<KWallet::KNewWalletDialog> newWalletDlg( new KWallet::KNewWalletDialog(appid, wallet, QWidget::find(w))); |
593 | GpgME::Key gpgKey; |
594 | setupDialog( newWalletDlg.get(), (WId)w, appid, true ); |
595 | if (newWalletDlg->exec() == QDialog::Accepted) { |
596 | newWalletType = newWalletDlg->isBlowfish() ? KWallet::BACKEND_CIPHER_BLOWFISH : KWallet::BACKEND_CIPHER_GPG; |
597 | gpgKey = newWalletDlg->gpgKey(); |
598 | } else { |
599 | // user cancelled the dialog box |
600 | delete b; |
601 | return -1; |
602 | } |
603 | |
604 | if (newWalletType == KWallet::BACKEND_CIPHER_GPG) { |
605 | b->setCipherType(newWalletType); |
606 | b->open(gpgKey); |
607 | } else if (newWalletType == KWallet::BACKEND_CIPHER_BLOWFISH) { |
608 | #endif // HAVE_QGPGME |
609 | b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH); |
610 | KNewPasswordDialog *kpd = new KNewPasswordDialog(); |
611 | if (wallet == KWallet::Wallet::LocalWallet() || |
612 | wallet == KWallet::Wallet::NetworkWallet()) |
613 | { |
614 | // Auto create these wallets. |
615 | if (appid.isEmpty()) { |
616 | 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." )); |
617 | } else { |
618 | 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))); |
619 | } |
620 | } else { |
621 | if (appid.length() == 0) { |
622 | 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))); |
623 | } else { |
624 | 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))); |
625 | } |
626 | } |
627 | kpd->setCaption(i18n("KDE Wallet Service" )); |
628 | kpd->setButtonGuiItem(KDialog::Ok,KGuiItem(i18n("C&reate" ),"document-new" )); |
629 | kpd->setPixmap(KIcon("kwalletmanager" ).pixmap(96, 96)); |
630 | while (!b->isOpen()) { |
631 | setupDialog( kpd, w, appid, modal ); |
632 | if (kpd->exec() == KDialog::Accepted) { |
633 | password = kpd->password(); |
634 | int rc = b->open(password.toUtf8()); |
635 | if (!b->isOpen()) { |
636 | 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))); |
637 | } |
638 | } else { |
639 | break; |
640 | } |
641 | } |
642 | delete kpd; |
643 | #ifdef HAVE_QGPGME |
644 | } |
645 | #endif |
646 | } |
647 | |
648 | |
649 | if ((b->cipherType() == KWallet::BACKEND_CIPHER_BLOWFISH) && |
650 | !emptyPass && (password.isNull() || !b->isOpen())) { |
651 | delete b; |
652 | return -1; |
653 | } |
654 | |
655 | if (emptyPass && !isAuthorizedApp(appid, wallet, w)) { |
656 | delete b; |
657 | return -1; |
658 | } |
659 | |
660 | _wallets.insert(rc = generateHandle(), b); |
661 | _sessions.addSession(appid, service, rc); |
662 | _syncTimers.addTimer(rc, _syncTime); |
663 | |
664 | if (brandNew) { |
665 | createFolder(rc, KWallet::Wallet::PasswordFolder(), appid); |
666 | createFolder(rc, KWallet::Wallet::FormDataFolder(), appid); |
667 | } |
668 | |
669 | b->ref(); |
670 | if (_closeIdle) { |
671 | _closeTimers.addTimer(rc, _idleTime); |
672 | } |
673 | if (brandNew) |
674 | emit walletCreated(wallet); |
675 | emit walletOpened(wallet); |
676 | if (_wallets.count() == 1 && _launchManager) { |
677 | KToolInvocation::startServiceByDesktopName("kwalletmanager-kwalletd" ); |
678 | } |
679 | } else { |
680 | // prematurely add a reference so that the wallet does not close while the |
681 | // authorization dialog is being shown. |
682 | walletInfo.second->ref(); |
683 | bool isAuthorized = _sessions.hasSession(appid, rc) || isAuthorizedApp(appid, wallet, w); |
684 | // as the wallet might have been forcefully closed, find it again to make sure it's |
685 | // still available (isAuthorizedApp might show a dialog). |
686 | walletInfo = findWallet(wallet); |
687 | if (!isAuthorized) { |
688 | if (walletInfo.first != -1) { |
689 | walletInfo.second->deref(); |
690 | // check if the wallet should be closed now. |
691 | internalClose(walletInfo.second, walletInfo.first, false); |
692 | } |
693 | return -1; |
694 | } else { |
695 | if (walletInfo.first != -1) { |
696 | _sessions.addSession(appid, service, rc); |
697 | } else { |
698 | // wallet was forcefully closed. |
699 | return -1; |
700 | } |
701 | } |
702 | } |
703 | |
704 | return rc; |
705 | } |
706 | |
707 | |
708 | bool KWalletD::isAuthorizedApp(const QString& appid, const QString& wallet, WId w) { |
709 | if (!_openPrompt) { |
710 | return true; |
711 | } |
712 | |
713 | int response = 0; |
714 | |
715 | QString thisApp; |
716 | if (appid.isEmpty()) { |
717 | thisApp = "KDE System" ; |
718 | } else { |
719 | thisApp = appid; |
720 | } |
721 | |
722 | if (!implicitAllow(wallet, thisApp)) { |
723 | KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc" )->group("Auto Allow" ); |
724 | if (!cfg.isEntryImmutable(wallet)) { |
725 | KBetterThanKDialog *dialog = new KBetterThanKDialog; |
726 | dialog->setWindowTitle(i18n("KDE Wallet Service" )); |
727 | if (appid.isEmpty()) { |
728 | dialog->setLabel(i18n("<qt>KDE has requested access to the open wallet '<b>%1</b>'.</qt>" , Qt::escape(wallet))); |
729 | } else { |
730 | 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))); |
731 | } |
732 | setupDialog( dialog, w, appid, false ); |
733 | response = dialog->exec(); |
734 | delete dialog; |
735 | } |
736 | } |
737 | |
738 | if (response == 0 || response == 1) { |
739 | if (response == 1) { |
740 | KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc" )->group("Auto Allow" ); |
741 | QStringList apps = cfg.readEntry(wallet, QStringList()); |
742 | if (!apps.contains(thisApp)) { |
743 | if (cfg.isEntryImmutable(wallet)) { |
744 | return false; |
745 | } |
746 | apps += thisApp; |
747 | _implicitAllowMap[wallet] += thisApp; |
748 | cfg.writeEntry(wallet, apps); |
749 | cfg.sync(); |
750 | } |
751 | } |
752 | } else if (response == 3) { |
753 | KConfigGroup cfg = KSharedConfig::openConfig("kwalletrc" )->group("Auto Deny" ); |
754 | QStringList apps = cfg.readEntry(wallet, QStringList()); |
755 | if (!apps.contains(thisApp)) { |
756 | apps += thisApp; |
757 | _implicitDenyMap[wallet] += thisApp; |
758 | cfg.writeEntry(wallet, apps); |
759 | cfg.sync(); |
760 | } |
761 | return false; |
762 | } else { |
763 | return false; |
764 | } |
765 | return true; |
766 | } |
767 | |
768 | |
769 | int KWalletD::deleteWallet(const QString& wallet) { |
770 | int result = -1; |
771 | QString path = KGlobal::dirs()->saveLocation("kwallet" ) + QDir::separator() + wallet + ".kwl" ; |
772 | |
773 | if (QFile::exists(path)) { |
774 | const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
775 | internalClose(walletInfo.second, walletInfo.first, true); |
776 | QFile::remove(path); |
777 | emit walletDeleted(wallet); |
778 | // also delete access control entries |
779 | KConfigGroup cfgAllow = KSharedConfig::openConfig("kwalletrc" )->group("Auto Allow" ); |
780 | cfgAllow.deleteEntry(wallet); |
781 | |
782 | KConfigGroup cfgDeny = KSharedConfig::openConfig("kwalletrc" )->group("Auto Deny" ); |
783 | cfgDeny.deleteEntry(wallet); |
784 | |
785 | result = 0; |
786 | } |
787 | |
788 | return result; |
789 | } |
790 | |
791 | |
792 | void KWalletD::changePassword(const QString& wallet, qlonglong wId, const QString& appid) { |
793 | KWalletTransaction *xact = new KWalletTransaction(connection()); |
794 | |
795 | message().setDelayedReply(true); |
796 | xact->message = message(); |
797 | // TODO GPG this shouldn't be allowed on a GPG managed wallet; a warning should be displayed about this |
798 | |
799 | xact->appid = appid; |
800 | xact->wallet = wallet; |
801 | xact->wId = wId; |
802 | xact->modal = false; |
803 | xact->tType = KWalletTransaction::ChangePassword; |
804 | |
805 | _transactions.append(xact); |
806 | |
807 | QTimer::singleShot(0, this, SLOT(processTransactions())); |
808 | checkActiveDialog(); |
809 | checkActiveDialog(); |
810 | } |
811 | |
812 | void KWalletD::initiateSync(int handle) { |
813 | // add a timer and reset it right away |
814 | _syncTimers.addTimer(handle, _syncTime); |
815 | _syncTimers.resetTimer(handle, _syncTime); |
816 | } |
817 | |
818 | void KWalletD::doTransactionChangePassword(const QString& appid, const QString& wallet, qlonglong wId) { |
819 | |
820 | const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
821 | int handle = walletInfo.first; |
822 | KWallet::Backend* w = walletInfo.second; |
823 | |
824 | bool reclose = false; |
825 | if (!w) { |
826 | handle = doTransactionOpen(appid, wallet, false, wId, false, "" ); |
827 | if (-1 == handle) { |
828 | KMessageBox::sorryWId((WId)wId, i18n("Unable to open wallet. The wallet must be opened in order to change the password." ), i18n("KDE Wallet Service" )); |
829 | return; |
830 | } |
831 | |
832 | w = _wallets.value(handle); |
833 | reclose = true; |
834 | } |
835 | |
836 | assert(w); |
837 | |
838 | #ifdef HAVE_QGPGME |
839 | if (w->cipherType() == KWallet::BACKEND_CIPHER_GPG) { |
840 | QString keyID = w->gpgKey().shortKeyID(); |
841 | assert(!keyID.isNull()); |
842 | 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)); |
843 | } else { |
844 | #endif |
845 | QPointer<KNewPasswordDialog> kpd = new KNewPasswordDialog(); |
846 | kpd->setPrompt(i18n("<qt>Please choose a new password for the wallet '<b>%1</b>'.</qt>" , Qt::escape(wallet))); |
847 | kpd->setCaption(i18n("KDE Wallet Service" )); |
848 | kpd->setAllowEmptyPasswords(true); |
849 | setupDialog( kpd, (WId)wId, appid, false ); |
850 | if (kpd->exec() == KDialog::Accepted && kpd) { |
851 | QString p = kpd->password(); |
852 | if (!p.isNull()) { |
853 | w->setPassword(p.toUtf8()); |
854 | int rc = w->close(true); |
855 | if (rc < 0) { |
856 | KMessageBox::sorryWId((WId)wId, i18n("Error re-encrypting the wallet. Password was not changed." ), i18n("KDE Wallet Service" )); |
857 | reclose = true; |
858 | } else { |
859 | rc = w->open(p.toUtf8()); |
860 | if (rc < 0) { |
861 | KMessageBox::sorryWId((WId)wId, i18n("Error reopening the wallet. Data may be lost." ), i18n("KDE Wallet Service" )); |
862 | reclose = true; |
863 | } |
864 | } |
865 | } |
866 | } |
867 | |
868 | delete kpd; |
869 | #ifdef HAVE_QGPGME |
870 | } |
871 | #endif |
872 | |
873 | if (reclose) { |
874 | internalClose(w, handle, true); |
875 | } |
876 | } |
877 | |
878 | |
879 | int KWalletD::close(const QString& wallet, bool force) { |
880 | const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
881 | int handle = walletInfo.first; |
882 | KWallet::Backend* w = walletInfo.second; |
883 | |
884 | return internalClose(w, handle, force); |
885 | } |
886 | |
887 | |
888 | int KWalletD::internalClose(KWallet::Backend *w, int handle, bool force) { |
889 | if (w) { |
890 | const QString& wallet = w->walletName(); |
891 | if ((w->refCount() == 0 && !_leaveOpen) || force) { |
892 | // this is only a safety measure. sessions should be gone already. |
893 | _sessions.removeAllSessions(handle); |
894 | if (_closeIdle) { |
895 | _closeTimers.removeTimer(handle); |
896 | } |
897 | _syncTimers.removeTimer(handle); |
898 | _wallets.remove(handle); |
899 | w->close(true); |
900 | doCloseSignals(handle, wallet); |
901 | delete w; |
902 | return 0; |
903 | } |
904 | return 1; |
905 | } |
906 | |
907 | return -1; |
908 | } |
909 | |
910 | |
911 | int KWalletD::close(int handle, bool force, const QString& appid) { |
912 | KWallet::Backend *w = _wallets.value(handle); |
913 | |
914 | if (w) { |
915 | if (_sessions.hasSession(appid, handle)) { |
916 | // remove one handle for the application |
917 | bool removed = _sessions.removeSession(appid, message().service(), handle); |
918 | // alternatively try sessionless |
919 | if (removed || _sessions.removeSession(appid, "" , handle)) { |
920 | w->deref(); |
921 | } |
922 | return internalClose(w, handle, force); |
923 | } |
924 | return 1; // not closed, handle unknown |
925 | } |
926 | return -1; // not open to begin with, or other error |
927 | } |
928 | |
929 | |
930 | bool KWalletD::isOpen(const QString& wallet) { |
931 | const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
932 | return walletInfo.second != 0; |
933 | } |
934 | |
935 | |
936 | bool KWalletD::isOpen(int handle) { |
937 | if (handle == 0) { |
938 | return false; |
939 | } |
940 | |
941 | KWallet::Backend *rc = _wallets.value(handle); |
942 | |
943 | if (rc == 0 && ++_failed > 5) { |
944 | _failed = 0; |
945 | QTimer::singleShot(0, this, SLOT(notifyFailures())); |
946 | } else if (rc != 0) { |
947 | _failed = 0; |
948 | } |
949 | |
950 | return rc != 0; |
951 | } |
952 | |
953 | |
954 | QStringList KWalletD::wallets() const { |
955 | QString path = KGlobal::dirs()->saveLocation("kwallet" ); |
956 | QDir dir(path, "*.kwl" ); |
957 | QStringList rc; |
958 | |
959 | dir.setFilter(QDir::Files | QDir::Hidden); |
960 | |
961 | foreach (const QFileInfo &fi, dir.entryInfoList()) { |
962 | QString fn = fi.fileName(); |
963 | if (fn.endsWith(QLatin1String(".kwl" ))) { |
964 | fn.truncate(fn.length()-4); |
965 | } |
966 | rc += fn; |
967 | } |
968 | return rc; |
969 | } |
970 | |
971 | |
972 | void KWalletD::sync(int handle, const QString& appid) { |
973 | KWallet::Backend *b; |
974 | |
975 | // get the wallet and check if we have a password for it (safety measure) |
976 | if ((b = getWallet(appid, handle))) { |
977 | QString wallet = b->walletName(); |
978 | b->sync(0); |
979 | } |
980 | } |
981 | |
982 | void KWalletD::timedOutSync(int handle) { |
983 | _syncTimers.removeTimer(handle); |
984 | if (_wallets.contains(handle) && _wallets[handle]) { |
985 | _wallets[handle]->sync(0); |
986 | } |
987 | } |
988 | |
989 | void KWalletD::doTransactionOpenCancelled(const QString& appid, const QString& wallet, |
990 | const QString& service) { |
991 | |
992 | // there will only be one session left to remove - all others |
993 | // have already been removed in slotServiceOwnerChanged and all |
994 | // transactions for opening new sessions have been deleted. |
995 | if (!_sessions.hasSession(appid)) { |
996 | return; |
997 | } |
998 | |
999 | const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
1000 | int handle = walletInfo.first; |
1001 | KWallet::Backend *b = walletInfo.second; |
1002 | if (handle != -1 && b) { |
1003 | b->deref(); |
1004 | internalClose(b, handle, false); |
1005 | } |
1006 | |
1007 | // close the session in case the wallet hasn't been closed yet |
1008 | _sessions.removeSession(appid, service, handle); |
1009 | } |
1010 | |
1011 | QStringList KWalletD::folderList(int handle, const QString& appid) { |
1012 | KWallet::Backend *b; |
1013 | |
1014 | if ((b = getWallet(appid, handle))) { |
1015 | return b->folderList(); |
1016 | } |
1017 | |
1018 | return QStringList(); |
1019 | } |
1020 | |
1021 | |
1022 | bool KWalletD::hasFolder(int handle, const QString& f, const QString& appid) { |
1023 | KWallet::Backend *b; |
1024 | |
1025 | if ((b = getWallet(appid, handle))) { |
1026 | return b->hasFolder(f); |
1027 | } |
1028 | |
1029 | return false; |
1030 | } |
1031 | |
1032 | |
1033 | bool KWalletD::removeFolder(int handle, const QString& f, const QString& appid) { |
1034 | KWallet::Backend *b; |
1035 | |
1036 | if ((b = getWallet(appid, handle))) { |
1037 | bool rc = b->removeFolder(f); |
1038 | initiateSync(handle); |
1039 | emit folderListUpdated(b->walletName()); |
1040 | return rc; |
1041 | } |
1042 | |
1043 | return false; |
1044 | } |
1045 | |
1046 | |
1047 | bool KWalletD::createFolder(int handle, const QString& f, const QString& appid) { |
1048 | KWallet::Backend *b; |
1049 | |
1050 | if ((b = getWallet(appid, handle))) { |
1051 | bool rc = b->createFolder(f); |
1052 | initiateSync(handle); |
1053 | emit folderListUpdated(b->walletName()); |
1054 | return rc; |
1055 | } |
1056 | |
1057 | return false; |
1058 | } |
1059 | |
1060 | |
1061 | QByteArray KWalletD::readMap(int handle, const QString& folder, const QString& key, const QString& appid) { |
1062 | KWallet::Backend *b; |
1063 | |
1064 | if ((b = getWallet(appid, handle))) { |
1065 | b->setFolder(folder); |
1066 | KWallet::Entry *e = b->readEntry(key); |
1067 | if (e && e->type() == KWallet::Wallet::Map) { |
1068 | return e->map(); |
1069 | } |
1070 | } |
1071 | |
1072 | return QByteArray(); |
1073 | } |
1074 | |
1075 | |
1076 | QVariantMap KWalletD::readMapList(int handle, const QString& folder, const QString& key, const QString& appid) { |
1077 | KWallet::Backend *b; |
1078 | |
1079 | if ((b = getWallet(appid, handle))) { |
1080 | b->setFolder(folder); |
1081 | QVariantMap rc; |
1082 | foreach (KWallet::Entry *entry, b->readEntryList(key)) { |
1083 | if (entry->type() == KWallet::Wallet::Map) { |
1084 | rc.insert(entry->key(), entry->map()); |
1085 | } |
1086 | } |
1087 | return rc; |
1088 | } |
1089 | |
1090 | return QVariantMap(); |
1091 | } |
1092 | |
1093 | |
1094 | QByteArray KWalletD::readEntry(int handle, const QString& folder, const QString& key, const QString& appid) { |
1095 | KWallet::Backend *b; |
1096 | |
1097 | if ((b = getWallet(appid, handle))) { |
1098 | b->setFolder(folder); |
1099 | KWallet::Entry *e = b->readEntry(key); |
1100 | if (e) { |
1101 | return e->value(); |
1102 | } |
1103 | } |
1104 | |
1105 | return QByteArray(); |
1106 | } |
1107 | |
1108 | |
1109 | QVariantMap KWalletD::readEntryList(int handle, const QString& folder, const QString& key, const QString& appid) { |
1110 | KWallet::Backend *b; |
1111 | |
1112 | if ((b = getWallet(appid, handle))) { |
1113 | b->setFolder(folder); |
1114 | QVariantMap rc; |
1115 | foreach (KWallet::Entry *entry, b->readEntryList(key)) { |
1116 | rc.insert(entry->key(), entry->value()); |
1117 | } |
1118 | return rc; |
1119 | } |
1120 | |
1121 | return QVariantMap(); |
1122 | } |
1123 | |
1124 | |
1125 | QStringList KWalletD::entryList(int handle, const QString& folder, const QString& appid) { |
1126 | KWallet::Backend *b; |
1127 | |
1128 | if ((b = getWallet(appid, handle))) { |
1129 | b->setFolder(folder); |
1130 | return b->entryList(); |
1131 | } |
1132 | |
1133 | return QStringList(); |
1134 | } |
1135 | |
1136 | |
1137 | QString KWalletD::readPassword(int handle, const QString& folder, const QString& key, const QString& appid) { |
1138 | KWallet::Backend *b; |
1139 | |
1140 | if ((b = getWallet(appid, handle))) { |
1141 | b->setFolder(folder); |
1142 | KWallet::Entry *e = b->readEntry(key); |
1143 | if (e && e->type() == KWallet::Wallet::Password) { |
1144 | return e->password(); |
1145 | } |
1146 | } |
1147 | |
1148 | return QString(); |
1149 | } |
1150 | |
1151 | |
1152 | QVariantMap KWalletD::readPasswordList(int handle, const QString& folder, const QString& key, const QString& appid) { |
1153 | KWallet::Backend *b; |
1154 | |
1155 | if ((b = getWallet(appid, handle))) { |
1156 | b->setFolder(folder); |
1157 | QVariantMap rc; |
1158 | foreach (KWallet::Entry *entry, b->readEntryList(key)) { |
1159 | if (entry->type() == KWallet::Wallet::Password) { |
1160 | rc.insert(entry->key(), entry->password()); |
1161 | } |
1162 | } |
1163 | return rc; |
1164 | } |
1165 | |
1166 | return QVariantMap(); |
1167 | } |
1168 | |
1169 | |
1170 | int KWalletD::writeMap(int handle, const QString& folder, const QString& key, const QByteArray& value, const QString& appid) { |
1171 | KWallet::Backend *b; |
1172 | |
1173 | if ((b = getWallet(appid, handle))) { |
1174 | b->setFolder(folder); |
1175 | KWallet::Entry e; |
1176 | e.setKey(key); |
1177 | e.setValue(value); |
1178 | e.setType(KWallet::Wallet::Map); |
1179 | b->writeEntry(&e); |
1180 | initiateSync(handle); |
1181 | emitFolderUpdated(b->walletName(), folder); |
1182 | return 0; |
1183 | } |
1184 | |
1185 | return -1; |
1186 | } |
1187 | |
1188 | |
1189 | int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, const QByteArray& value, int entryType, const QString& appid) { |
1190 | KWallet::Backend *b; |
1191 | |
1192 | if ((b = getWallet(appid, handle))) { |
1193 | b->setFolder(folder); |
1194 | KWallet::Entry e; |
1195 | e.setKey(key); |
1196 | e.setValue(value); |
1197 | e.setType(KWallet::Wallet::EntryType(entryType)); |
1198 | b->writeEntry(&e); |
1199 | initiateSync(handle); |
1200 | emitFolderUpdated(b->walletName(), folder); |
1201 | return 0; |
1202 | } |
1203 | |
1204 | return -1; |
1205 | } |
1206 | |
1207 | |
1208 | int KWalletD::writeEntry(int handle, const QString& folder, const QString& key, const QByteArray& value, const QString& appid) { |
1209 | KWallet::Backend *b; |
1210 | |
1211 | if ((b = getWallet(appid, handle))) { |
1212 | b->setFolder(folder); |
1213 | KWallet::Entry e; |
1214 | e.setKey(key); |
1215 | e.setValue(value); |
1216 | e.setType(KWallet::Wallet::Stream); |
1217 | b->writeEntry(&e); |
1218 | initiateSync(handle); |
1219 | emitFolderUpdated(b->walletName(), folder); |
1220 | return 0; |
1221 | } |
1222 | |
1223 | return -1; |
1224 | } |
1225 | |
1226 | |
1227 | int KWalletD::writePassword(int handle, const QString& folder, const QString& key, const QString& value, const QString& appid) { |
1228 | KWallet::Backend *b; |
1229 | |
1230 | if ((b = getWallet(appid, handle))) { |
1231 | b->setFolder(folder); |
1232 | KWallet::Entry e; |
1233 | e.setKey(key); |
1234 | e.setValue(value); |
1235 | e.setType(KWallet::Wallet::Password); |
1236 | b->writeEntry(&e); |
1237 | initiateSync(handle); |
1238 | emitFolderUpdated(b->walletName(), folder); |
1239 | return 0; |
1240 | } |
1241 | |
1242 | return -1; |
1243 | } |
1244 | |
1245 | |
1246 | int KWalletD::entryType(int handle, const QString& folder, const QString& key, const QString& appid) { |
1247 | KWallet::Backend *b; |
1248 | |
1249 | if ((b = getWallet(appid, handle))) { |
1250 | if (!b->hasFolder(folder)) { |
1251 | return KWallet::Wallet::Unknown; |
1252 | } |
1253 | b->setFolder(folder); |
1254 | if (b->hasEntry(key)) { |
1255 | return b->readEntry(key)->type(); |
1256 | } |
1257 | } |
1258 | |
1259 | return KWallet::Wallet::Unknown; |
1260 | } |
1261 | |
1262 | |
1263 | bool KWalletD::hasEntry(int handle, const QString& folder, const QString& key, const QString& appid) { |
1264 | KWallet::Backend *b; |
1265 | |
1266 | if ((b = getWallet(appid, handle))) { |
1267 | if (!b->hasFolder(folder)) { |
1268 | return false; |
1269 | } |
1270 | b->setFolder(folder); |
1271 | return b->hasEntry(key); |
1272 | } |
1273 | |
1274 | return false; |
1275 | } |
1276 | |
1277 | |
1278 | int KWalletD::removeEntry(int handle, const QString& folder, const QString& key, const QString& appid) { |
1279 | KWallet::Backend *b; |
1280 | |
1281 | if ((b = getWallet(appid, handle))) { |
1282 | if (!b->hasFolder(folder)) { |
1283 | return 0; |
1284 | } |
1285 | b->setFolder(folder); |
1286 | bool rc = b->removeEntry(key); |
1287 | initiateSync(handle); |
1288 | emitFolderUpdated(b->walletName(), folder); |
1289 | return rc ? 0 : -3; |
1290 | } |
1291 | |
1292 | return -1; |
1293 | } |
1294 | |
1295 | |
1296 | void KWalletD::slotServiceOwnerChanged(const QString& name, const QString &oldOwner, |
1297 | const QString& newOwner) |
1298 | { |
1299 | Q_UNUSED(name); |
1300 | kDebug() << "slotServiceOwnerChanged " << name << ", " << oldOwner << ", " << newOwner; |
1301 | |
1302 | if (!newOwner.isEmpty()) { |
1303 | return; // no application exit, don't care. |
1304 | } |
1305 | |
1306 | // as we don't have the application id we have to cycle |
1307 | // all sessions. As an application can basically open wallets |
1308 | // with several appids, we can't stop if we found one. |
1309 | QString service(oldOwner); |
1310 | QList<KWalletAppHandlePair> sessremove(_sessions.findSessions(service)); |
1311 | KWallet::Backend *b = 0; |
1312 | |
1313 | // check all sessions for wallets to close |
1314 | Q_FOREACH(const KWalletAppHandlePair &s, sessremove) { |
1315 | b = getWallet(s.first, s.second); |
1316 | if (b) { |
1317 | b->deref(); |
1318 | internalClose(b, s.second, false); |
1319 | } |
1320 | } |
1321 | |
1322 | // remove all the sessions in case they aren't gone yet |
1323 | Q_FOREACH(const KWalletAppHandlePair &s, sessremove) { |
1324 | _sessions.removeSession(s.first, service, s.second); |
1325 | } |
1326 | |
1327 | // cancel all open-transactions still running for the service |
1328 | QList<KWalletTransaction*>::iterator tit; |
1329 | for (tit = _transactions.begin(); tit != _transactions.end(); ++tit) { |
1330 | if ((*tit)->tType == KWalletTransaction::Open && (*tit)->service == oldOwner) { |
1331 | delete (*tit); |
1332 | *tit = 0; |
1333 | } |
1334 | } |
1335 | _transactions.removeAll(0); |
1336 | |
1337 | // if there's currently an open-transaction being handled, |
1338 | // mark it as cancelled. |
1339 | if (_curtrans && _curtrans->tType == KWalletTransaction::Open && |
1340 | _curtrans->service == oldOwner) { |
1341 | kDebug() << "Cancelling current transaction!" ; |
1342 | _curtrans->cancelled = true; |
1343 | } |
1344 | _serviceWatcher.removeWatchedService(oldOwner); |
1345 | } |
1346 | |
1347 | KWallet::Backend *KWalletD::getWallet(const QString& appid, int handle) { |
1348 | if (handle == 0) { |
1349 | return 0L; |
1350 | } |
1351 | |
1352 | KWallet::Backend *w = _wallets.value(handle); |
1353 | |
1354 | if (w) { // the handle is valid |
1355 | if (_sessions.hasSession(appid, handle)) { |
1356 | // the app owns this handle |
1357 | _failed = 0; |
1358 | if (_closeIdle) { |
1359 | _closeTimers.resetTimer(handle, _idleTime); |
1360 | } |
1361 | return w; |
1362 | } |
1363 | } |
1364 | |
1365 | if (++_failed > 5) { |
1366 | _failed = 0; |
1367 | QTimer::singleShot(0, this, SLOT(notifyFailures())); |
1368 | } |
1369 | |
1370 | return 0L; |
1371 | } |
1372 | |
1373 | |
1374 | void KWalletD::notifyFailures() { |
1375 | if (!_showingFailureNotify) { |
1376 | _showingFailureNotify = true; |
1377 | 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" )); |
1378 | _showingFailureNotify = false; |
1379 | } |
1380 | } |
1381 | |
1382 | |
1383 | void KWalletD::doCloseSignals(int handle, const QString& wallet) { |
1384 | emit walletClosed(handle); |
1385 | emit walletClosed(wallet); |
1386 | if (_wallets.isEmpty()) { |
1387 | emit allWalletsClosed(); |
1388 | } |
1389 | } |
1390 | |
1391 | |
1392 | int KWalletD::renameEntry(int handle, const QString& folder, const QString& oldName, const QString& newName, const QString& appid) { |
1393 | KWallet::Backend *b; |
1394 | |
1395 | if ((b = getWallet(appid, handle))) { |
1396 | b->setFolder(folder); |
1397 | int rc = b->renameEntry(oldName, newName); |
1398 | initiateSync(handle); |
1399 | emitFolderUpdated(b->walletName(), folder); |
1400 | return rc; |
1401 | } |
1402 | |
1403 | return -1; |
1404 | } |
1405 | |
1406 | |
1407 | QStringList KWalletD::users(const QString& wallet) const { |
1408 | const QPair<int,KWallet::Backend*> walletInfo = findWallet(wallet); |
1409 | return _sessions.getApplications(walletInfo.first); |
1410 | } |
1411 | |
1412 | |
1413 | bool KWalletD::disconnectApplication(const QString& wallet, const QString& application) { |
1414 | const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
1415 | int handle = walletInfo.first; |
1416 | KWallet::Backend* backend = walletInfo.second; |
1417 | |
1418 | if (handle != -1 && _sessions.hasSession(application, handle)) { |
1419 | int removed = _sessions.removeAllSessions(application, handle); |
1420 | |
1421 | for (int i = 0; i < removed; ++i) { |
1422 | backend->deref(); |
1423 | } |
1424 | internalClose(backend, handle, false); |
1425 | |
1426 | emit applicationDisconnected(wallet, application); |
1427 | return true; |
1428 | } |
1429 | |
1430 | return false; |
1431 | } |
1432 | |
1433 | |
1434 | void KWalletD::emitFolderUpdated(const QString& wallet, const QString& folder) { |
1435 | emit folderUpdated(wallet, folder); |
1436 | } |
1437 | |
1438 | |
1439 | void KWalletD::emitWalletListDirty() { |
1440 | emit walletListDirty(); |
1441 | } |
1442 | |
1443 | |
1444 | void KWalletD::reconfigure() { |
1445 | KConfig cfg("kwalletrc" ); |
1446 | KConfigGroup walletGroup(&cfg, "Wallet" ); |
1447 | _firstUse = walletGroup.readEntry("First Use" , true); |
1448 | _enabled = walletGroup.readEntry("Enabled" , true); |
1449 | _launchManager = walletGroup.readEntry("Launch Manager" , false); |
1450 | _leaveOpen = walletGroup.readEntry("Leave Open" , false); |
1451 | bool idleSave = _closeIdle; |
1452 | _closeIdle = walletGroup.readEntry("Close When Idle" , false); |
1453 | _openPrompt = walletGroup.readEntry("Prompt on Open" , false); |
1454 | int timeSave = _idleTime; |
1455 | // in minutes! |
1456 | _idleTime = walletGroup.readEntry("Idle Timeout" , 10) * 60 * 1000; |
1457 | #ifdef Q_WS_X11 |
1458 | if (walletGroup.readEntry("Close on Screensaver" , false)) { |
1459 | // BUG 254273 : if kwalletd starts before the screen saver, then the connection fails and kwalletd never receives it's notifications |
1460 | // To fix this, we use a timer and perform periodic connection attempts until connection succeeds |
1461 | QTimer::singleShot(0, this, SLOT(connectToScreenSaver())); |
1462 | } else { |
1463 | if (screensaver && screensaver->isValid()) { |
1464 | screensaver->disconnect(SIGNAL(ActiveChanged(bool)), this, SLOT(screenSaverChanged(bool))); |
1465 | delete screensaver; |
1466 | screensaver = 0; |
1467 | } |
1468 | } |
1469 | #endif |
1470 | // Handle idle changes |
1471 | if (_closeIdle) { |
1472 | if (_idleTime != timeSave) { // Timer length changed |
1473 | Wallets::const_iterator it = _wallets.constBegin(); |
1474 | const Wallets::const_iterator end = _wallets.constEnd(); |
1475 | for (; it != end; ++it) { |
1476 | _closeTimers.resetTimer(it.key(), _idleTime); |
1477 | } |
1478 | } |
1479 | |
1480 | if (!idleSave) { // add timers for all the wallets |
1481 | Wallets::const_iterator it = _wallets.constBegin(); |
1482 | const Wallets::const_iterator end = _wallets.constEnd(); |
1483 | for (; it != end; ++it) { |
1484 | _closeTimers.addTimer(it.key(), _idleTime); |
1485 | } |
1486 | } |
1487 | } else { |
1488 | _closeTimers.clear(); |
1489 | } |
1490 | |
1491 | // Update the implicit allow stuff |
1492 | _implicitAllowMap.clear(); |
1493 | const KConfigGroup autoAllowGroup(&cfg, "Auto Allow" ); |
1494 | QStringList entries = autoAllowGroup.entryMap().keys(); |
1495 | for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) { |
1496 | _implicitAllowMap[*i] = autoAllowGroup.readEntry(*i, QStringList()); |
1497 | } |
1498 | |
1499 | // Update the implicit allow stuff |
1500 | _implicitDenyMap.clear(); |
1501 | const KConfigGroup autoDenyGroup(&cfg, "Auto Deny" ); |
1502 | entries = autoDenyGroup.entryMap().keys(); |
1503 | for (QStringList::const_iterator i = entries.constBegin(); i != entries.constEnd(); ++i) { |
1504 | _implicitDenyMap[*i] = autoDenyGroup.readEntry(*i, QStringList()); |
1505 | } |
1506 | |
1507 | // Update if wallet was enabled/disabled |
1508 | if (!_enabled) { // close all wallets |
1509 | while (!_wallets.isEmpty()) { |
1510 | Wallets::const_iterator it = _wallets.constBegin(); |
1511 | internalClose(it.value(), it.key(), true); |
1512 | } |
1513 | KUniqueApplication::exit(0); |
1514 | } |
1515 | } |
1516 | |
1517 | |
1518 | bool KWalletD::isEnabled() const { |
1519 | return _enabled; |
1520 | } |
1521 | |
1522 | |
1523 | bool KWalletD::folderDoesNotExist(const QString& wallet, const QString& folder) { |
1524 | if (!wallets().contains(wallet)) { |
1525 | return true; |
1526 | } |
1527 | |
1528 | const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
1529 | if (walletInfo.second) { |
1530 | return walletInfo.second->folderDoesNotExist(folder); |
1531 | } |
1532 | |
1533 | KWallet::Backend *b = new KWallet::Backend(wallet); |
1534 | b->open(QByteArray()); |
1535 | bool rc = b->folderDoesNotExist(folder); |
1536 | delete b; |
1537 | return rc; |
1538 | } |
1539 | |
1540 | |
1541 | bool KWalletD::keyDoesNotExist(const QString& wallet, const QString& folder, const QString& key) { |
1542 | if (!wallets().contains(wallet)) { |
1543 | return true; |
1544 | } |
1545 | |
1546 | const QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
1547 | if (walletInfo.second) { |
1548 | return walletInfo.second->entryDoesNotExist(folder, key); |
1549 | } |
1550 | |
1551 | KWallet::Backend *b = new KWallet::Backend(wallet); |
1552 | b->open(QByteArray()); |
1553 | bool rc = b->entryDoesNotExist(folder, key); |
1554 | delete b; |
1555 | return rc; |
1556 | } |
1557 | |
1558 | |
1559 | bool KWalletD::implicitAllow(const QString& wallet, const QString& app) { |
1560 | return _implicitAllowMap[wallet].contains(app); |
1561 | } |
1562 | |
1563 | |
1564 | bool KWalletD::implicitDeny(const QString& wallet, const QString& app) { |
1565 | return _implicitDenyMap[wallet].contains(app); |
1566 | } |
1567 | |
1568 | |
1569 | void KWalletD::timedOutClose(int id) { |
1570 | KWallet::Backend *w = _wallets.value(id); |
1571 | if (w) { |
1572 | internalClose(w, id, true); |
1573 | } |
1574 | } |
1575 | |
1576 | |
1577 | void KWalletD::closeAllWallets() { |
1578 | Wallets walletsCopy = _wallets; |
1579 | |
1580 | Wallets::const_iterator it = walletsCopy.constBegin(); |
1581 | const Wallets::const_iterator end = walletsCopy.constEnd(); |
1582 | for (; it != end; ++it) { |
1583 | internalClose(it.value(), it.key(), true); |
1584 | } |
1585 | |
1586 | walletsCopy.clear(); |
1587 | |
1588 | // All of this should be basically noop. Let's just be safe. |
1589 | _wallets.clear(); |
1590 | } |
1591 | |
1592 | |
1593 | QString KWalletD::networkWallet() { |
1594 | return KWallet::Wallet::NetworkWallet(); |
1595 | } |
1596 | |
1597 | |
1598 | QString KWalletD::localWallet() { |
1599 | return KWallet::Wallet::LocalWallet(); |
1600 | } |
1601 | |
1602 | void KWalletD::screenSaverChanged(bool s) |
1603 | { |
1604 | if (s) |
1605 | closeAllWallets(); |
1606 | } |
1607 | |
1608 | void KWalletD::activatePasswordDialog() |
1609 | { |
1610 | checkActiveDialog(); |
1611 | } |
1612 | |
1613 | int KWalletD::pamOpen(const QString &wallet, const QByteArray &passwordHash, int sessionTimeout) |
1614 | { |
1615 | if (_processing) { |
1616 | return -1; |
1617 | } |
1618 | |
1619 | if (!QRegExp("^[\\w\\^\\&\\'\\@\\{\\}\\[\\]\\,\\$\\=\\!\\-\\#\\(\\)\\%\\.\\+\\_\\s]+$" ).exactMatch(wallet)) { |
1620 | return -1; |
1621 | } |
1622 | |
1623 | // check if the wallet is already open |
1624 | QPair<int, KWallet::Backend*> walletInfo = findWallet(wallet); |
1625 | int rc = walletInfo.first; |
1626 | if (rc != -1) { |
1627 | return rc;//Wallet already opened, return handle |
1628 | } |
1629 | |
1630 | KWallet::Backend *b = 0; |
1631 | //If the wallet we want to open does not exists. create it and set pam hash |
1632 | if (!wallets().contains(wallet)) { |
1633 | b = new KWallet::Backend(wallet); |
1634 | b->setCipherType(KWallet::BACKEND_CIPHER_BLOWFISH); |
1635 | } else { |
1636 | b = new KWallet::Backend(wallet); |
1637 | } |
1638 | |
1639 | if (_wallets.count() > 20) { |
1640 | return -1; |
1641 | } |
1642 | |
1643 | int openrc = b->openPreHashed(passwordHash); |
1644 | if (openrc != 0 || !b->isOpen()) { |
1645 | return -1; |
1646 | } |
1647 | |
1648 | // opening the wallet was successful |
1649 | int handle = generateHandle(); |
1650 | _wallets.insert(handle, b); |
1651 | _syncTimers.addTimer(handle, _syncTime); |
1652 | |
1653 | // don't reference the wallet or add a session so it |
1654 | // can be reclosed easily. |
1655 | |
1656 | if (sessionTimeout > 0) { |
1657 | _closeTimers.addTimer(handle, sessionTimeout); |
1658 | } else if (_closeIdle) { |
1659 | _closeTimers.addTimer(handle, _idleTime); |
1660 | } |
1661 | emit walletOpened(wallet); |
1662 | |
1663 | if (_wallets.count() == 1 && _launchManager) { |
1664 | KToolInvocation::startServiceByDesktopName("kwalletmanager-kwalletd" ); |
1665 | } |
1666 | |
1667 | return handle; |
1668 | } |
1669 | |
1670 | #include "kwalletd.moc" |
1671 | |