keychain_unix.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. /******************************************************************************
  2. * Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
  3. * *
  4. * This program is distributed in the hope that it will be useful, but *
  5. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
  6. * or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
  7. * details, check the accompanying file 'COPYING'. *
  8. *****************************************************************************/
  9. #include "keychain_p.h"
  10. #include "gnomekeyring_p.h"
  11. #include "libsecret_p.h"
  12. #include "plaintextstore_p.h"
  13. #include <QScopedPointer>
  14. using namespace QKeychain;
  15. enum KeyringBackend {
  16. Backend_LibSecretKeyring,
  17. Backend_GnomeKeyring,
  18. Backend_Kwallet4,
  19. Backend_Kwallet5
  20. };
  21. enum DesktopEnvironment {
  22. DesktopEnv_Gnome,
  23. DesktopEnv_Kde4,
  24. DesktopEnv_Plasma5,
  25. DesktopEnv_Unity,
  26. DesktopEnv_Xfce,
  27. DesktopEnv_Other
  28. };
  29. // the following detection algorithm is derived from chromium,
  30. // licensed under BSD, see base/nix/xdg_util.cc
  31. static DesktopEnvironment getKdeVersion() {
  32. QByteArray value = qgetenv("KDE_SESSION_VERSION");
  33. if ( value == "5" ) {
  34. return DesktopEnv_Plasma5;
  35. } else if (value == "4" ) {
  36. return DesktopEnv_Kde4;
  37. } else {
  38. // most likely KDE3
  39. return DesktopEnv_Other;
  40. }
  41. }
  42. static DesktopEnvironment detectDesktopEnvironment() {
  43. QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
  44. if ( xdgCurrentDesktop == "GNOME" ) {
  45. return DesktopEnv_Gnome;
  46. } else if ( xdgCurrentDesktop == "Unity" ) {
  47. return DesktopEnv_Unity;
  48. } else if ( xdgCurrentDesktop == "KDE" ) {
  49. return getKdeVersion();
  50. } else if ( xdgCurrentDesktop == "XFCE" ) {
  51. return DesktopEnv_Xfce;
  52. }
  53. QByteArray desktopSession = qgetenv("DESKTOP_SESSION");
  54. if ( desktopSession == "gnome" ) {
  55. return DesktopEnv_Gnome;
  56. } else if ( desktopSession == "kde" ) {
  57. return getKdeVersion();
  58. } else if ( desktopSession == "kde4" ) {
  59. return DesktopEnv_Kde4;
  60. } else if ( desktopSession.contains("xfce") || desktopSession == "xubuntu" ) {
  61. return DesktopEnv_Xfce;
  62. }
  63. if ( !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty() ) {
  64. return DesktopEnv_Gnome;
  65. } else if ( !qgetenv("KDE_FULL_SESSION").isEmpty() ) {
  66. return getKdeVersion();
  67. }
  68. return DesktopEnv_Other;
  69. }
  70. static bool isKwallet5Available()
  71. {
  72. if (!QDBusConnection::sessionBus().isConnected())
  73. return false;
  74. org::kde::KWallet iface(
  75. QLatin1String("org.kde.kwalletd5"),
  76. QLatin1String("/modules/kwalletd5"),
  77. QDBusConnection::sessionBus());
  78. // At this point iface.isValid() can return false even though the
  79. // interface is activatable by making a call. Hence we check whether
  80. // a wallet can be opened.
  81. iface.setTimeout(500);
  82. QDBusMessage reply = iface.call(QLatin1String("networkWallet"));
  83. return reply.type() == QDBusMessage::ReplyMessage;
  84. }
  85. static KeyringBackend detectKeyringBackend()
  86. {
  87. /* The secret service dbus api, accessible through libsecret, is supposed
  88. * to unify password services.
  89. *
  90. * Unfortunately at the time of Kubuntu 18.04 the secret service backend
  91. * in KDE is gnome-keyring-daemon - using it has several complications:
  92. * - the default collection isn't opened on session start, so users need
  93. * to manually unlock it when the first application uses it
  94. * - it's separate from the kwallet5 keyring, so switching to it means the
  95. * existing keyring data can't be accessed anymore
  96. *
  97. * Thus we still prefer kwallet backends on KDE even if libsecret is
  98. * available.
  99. */
  100. switch (detectDesktopEnvironment()) {
  101. case DesktopEnv_Kde4:
  102. return Backend_Kwallet4;
  103. case DesktopEnv_Plasma5:
  104. if (isKwallet5Available()) {
  105. return Backend_Kwallet5;
  106. }
  107. if (LibSecretKeyring::isAvailable()) {
  108. return Backend_LibSecretKeyring;
  109. }
  110. if (GnomeKeyring::isAvailable()) {
  111. return Backend_GnomeKeyring;
  112. }
  113. // During startup the keychain backend might just not have started yet
  114. return Backend_Kwallet5;
  115. case DesktopEnv_Gnome:
  116. case DesktopEnv_Unity:
  117. case DesktopEnv_Xfce:
  118. case DesktopEnv_Other:
  119. default:
  120. if (LibSecretKeyring::isAvailable()) {
  121. return Backend_LibSecretKeyring;
  122. }
  123. if (GnomeKeyring::isAvailable()) {
  124. return Backend_GnomeKeyring;
  125. }
  126. if (isKwallet5Available()) {
  127. return Backend_Kwallet5;
  128. }
  129. // During startup the keychain backend might just not have started yet
  130. //
  131. // This doesn't need to be libsecret because LibSecretKeyring::isAvailable()
  132. // only fails if the libsecret shared library couldn't be loaded. In contrast
  133. // to that GnomeKeyring::isAvailable() can return false if the shared library
  134. // *was* loaded but its libgnome_keyring::is_available() returned false.
  135. //
  136. // In the future there should be a difference between "API available" and
  137. // "keychain available".
  138. return Backend_GnomeKeyring;
  139. }
  140. }
  141. static KeyringBackend getKeyringBackend()
  142. {
  143. static KeyringBackend backend = detectKeyringBackend();
  144. return backend;
  145. }
  146. static void kwalletReadPasswordScheduledStartImpl(const char * service, const char * path, ReadPasswordJobPrivate * priv) {
  147. if ( QDBusConnection::sessionBus().isConnected() )
  148. {
  149. priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv );
  150. const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
  151. QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv );
  152. priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) );
  153. }
  154. else
  155. {
  156. // D-Bus is not reachable so none can tell us something about KWalletd
  157. QDBusError err( QDBusError::NoServer, ReadPasswordJobPrivate::tr("D-Bus is not running") );
  158. priv->fallbackOnError( err );
  159. }
  160. }
  161. void ReadPasswordJobPrivate::scheduledStart() {
  162. switch ( getKeyringBackend() ) {
  163. case Backend_LibSecretKeyring: {
  164. if ( !LibSecretKeyring::findPassword(key, q->service(), this) ) {
  165. q->emitFinishedWithError( OtherError, tr("Unknown error") );
  166. }
  167. } break;
  168. case Backend_GnomeKeyring:
  169. this->mode = JobPrivate::Text;
  170. if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(),
  171. q->service().toUtf8().constData(),
  172. "plaintext",
  173. reinterpret_cast<GnomeKeyring::OperationGetStringCallback>( &JobPrivate::gnomeKeyring_readCb ),
  174. this, 0 ) )
  175. q->emitFinishedWithError( OtherError, tr("Unknown error") );
  176. break;
  177. case Backend_Kwallet4:
  178. kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd", "/modules/kwalletd", this);
  179. break;
  180. case Backend_Kwallet5:
  181. kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd5", "/modules/kwalletd5", this);
  182. break;
  183. }
  184. }
  185. void JobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher)
  186. {
  187. watcher->deleteLater();
  188. const QDBusPendingReply<QString> reply = *watcher;
  189. const QDBusPendingReply<int> pendingReply = iface->open( reply.value(), 0, q->service() );
  190. QDBusPendingCallWatcher* pendingWatcher = new QDBusPendingCallWatcher( pendingReply, this );
  191. connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
  192. this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) );
  193. }
  194. static QPair<Error, QString> mapGnomeKeyringError( int result )
  195. {
  196. Q_ASSERT( result != GnomeKeyring::RESULT_OK );
  197. switch ( result ) {
  198. case GnomeKeyring::RESULT_DENIED:
  199. return qMakePair( AccessDenied, QObject::tr("Access to keychain denied") );
  200. case GnomeKeyring::RESULT_NO_KEYRING_DAEMON:
  201. return qMakePair( NoBackendAvailable, QObject::tr("No keyring daemon") );
  202. case GnomeKeyring::RESULT_ALREADY_UNLOCKED:
  203. return qMakePair( OtherError, QObject::tr("Already unlocked") );
  204. case GnomeKeyring::RESULT_NO_SUCH_KEYRING:
  205. return qMakePair( OtherError, QObject::tr("No such keyring") );
  206. case GnomeKeyring::RESULT_BAD_ARGUMENTS:
  207. return qMakePair( OtherError, QObject::tr("Bad arguments") );
  208. case GnomeKeyring::RESULT_IO_ERROR:
  209. return qMakePair( OtherError, QObject::tr("I/O error") );
  210. case GnomeKeyring::RESULT_CANCELLED:
  211. return qMakePair( OtherError, QObject::tr("Cancelled") );
  212. case GnomeKeyring::RESULT_KEYRING_ALREADY_EXISTS:
  213. return qMakePair( OtherError, QObject::tr("Keyring already exists") );
  214. case GnomeKeyring::RESULT_NO_MATCH:
  215. return qMakePair( EntryNotFound, QObject::tr("No match") );
  216. default:
  217. break;
  218. }
  219. return qMakePair( OtherError, QObject::tr("Unknown error") );
  220. }
  221. void JobPrivate::gnomeKeyring_readCb( int result, const char* string, JobPrivate* self )
  222. {
  223. if ( result == GnomeKeyring::RESULT_OK ) {
  224. if (self->mode == JobPrivate::Text)
  225. self->data = QByteArray(string);
  226. else
  227. self->data = QByteArray::fromBase64(string);
  228. self->q->emitFinished();
  229. } else if (self->mode == JobPrivate::Text) {
  230. self->mode = JobPrivate::Binary;
  231. if ( !GnomeKeyring::find_network_password( self->key.toUtf8().constData(),
  232. self->q->service().toUtf8().constData(),
  233. "base64",
  234. reinterpret_cast<GnomeKeyring::OperationGetStringCallback>( &JobPrivate::gnomeKeyring_readCb ),
  235. self, 0 ) )
  236. self->q->emitFinishedWithError( OtherError, tr("Unknown error") );
  237. } else {
  238. const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
  239. self->q->emitFinishedWithError( errorResult.first, errorResult.second );
  240. }
  241. }
  242. void ReadPasswordJobPrivate::fallbackOnError(const QDBusError& err )
  243. {
  244. PlainTextStore plainTextStore( q->service(), q->settings() );
  245. if ( q->insecureFallback() && plainTextStore.contains( key ) ) {
  246. mode = plainTextStore.readMode( key );
  247. data = plainTextStore.readData( key );
  248. if ( plainTextStore.error() != NoError )
  249. q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
  250. else
  251. q->emitFinished();
  252. } else {
  253. if ( err.type() == QDBusError::ServiceUnknown ) //KWalletd not running
  254. q->emitFinishedWithError( NoBackendAvailable, tr("No keychain service available") );
  255. else
  256. q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
  257. }
  258. }
  259. void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
  260. watcher->deleteLater();
  261. const QDBusPendingReply<int> reply = *watcher;
  262. if ( reply.isError() ) {
  263. fallbackOnError( reply.error() );
  264. return;
  265. }
  266. PlainTextStore plainTextStore( q->service(), q->settings() );
  267. if ( plainTextStore.contains( key ) ) {
  268. // We previously stored data in the insecure QSettings, but now have KWallet available.
  269. // Do the migration
  270. data = plainTextStore.readData( key );
  271. const WritePasswordJobPrivate::Mode mode = plainTextStore.readMode( key );
  272. plainTextStore.remove( key );
  273. q->emitFinished();
  274. WritePasswordJob* j = new WritePasswordJob( q->service(), 0 );
  275. j->setSettings( q->settings() );
  276. j->setKey( key );
  277. j->setAutoDelete( true );
  278. if ( mode == WritePasswordJobPrivate::Binary )
  279. j->setBinaryData( data );
  280. else if ( mode == WritePasswordJobPrivate::Text )
  281. j->setTextData( QString::fromUtf8( data ) );
  282. else
  283. Q_ASSERT( false );
  284. j->start();
  285. return;
  286. }
  287. walletHandle = reply.value();
  288. if ( walletHandle < 0 ) {
  289. q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
  290. return;
  291. }
  292. const QDBusPendingReply<int> nextReply = iface->entryType( walletHandle, q->service(), key, q->service() );
  293. QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
  294. connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletEntryTypeFinished(QDBusPendingCallWatcher*)) );
  295. }
  296. //Must be in sync with KWallet::EntryType (kwallet.h)
  297. enum KWalletEntryType {
  298. Unknown=0,
  299. Password,
  300. Stream,
  301. Map
  302. };
  303. void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ) {
  304. watcher->deleteLater();
  305. if ( watcher->isError() ) {
  306. const QDBusError err = watcher->error();
  307. q->emitFinishedWithError( OtherError, tr("Could not determine data type: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
  308. return;
  309. }
  310. const QDBusPendingReply<int> reply = *watcher;
  311. const int value = reply.value();
  312. switch ( value ) {
  313. case Unknown:
  314. q->emitFinishedWithError( EntryNotFound, tr("Entry not found") );
  315. return;
  316. case Password:
  317. mode = Text;
  318. break;
  319. case Stream:
  320. mode = Binary;
  321. break;
  322. case Map:
  323. q->emitFinishedWithError( EntryNotFound, tr("Unsupported entry type 'Map'") );
  324. return;
  325. default:
  326. q->emitFinishedWithError( OtherError, tr("Unknown kwallet entry type '%1'").arg( value ) );
  327. return;
  328. }
  329. const QDBusPendingCall nextReply = (mode == Text)
  330. ? QDBusPendingCall( iface->readPassword( walletHandle, q->service(), key, q->service() ) )
  331. : QDBusPendingCall( iface->readEntry( walletHandle, q->service(), key, q->service() ) );
  332. QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
  333. connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) );
  334. }
  335. void ReadPasswordJobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) {
  336. if ( !watcher->isError() ) {
  337. if ( mode == Binary ) {
  338. QDBusPendingReply<QByteArray> reply = *watcher;
  339. if (reply.isValid()) {
  340. data = reply.value();
  341. }
  342. } else {
  343. QDBusPendingReply<QString> reply = *watcher;
  344. if (reply.isValid()) {
  345. data = reply.value().toUtf8();
  346. }
  347. }
  348. }
  349. JobPrivate::kwalletFinished(watcher);
  350. }
  351. static void kwalletWritePasswordScheduledStart( const char * service, const char * path, JobPrivate * priv ) {
  352. if ( QDBusConnection::sessionBus().isConnected() )
  353. {
  354. priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv );
  355. const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
  356. QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv );
  357. priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) );
  358. }
  359. else
  360. {
  361. // D-Bus is not reachable so none can tell us something about KWalletd
  362. QDBusError err( QDBusError::NoServer, WritePasswordJobPrivate::tr("D-Bus is not running") );
  363. priv->fallbackOnError( err );
  364. }
  365. }
  366. void WritePasswordJobPrivate::scheduledStart() {
  367. switch ( getKeyringBackend() ) {
  368. case Backend_LibSecretKeyring: {
  369. if ( !LibSecretKeyring::writePassword(service, key, service, mode,
  370. data, this) ) {
  371. q->emitFinishedWithError( OtherError, tr("Unknown error") );
  372. }
  373. } break;
  374. case Backend_GnomeKeyring: {
  375. QString type;
  376. QByteArray password;
  377. switch(mode) {
  378. case JobPrivate::Text:
  379. type = QLatin1String("plaintext");
  380. password = data;
  381. break;
  382. default:
  383. type = QLatin1String("base64");
  384. password = data.toBase64();
  385. break;
  386. }
  387. QByteArray service = q->service().toUtf8();
  388. if ( !GnomeKeyring::store_network_password( GnomeKeyring::GNOME_KEYRING_DEFAULT,
  389. service.constData(),
  390. key.toUtf8().constData(),
  391. service.constData(),
  392. type.toUtf8().constData(),
  393. password.constData(),
  394. reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &JobPrivate::gnomeKeyring_writeCb ),
  395. this, 0 ) )
  396. q->emitFinishedWithError( OtherError, tr("Unknown error") );
  397. }
  398. break;
  399. case Backend_Kwallet4:
  400. kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this);
  401. break;
  402. case Backend_Kwallet5:
  403. kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this);
  404. break;
  405. }
  406. }
  407. void WritePasswordJobPrivate::fallbackOnError(const QDBusError &err)
  408. {
  409. if ( !q->insecureFallback() ) {
  410. q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
  411. return;
  412. }
  413. PlainTextStore plainTextStore( q->service(), q->settings() );
  414. plainTextStore.write( key, data, mode );
  415. if ( plainTextStore.error() != NoError )
  416. q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
  417. else
  418. q->emitFinished();
  419. }
  420. void JobPrivate::gnomeKeyring_writeCb(int result, JobPrivate* self )
  421. {
  422. if ( result == GnomeKeyring::RESULT_OK ) {
  423. self->q->emitFinished();
  424. } else {
  425. const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
  426. self->q->emitFinishedWithError( errorResult.first, errorResult.second );
  427. }
  428. }
  429. void JobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
  430. watcher->deleteLater();
  431. QDBusPendingReply<int> reply = *watcher;
  432. if ( reply.isError() ) {
  433. fallbackOnError( reply.error() );
  434. return;
  435. }
  436. PlainTextStore plainTextStore( q->service(), q->settings() );
  437. if ( plainTextStore.contains( key ) ) {
  438. // If we had previously written to QSettings, but we now have a kwallet available, migrate and delete old insecure data
  439. plainTextStore.remove( key );
  440. }
  441. const int handle = reply.value();
  442. if ( handle < 0 ) {
  443. q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
  444. return;
  445. }
  446. QDBusPendingReply<int> nextReply;
  447. if ( mode == Text )
  448. nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() );
  449. else if ( mode == Binary )
  450. nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() );
  451. else
  452. nextReply = iface->removeEntry( handle, q->service(), key, q->service() );
  453. QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
  454. connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) );
  455. }
  456. void JobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) {
  457. if ( !watcher->isError() ) {
  458. if ( mode == Binary ) {
  459. QDBusPendingReply<QByteArray> reply = *watcher;
  460. if (reply.isValid()) {
  461. data = reply.value();
  462. }
  463. } else {
  464. QDBusPendingReply<QString> reply = *watcher;
  465. if (reply.isValid()) {
  466. data = reply.value().toUtf8();
  467. }
  468. }
  469. }
  470. q->emitFinished();
  471. }
  472. void DeletePasswordJobPrivate::scheduledStart() {
  473. switch ( getKeyringBackend() ) {
  474. case Backend_LibSecretKeyring: {
  475. if ( !LibSecretKeyring::deletePassword(key, q->service(), this) ) {
  476. q->emitFinishedWithError( OtherError, tr("Unknown error") );
  477. }
  478. } break;
  479. case Backend_GnomeKeyring: {
  480. if ( !GnomeKeyring::delete_network_password(
  481. key.toUtf8().constData(), q->service().toUtf8().constData(),
  482. reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &JobPrivate::gnomeKeyring_writeCb ),
  483. this, 0 ) )
  484. q->emitFinishedWithError( OtherError, tr("Unknown error") );
  485. }
  486. break;
  487. case Backend_Kwallet4:
  488. kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this);
  489. break;
  490. case Backend_Kwallet5:
  491. kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this);
  492. break;
  493. }
  494. }
  495. void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) {
  496. QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
  497. QSettings* actual = q->settings() ? q->settings() : local.data();
  498. if ( !q->insecureFallback() ) {
  499. q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2")
  500. .arg( QDBusError::errorString( err.type() ), err.message() ) );
  501. return;
  502. }
  503. actual->remove( key );
  504. actual->sync();
  505. q->emitFinished();
  506. q->emitFinished();
  507. }