Merged pidgin/main into default
[pidgin-git.git] / libpurple / plugins / keyrings / gnomekeyring.c
blobbe96810442377d13f7da41d6887f93cf799a6934
1 /**
2 * @file gnomekeyring.c Gnome keyring 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 "debug.h"
30 #include "glibcompat.h"
31 #include "keyring.h"
32 #include "plugins.h"
33 #include "version.h"
35 #include <gnome-keyring.h>
36 #include <gnome-keyring-memory.h>
38 #warning Gnome-Keyring API is deprecated, please use libSecret keyring instead.
40 #define GNOMEKEYRING_NAME N_("GNOME Keyring")
41 #define GNOMEKEYRING_DESCRIPTION N_("This plugin will store passwords in " \
42 "GNOME Keyring.")
43 #define GNOMEKEYRING_ID "keyring-gnomekeyring"
44 #define GNOMEKEYRING_AUTHORS \
45 { "Tomek Wasilczyk <twasilczyk@pidgin.im>", NULL }
47 #define GNOMEKEYRING_DOMAIN (g_quark_from_static_string(GNOMEKEYRING_ID))
49 static PurpleKeyring *keyring_handler = NULL;
50 static GList *request_queue = NULL;
51 static gpointer current_request = NULL;
53 typedef struct
55 enum
57 GNOMEKEYRING_REQUEST_READ,
58 GNOMEKEYRING_REQUEST_SAVE
59 } type;
60 PurpleAccount *account;
61 gchar *password;
62 union
64 PurpleKeyringReadCallback read;
65 PurpleKeyringSaveCallback save;
66 } cb;
67 gpointer cb_data;
68 gboolean handled;
69 } gnomekeyring_request;
71 static void gnomekeyring_cancel_queue(void);
72 static void gnomekeyring_process_queue(void);
74 static void gnomekeyring_request_free(gnomekeyring_request *req)
76 if (req->password != NULL) {
77 memset(req->password, 0, strlen(req->password));
78 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
79 gnome_keyring_memory_free(req->password);
80 G_GNUC_END_IGNORE_DEPRECATIONS
82 g_free(req);
85 static void
86 gnomekeyring_enqueue(gnomekeyring_request *req)
88 request_queue = g_list_append(request_queue, req);
89 gnomekeyring_process_queue();
92 static void
93 gnomekeyring_read_cb(GnomeKeyringResult result, const char *password,
94 gpointer _req)
96 gnomekeyring_request *req = _req;
97 PurpleAccount *account;
98 GError *error = NULL;
100 g_return_if_fail(req != NULL);
102 current_request = NULL;
103 account = req->account;
105 if (result == GNOME_KEYRING_RESULT_OK) {
106 error = NULL;
107 } else if (result == GNOME_KEYRING_RESULT_NO_MATCH) {
108 error = g_error_new(PURPLE_KEYRING_ERROR,
109 PURPLE_KEYRING_ERROR_NOPASSWORD,
110 _("No password found for account."));
111 } else if (result == GNOME_KEYRING_RESULT_DENIED ||
112 result == GNOME_KEYRING_RESULT_CANCELLED)
114 error = g_error_new(PURPLE_KEYRING_ERROR,
115 PURPLE_KEYRING_ERROR_ACCESSDENIED,
116 _("Access denied."));
117 gnomekeyring_cancel_queue();
118 } else if (result == GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON ||
119 result == GNOME_KEYRING_RESULT_IO_ERROR)
121 error = g_error_new(PURPLE_KEYRING_ERROR,
122 PURPLE_KEYRING_ERROR_BACKENDFAIL,
123 _("Communication with GNOME Keyring failed."));
124 } else {
125 error = g_error_new(PURPLE_KEYRING_ERROR,
126 PURPLE_KEYRING_ERROR_BACKENDFAIL,
127 _("Unknown error (code: %d)."), result);
130 if (error == NULL && password == NULL) {
131 error = g_error_new(PURPLE_KEYRING_ERROR,
132 PURPLE_KEYRING_ERROR_BACKENDFAIL,
133 _("Unknown error (password empty)."));
136 if (error == NULL) {
137 purple_debug_misc("keyring-gnome",
138 "Got password for account %s (%s).\n",
139 purple_account_get_username(account),
140 purple_account_get_protocol_id(account));
141 } else if (result == GNOME_KEYRING_RESULT_NO_MATCH) {
142 if (purple_debug_is_verbose()) {
143 purple_debug_info("keyring-gnome",
144 "Password for account %s (%s) isn't stored.\n",
145 purple_account_get_username(account),
146 purple_account_get_protocol_id(account));
148 } else {
149 password = NULL;
150 purple_debug_warning("keyring-gnome", "Failed to read "
151 "password for account %s (%s), code: %d.\n",
152 purple_account_get_username(account),
153 purple_account_get_protocol_id(account),
154 result);
157 if (req->cb.read != NULL)
158 req->cb.read(account, password, error, req->cb_data);
159 req->handled = TRUE;
161 if (error)
162 g_error_free(error);
164 gnomekeyring_process_queue();
167 static void
168 gnomekeyring_save_cb(GnomeKeyringResult result, gpointer _req)
170 gnomekeyring_request *req = _req;
171 PurpleAccount *account;
172 GError *error = NULL;
173 gboolean already_removed = FALSE;
175 g_return_if_fail(req != NULL);
177 current_request = NULL;
178 account = req->account;
180 if (result == GNOME_KEYRING_RESULT_OK) {
181 error = NULL;
182 } else if (result == GNOME_KEYRING_RESULT_NO_MATCH &&
183 req->password == NULL)
185 error = NULL;
186 already_removed = TRUE;
187 } else if (result == GNOME_KEYRING_RESULT_DENIED ||
188 result == GNOME_KEYRING_RESULT_CANCELLED)
190 error = g_error_new(PURPLE_KEYRING_ERROR,
191 PURPLE_KEYRING_ERROR_ACCESSDENIED,
192 _("Access denied."));
193 gnomekeyring_cancel_queue();
194 } else if (result == GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON ||
195 result == GNOME_KEYRING_RESULT_IO_ERROR)
197 error = g_error_new(PURPLE_KEYRING_ERROR,
198 PURPLE_KEYRING_ERROR_BACKENDFAIL,
199 _("Communication with GNOME Keyring failed."));
200 } else {
201 error = g_error_new(PURPLE_KEYRING_ERROR,
202 PURPLE_KEYRING_ERROR_BACKENDFAIL,
203 _("Unknown error (code: %d)."), result);
206 if (already_removed) {
207 /* no operation */
208 } else if (error == NULL) {
209 purple_debug_misc("keyring-gnome",
210 "Password %s for account %s (%s).\n",
211 req->password ? "saved" : "removed",
212 purple_account_get_username(account),
213 purple_account_get_protocol_id(account));
214 } else {
215 purple_debug_warning("keyring-gnome", "Failed updating "
216 "password for account %s (%s), code: %d.\n",
217 purple_account_get_username(account),
218 purple_account_get_protocol_id(account),
219 result);
222 if (req->cb.save != NULL)
223 req->cb.save(account, error, req->cb_data);
224 req->handled = TRUE;
226 if (error)
227 g_error_free(error);
229 gnomekeyring_process_queue();
232 static void
233 gnomekeyring_request_cancel(gpointer _req)
235 gnomekeyring_request *req = _req;
236 PurpleAccount *account;
237 GError *error;
239 g_return_if_fail(req != NULL);
241 if (req->handled) {
242 gnomekeyring_request_free(req);
243 return;
246 purple_debug_warning("keyring-gnome",
247 "operation cancelled (%d %s:%s)\n", req->type,
248 purple_account_get_protocol_id(req->account),
249 purple_account_get_username(req->account));
251 account = req->account;
252 error = g_error_new(PURPLE_KEYRING_ERROR,
253 PURPLE_KEYRING_ERROR_CANCELLED,
254 _("Operation cancelled."));
255 if (req->type == GNOMEKEYRING_REQUEST_READ && req->cb.read)
256 req->cb.read(account, NULL, error, req->cb_data);
257 if (req->type == GNOMEKEYRING_REQUEST_SAVE && req->cb.save)
258 req->cb.save(account, error, req->cb_data);
259 g_error_free(error);
261 gnomekeyring_request_free(req);
262 gnomekeyring_process_queue();
265 static void
266 gnomekeyring_cancel_queue(void)
268 GList *cancel_list = request_queue;
270 if (request_queue == NULL)
271 return;
273 purple_debug_info("gnome-keyring", "cancelling all pending requests\n");
274 request_queue = NULL;
276 g_list_free_full(cancel_list, gnomekeyring_request_cancel);
279 static void
280 gnomekeyring_process_queue(void)
282 gnomekeyring_request *req;
283 PurpleAccount *account;
284 GList *first;
286 if (request_queue == NULL)
287 return;
289 if (current_request) {
290 if (purple_debug_is_verbose())
291 purple_debug_misc("keyring-gnome", "busy...\n");
292 return;
295 first = g_list_first(request_queue);
296 req = first->data;
297 request_queue = g_list_delete_link(request_queue, first);
298 account = req->account;
300 if (purple_debug_is_verbose()) {
301 purple_debug_misc("keyring-gnome",
302 "%s password for account %s (%s)\n",
303 req->type == GNOMEKEYRING_REQUEST_READ ? "reading" :
304 (req->password == NULL ? "removing" : "updating"),
305 purple_account_get_username(account),
306 purple_account_get_protocol_id(account));
309 if (req->type == GNOMEKEYRING_REQUEST_READ) {
310 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
311 current_request = gnome_keyring_find_password(
312 GNOME_KEYRING_NETWORK_PASSWORD, gnomekeyring_read_cb,
313 req, gnomekeyring_request_cancel,
314 "user", purple_account_get_username(account),
315 "protocol", purple_account_get_protocol_id(account),
316 NULL);
317 G_GNUC_END_IGNORE_DEPRECATIONS
318 } else if (req->type == GNOMEKEYRING_REQUEST_SAVE &&
319 req->password != NULL)
321 gchar *display_name = g_strdup_printf(
322 _("Pidgin IM password for account %s"),
323 purple_account_get_username(account));
324 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
325 current_request = gnome_keyring_store_password(
326 GNOME_KEYRING_NETWORK_PASSWORD, GNOME_KEYRING_DEFAULT,
327 display_name, req->password, gnomekeyring_save_cb, req,
328 gnomekeyring_request_cancel,
329 "user", purple_account_get_username(account),
330 "protocol", purple_account_get_protocol_id(account),
331 NULL);
332 G_GNUC_END_IGNORE_DEPRECATIONS
333 g_free(display_name);
334 } else if (req->type == GNOMEKEYRING_REQUEST_SAVE &&
335 req->password == NULL)
337 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
338 current_request = gnome_keyring_delete_password(
339 GNOME_KEYRING_NETWORK_PASSWORD, gnomekeyring_save_cb,
340 req, gnomekeyring_request_cancel,
341 "user", purple_account_get_username(account),
342 "protocol", purple_account_get_protocol_id(account),
343 NULL);
344 G_GNUC_END_IGNORE_DEPRECATIONS
345 } else {
346 g_return_if_reached();
350 static void
351 gnomekeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
352 gpointer data)
354 gnomekeyring_request *req;
356 g_return_if_fail(account != NULL);
358 req = g_new0(gnomekeyring_request, 1);
359 req->type = GNOMEKEYRING_REQUEST_READ;
360 req->account = account;
361 req->cb.read = cb;
362 req->cb_data = data;
364 gnomekeyring_enqueue(req);
367 static void
368 gnomekeyring_save(PurpleAccount *account, const gchar *password,
369 PurpleKeyringSaveCallback cb, gpointer data)
371 gnomekeyring_request *req;
373 g_return_if_fail(account != NULL);
375 req = g_new0(gnomekeyring_request, 1);
376 req->type = GNOMEKEYRING_REQUEST_SAVE;
377 req->account = account;
378 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
379 req->password = gnome_keyring_memory_strdup(password);
380 G_GNUC_END_IGNORE_DEPRECATIONS
381 req->cb.save = cb;
382 req->cb_data = data;
384 gnomekeyring_enqueue(req);
387 static void
388 gnomekeyring_cancel(void)
390 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
391 gnomekeyring_cancel_queue();
392 G_GNUC_END_IGNORE_DEPRECATIONS
393 if (current_request) {
394 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
395 gnome_keyring_cancel_request(current_request);
396 G_GNUC_END_IGNORE_DEPRECATIONS
397 while (g_main_iteration(FALSE));
401 static void
402 gnomekeyring_close(void)
404 gnomekeyring_cancel();
407 static PurplePluginInfo *
408 plugin_query(GError **error)
410 const gchar * const authors[] = GNOMEKEYRING_AUTHORS;
412 return purple_plugin_info_new(
413 "id", GNOMEKEYRING_ID,
414 "name", GNOMEKEYRING_NAME,
415 "version", DISPLAY_VERSION,
416 "category", N_("Keyring"),
417 "summary", "GNOME Keyring Plugin",
418 "description", GNOMEKEYRING_DESCRIPTION,
419 "authors", authors,
420 "website", PURPLE_WEBSITE,
421 "abi-version", PURPLE_ABI_VERSION,
422 "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
423 NULL
427 static gboolean
428 plugin_load(PurplePlugin *plugin, GError **error)
430 GModule *gkr_module;
432 /* libgnome-keyring may crash, if was unloaded before glib main loop
433 * termination.
435 gkr_module = g_module_open("libgnome-keyring", 0);
436 if (gkr_module == NULL) {
437 gkr_module = g_module_open("libgnome-keyring.so.0", 0);
438 if (gkr_module == NULL) {
439 gkr_module = g_module_open("libgnome-keyring.so.1", 0);
442 if (gkr_module == NULL) {
443 purple_debug_info("keyring-gnome", "GNOME Keyring module not "
444 "found\n");
445 return FALSE;
447 g_module_make_resident(gkr_module);
449 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
450 if (!gnome_keyring_is_available()) {
451 G_GNUC_END_IGNORE_DEPRECATIONS
452 g_set_error(error, GNOMEKEYRING_DOMAIN, 0, "GNOME Keyring service is "
453 "disabled.");
454 purple_debug_info("keyring-gnome", "GNOME Keyring service is "
455 "disabled\n");
456 return FALSE;
459 keyring_handler = purple_keyring_new();
461 purple_keyring_set_name(keyring_handler, _(GNOMEKEYRING_NAME));
462 purple_keyring_set_id(keyring_handler, GNOMEKEYRING_ID);
463 purple_keyring_set_read_password(keyring_handler, gnomekeyring_read);
464 purple_keyring_set_save_password(keyring_handler, gnomekeyring_save);
465 purple_keyring_set_cancel_requests(keyring_handler,
466 gnomekeyring_cancel);
467 purple_keyring_set_close_keyring(keyring_handler, gnomekeyring_close);
469 purple_keyring_register(keyring_handler);
471 return TRUE;
474 static gboolean
475 plugin_unload(PurplePlugin *plugin, GError **error)
477 if (purple_keyring_get_inuse() == keyring_handler) {
478 g_set_error(error, GNOMEKEYRING_DOMAIN, 0, "The keyring is currently "
479 "in use.");
480 purple_debug_warning("keyring-gnome",
481 "keyring in use, cannot unload\n");
482 return FALSE;
485 gnomekeyring_close();
487 purple_keyring_unregister(keyring_handler);
488 purple_keyring_free(keyring_handler);
489 keyring_handler = NULL;
491 return TRUE;
494 PURPLE_PLUGIN_INIT(gnome_keyring, plugin_query, plugin_load, plugin_unload);