Merged pidgin/main into default
[pidgin-git.git] / libpurple / plugins / keyrings / kwallet.cpp
blob9ed1537ac3ac6982c0e99334faaf5542421f3f51
1 /**
2 * @file kwallet.cpp KWallet password storage
3 * @ingroup plugins
4 */
6 /* purple
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program ; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "internal.h"
28 #include "account.h"
29 #include "core.h"
30 #include "debug.h"
31 #include "keyring.h"
32 #include "plugins.h"
33 #include "version.h"
35 #include <QQueue>
36 #include <QCoreApplication>
37 #include <kwallet.h>
39 #define KWALLET_NAME N_("KWallet")
40 #define KWALLET_DESCRIPTION N_("This plugin will store passwords in KWallet.")
41 #define KWALLET_AUTHORS { "QuLogic (qulogic[at]pidgin.im)", NULL }
42 #define KWALLET_ID "keyring-kwallet"
43 #define KWALLET_DOMAIN (g_quark_from_static_string(KWALLET_ID))
45 #define KWALLET_WALLET_NAME KWallet::Wallet::NetworkWallet()
46 #define KWALLET_APP_NAME "Libpurple"
47 #define KWALLET_FOLDER_NAME "libpurple"
49 PurpleKeyring *keyring_handler = NULL;
50 QCoreApplication *qCoreApp = NULL;
52 namespace KWalletPlugin {
54 class request
56 public:
57 virtual ~request();
58 virtual void detailedAbort(enum PurpleKeyringError error) = 0;
59 void abort();
60 virtual void execute(KWallet::Wallet *wallet) = 0;
62 protected:
63 gpointer data;
64 PurpleAccount *account;
65 QString password;
66 bool noPassword;
69 class engine : private QObject, private QQueue<request*>
71 Q_OBJECT
73 public:
74 engine();
75 ~engine();
76 void queue(request *req);
77 void abortAll();
78 static engine *instance(bool create);
79 static void closeInstance(void);
81 private slots:
82 void walletOpened(bool opened);
83 void walletClosed();
85 private:
86 static engine *pinstance;
88 bool connected;
89 bool failed;
90 bool closing;
91 bool externallyClosed;
92 bool busy;
93 bool closeAfterBusy;
95 KWallet::Wallet *wallet;
97 void reopenWallet();
98 void executeRequests();
101 class save_request : public request
103 public:
104 save_request(PurpleAccount *account, const char *password,
105 PurpleKeyringSaveCallback cb, void *data);
106 void detailedAbort(enum PurpleKeyringError error);
107 void execute(KWallet::Wallet *wallet);
109 private:
110 PurpleKeyringSaveCallback callback;
113 class read_request : public request
115 public:
116 read_request(PurpleAccount *account,
117 PurpleKeyringReadCallback cb, void *data);
118 void detailedAbort(enum PurpleKeyringError error);
119 void execute(KWallet::Wallet *wallet);
121 private:
122 PurpleKeyringReadCallback callback;
127 static gboolean
128 kwallet_is_enabled(void)
130 return KWallet::Wallet::isEnabled() ? TRUE : FALSE;
133 KWalletPlugin::engine *KWalletPlugin::engine::pinstance = NULL;
135 KWalletPlugin::request::~request()
139 void
140 KWalletPlugin::request::abort()
142 detailedAbort(PURPLE_KEYRING_ERROR_CANCELLED);
145 KWalletPlugin::engine::engine()
147 connected = false;
148 failed = false;
149 closing = false;
150 externallyClosed = false;
151 wallet = NULL;
152 busy = false;
153 closeAfterBusy = false;
155 reopenWallet();
158 void
159 KWalletPlugin::engine::reopenWallet()
161 if (closing) {
162 purple_debug_error("keyring-kwallet",
163 "wallet is closing right now\n");
164 failed = true;
165 return;
168 connected = false;
169 failed = false;
170 externallyClosed = false;
172 wallet = KWallet::Wallet::openWallet(KWALLET_WALLET_NAME, 0,
173 KWallet::Wallet::Asynchronous);
174 if (wallet == NULL) {
175 failed = true;
176 purple_debug_error("keyring-kwallet",
177 "failed opening a wallet\n");
178 return;
181 failed |= !connect(wallet, SIGNAL(walletClosed()),
182 SLOT(walletClosed()));
183 failed |= !connect(wallet, SIGNAL(walletOpened(bool)),
184 SLOT(walletOpened(bool)));
185 if (failed) {
186 purple_debug_error("keyring-kwallet",
187 "failed connecting to wallet signal\n");
191 KWalletPlugin::engine::~engine()
193 closing = true;
195 abortAll();
197 delete wallet;
199 if (pinstance == this)
200 pinstance = NULL;
203 void
204 KWalletPlugin::engine::abortAll()
206 int abortedCount = 0;
208 while (!isEmpty()) {
209 request *req = dequeue();
210 req->abort();
211 delete req;
212 abortedCount++;
215 if (abortedCount > 0) {
216 purple_debug_info("keyring-kwallet", "aborted requests: %d\n",
217 abortedCount);
221 KWalletPlugin::engine *
222 KWalletPlugin::engine::instance(bool create)
224 if (pinstance == NULL && create)
225 pinstance = new engine;
226 return pinstance;
229 void
230 KWalletPlugin::engine::closeInstance(void)
232 if (pinstance == NULL)
233 return;
234 if (pinstance->closing)
235 return;
236 if (pinstance->busy) {
237 purple_debug_misc("keyring-kwallet",
238 "current instance is busy, will be freed later\n");
239 pinstance->closeAfterBusy = true;
240 } else
241 delete pinstance;
242 pinstance = NULL;
245 void
246 KWalletPlugin::engine::walletOpened(bool opened)
248 connected = opened;
250 if (!opened) {
251 purple_debug_warning("keyring-kwallet",
252 "failed to open a wallet\n");
253 delete this;
254 return;
257 if (!wallet->hasFolder(KWALLET_FOLDER_NAME)) {
258 if (!wallet->createFolder(KWALLET_FOLDER_NAME)) {
259 purple_debug_error("keyring-kwallet",
260 "couldn't create \"" KWALLET_FOLDER_NAME
261 "\" folder in wallet\n");
262 failed = true;
265 if (!failed)
266 wallet->setFolder(KWALLET_FOLDER_NAME);
268 executeRequests();
271 void
272 KWalletPlugin::engine::walletClosed()
274 if (!closing) {
275 purple_debug_info("keyring-kwallet",
276 "wallet was externally closed\n");
277 externallyClosed = true;
278 delete wallet;
279 wallet = NULL;
283 void
284 KWalletPlugin::engine::queue(request *req)
286 enqueue(req);
287 executeRequests();
290 void
291 KWalletPlugin::engine::executeRequests()
293 if (closing || busy)
294 return;
295 busy = true;
296 if (externallyClosed) {
297 reopenWallet();
298 } else if (connected || failed) {
299 while (!isEmpty()) {
300 request *req = dequeue();
301 if (connected)
302 req->execute(wallet);
303 else
304 req->abort();
305 delete req;
307 } else if (purple_debug_is_verbose()) {
308 purple_debug_misc("keyring-kwallet", "not yet connected\n");
310 busy = false;
311 if (closeAfterBusy) {
312 purple_debug_misc("keyring-kwallet",
313 "instance freed after being busy\n");
314 delete this;
318 KWalletPlugin::save_request::save_request(PurpleAccount *acc, const char *pw,
319 PurpleKeyringSaveCallback cb, void *userdata)
321 account = acc;
322 data = userdata;
323 callback = cb;
324 password = QString(pw);
325 noPassword = (pw == NULL);
328 KWalletPlugin::read_request::read_request(PurpleAccount *acc,
329 PurpleKeyringReadCallback cb, void *userdata)
331 account = acc;
332 data = userdata;
333 callback = cb;
334 password = QString();
337 void
338 KWalletPlugin::save_request::detailedAbort(enum PurpleKeyringError error)
340 GError *gerror;
341 if (callback == NULL)
342 return;
344 gerror = g_error_new(PURPLE_KEYRING_ERROR, error,
345 _("Failed to save password."));
346 callback(account, gerror, data);
347 g_error_free(gerror);
350 void
351 KWalletPlugin::read_request::detailedAbort(enum PurpleKeyringError error)
353 GError *gerror;
354 if (callback == NULL)
355 return;
357 gerror = g_error_new(PURPLE_KEYRING_ERROR, error,
358 _("Failed to read password."));
359 callback(account, NULL, gerror, data);
360 g_error_free(gerror);
363 static QString
364 kwallet_account_key(PurpleAccount *account)
366 return QString(purple_account_get_protocol_id(account)) + ":" +
367 purple_account_get_username(account);
370 void
371 KWalletPlugin::read_request::execute(KWallet::Wallet *wallet)
373 int result;
375 g_return_if_fail(wallet != NULL);
377 result = wallet->readPassword(kwallet_account_key(account), password);
379 if (result != 0) {
380 purple_debug_warning("keyring-kwallet",
381 "failed to read password, result was %d\n", result);
382 abort();
383 return;
386 purple_debug_misc("keyring-kwallet",
387 "Got password for account %s (%s).\n",
388 purple_account_get_username(account),
389 purple_account_get_protocol_id(account));
391 if (callback != NULL)
392 callback(account, password.toUtf8().constData(), NULL, data);
395 void
396 KWalletPlugin::save_request::execute(KWallet::Wallet *wallet)
398 int result;
400 g_return_if_fail(wallet != NULL);
402 if (noPassword)
403 result = wallet->removeEntry(kwallet_account_key(account));
404 else {
405 result = wallet->writePassword(kwallet_account_key(account),
406 password);
409 if (result != 0) {
410 purple_debug_warning("keyring-kwallet",
411 "failed to write password, result was %d\n", result);
412 abort();
413 return;
416 purple_debug_misc("keyring-kwallet",
417 "Password %s for account %s (%s).\n",
418 (noPassword ? "removed" : "saved"),
419 purple_account_get_username(account),
420 purple_account_get_protocol_id(account));
422 if (callback != NULL)
423 callback(account, NULL, data);
426 extern "C"
429 static void
430 kwallet_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
431 gpointer data)
433 KWalletPlugin::read_request *req =
434 new KWalletPlugin::read_request(account, cb, data);
436 if (KWallet::Wallet::keyDoesNotExist(KWALLET_WALLET_NAME,
437 KWALLET_FOLDER_NAME, kwallet_account_key(account)))
439 req->detailedAbort(PURPLE_KEYRING_ERROR_NOPASSWORD);
440 delete req;
442 else
443 KWalletPlugin::engine::instance(true)->queue(req);
446 static void
447 kwallet_save(PurpleAccount *account, const char *password,
448 PurpleKeyringSaveCallback cb, gpointer data)
450 if (password == NULL && KWallet::Wallet::keyDoesNotExist(
451 KWALLET_WALLET_NAME, KWALLET_FOLDER_NAME,
452 kwallet_account_key(account)))
454 if (cb != NULL)
455 cb(account, NULL, data);
457 else
458 KWalletPlugin::engine::instance(true)->queue(
459 new KWalletPlugin::save_request(account, password, cb,
460 data));
463 static void
464 kwallet_cancel(void)
466 KWalletPlugin::engine *instance =
467 KWalletPlugin::engine::instance(false);
468 if (instance)
469 instance->abortAll();
472 static void *
473 kwallet_get_handle(void)
475 static int handle;
477 return &handle;
480 static const char *kwallet_get_ui_name(void)
482 GHashTable *ui_info;
483 const char *ui_name = NULL;
485 ui_info = purple_core_get_ui_info();
486 if (ui_info != NULL)
487 ui_name = (const char*)g_hash_table_lookup(ui_info, "name");
488 if (ui_name == NULL)
489 ui_name = KWALLET_APP_NAME;
491 return ui_name;
494 static PurplePluginInfo *
495 plugin_query(GError **error)
497 const gchar * const authors[] = KWALLET_AUTHORS;
499 return purple_plugin_info_new(
500 "id", KWALLET_ID,
501 "name", KWALLET_NAME,
502 "version", DISPLAY_VERSION,
503 "category", N_("Keyring"),
504 "summary", "KWallet Keyring Plugin",
505 "description", KWALLET_DESCRIPTION,
506 "authors", authors,
507 "website", PURPLE_WEBSITE,
508 "abi-version", PURPLE_ABI_VERSION,
509 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
510 NULL
514 static gboolean
515 plugin_load(PurplePlugin *plugin, GError **error)
517 if (!qCoreApp) {
518 int argc = 0;
519 qCoreApp = new QCoreApplication(argc, NULL);
520 qCoreApp->setApplicationName(kwallet_get_ui_name());
523 if (!kwallet_is_enabled()) {
524 g_set_error(error, KWALLET_DOMAIN, 0, "KWallet service is disabled.");
525 purple_debug_info("keyring-kwallet",
526 "KWallet service is disabled\n");
527 return FALSE;
530 keyring_handler = purple_keyring_new();
532 purple_keyring_set_name(keyring_handler, _(KWALLET_NAME));
533 purple_keyring_set_id(keyring_handler, KWALLET_ID);
534 purple_keyring_set_read_password(keyring_handler, kwallet_read);
535 purple_keyring_set_save_password(keyring_handler, kwallet_save);
536 purple_keyring_set_cancel_requests(keyring_handler, kwallet_cancel);
537 purple_keyring_set_close_keyring(keyring_handler,
538 KWalletPlugin::engine::closeInstance);
540 purple_keyring_register(keyring_handler);
542 return TRUE;
545 static gboolean
546 plugin_unload(PurplePlugin *plugin, GError **error)
548 if (purple_keyring_get_inuse() == keyring_handler) {
549 g_set_error(error, KWALLET_DOMAIN, 0, "The keyring is currently "
550 "in use.");
551 purple_debug_warning("keyring-kwallet",
552 "keyring in use, cannot unload\n");
553 return FALSE;
556 purple_signals_disconnect_by_handle(kwallet_get_handle());
558 KWalletPlugin::engine::closeInstance();
560 purple_keyring_unregister(keyring_handler);
561 purple_keyring_free(keyring_handler);
562 keyring_handler = NULL;
564 if (qCoreApp) {
565 delete qCoreApp;
566 qCoreApp = NULL;
569 return TRUE;
572 PURPLE_PLUGIN_INIT(kwallet_keyring, plugin_query, plugin_load, plugin_unload);
574 } /* extern "C" */
576 #include "kwallet.moc"