--- a/libpurple/plugins/keyrings/gnomekeyring.c Sat Mar 23 22:18:29 2013 +0100 +++ b/libpurple/plugins/keyrings/gnomekeyring.c Mon Mar 25 16:28:56 2013 +0100 @@ -32,33 +32,325 @@ #include "version.h" #include <gnome-keyring.h> +#include <gnome-keyring-memory.h> #define GNOMEKEYRING_NAME N_("GNOME Keyring") -#define GNOMEKEYRING_DESCRIPTION N_("This plugin will store passwords in GNOME Keyring.") +#define GNOMEKEYRING_DESCRIPTION N_("This plugin will store passwords in " \ + "GNOME Keyring.") #define GNOMEKEYRING_AUTHOR "Tomek Wasilczyk (tomkiewicz@cpw.pidgin.im)" #define GNOMEKEYRING_ID "keyring-gnomekeyring" static PurpleKeyring *keyring_handler = NULL; +static GList *request_queue = NULL; +static gpointer current_request = NULL; +static gboolean is_closing = FALSE; /* TODO */ + +typedef struct +{ + enum + { + GNOMEKEYRING_REQUEST_READ, + GNOMEKEYRING_REQUEST_SAVE + } type; + PurpleAccount *account; + gchar *password; + union + { + PurpleKeyringReadCallback read; + PurpleKeyringSaveCallback save; + } cb; + gpointer cb_data; + gboolean handled; +} gnomekeyring_request; + +static void gnomekeyring_cancel_queue(void); +static void gnomekeyring_process_queue(void); + +static void gnomekeyring_request_free(gnomekeyring_request *req) +{ + if (req->password != NULL) { + memset(req->password, 0, strlen(req->password)); + gnome_keyring_memory_free(req->password); + } + g_free(req); +} + +static void +gnomekeyring_enqueue(gnomekeyring_request *req) +{ + request_queue = g_list_append(request_queue, req); + gnomekeyring_process_queue(); +} static void -keyring_gnome_read(PurpleAccount *account,PurpleKeyringReadCallback cb, - gpointer data) +gnomekeyring_read_cb(GnomeKeyringResult result, const char *password, + gpointer _req) { + gnomekeyring_request *req = _req; + PurpleAccount *account; + GError *error = NULL; + + g_return_if_fail(req != NULL); + + current_request = NULL; + account = req->account; + + if (result == GNOME_KEYRING_RESULT_OK) { + error = NULL; + } else if (result == GNOME_KEYRING_RESULT_NO_MATCH) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOPASSWD, + "No password found for account"); + } else if (result == GNOME_KEYRING_RESULT_DENIED || + result == GNOME_KEYRING_RESULT_CANCELLED) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_WRONGPASS, + "Access denied"); + gnomekeyring_cancel_queue(); + } else if (result == GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON || + GNOME_KEYRING_RESULT_IO_ERROR) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOCHANNEL, + "Communication with GNOME Keyring failed"); + } else { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOCHANNEL, + "Unknown error (code: %d)", result); + } + + if (error == NULL && password == NULL) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOCHANNEL, + "Unknown error (password empty)"); + } + + if (error == NULL) { + purple_debug_misc("keyring-gnome", + "Got password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } else { + password = NULL; + purple_debug_warning("keyring-gnome", "Failed to read " + "password for account %s (%s), code: %d.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account), + result); + } + + if (req->cb.read != NULL) + req->cb.read(account, password, error, req->cb_data); + req->handled = TRUE; + + if (error) + g_error_free(error); + + gnomekeyring_process_queue(); } static void -keyring_gnome_save(PurpleAccount *account, const gchar *password, - PurpleKeyringSaveCallback cb, gpointer data) +gnomekeyring_save_cb(GnomeKeyringResult result, gpointer _req) { + gnomekeyring_request *req = _req; + PurpleAccount *account; + GError *error = NULL; + + g_return_if_fail(req != NULL); + + current_request = NULL; + account = req->account; + + if (result == GNOME_KEYRING_RESULT_OK) { + error = NULL; + } else if (result == GNOME_KEYRING_RESULT_DENIED || + result == GNOME_KEYRING_RESULT_CANCELLED) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_WRONGPASS, + "Access denied"); + gnomekeyring_cancel_queue(); + } else if (result == GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON || + GNOME_KEYRING_RESULT_IO_ERROR) { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOCHANNEL, + "Communication with GNOME Keyring failed"); + } else { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOCHANNEL, + "Unknown error (code: %d)", result); + } + + if (error == NULL) { + purple_debug_misc("keyring-gnome", + "Password %s for account %s (%s).\n", + req->password ? "saved" : "removed", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } else { + purple_debug_warning("keyring-gnome", "Failed updating " + "password for account %s (%s), code: %d.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account), + result); + } + + if (req->cb.save != NULL) + req->cb.save(account, error, req->cb_data); + req->handled = TRUE; + + if (error) + g_error_free(error); + + gnomekeyring_process_queue(); +} + +static void +gnomekeyring_request_cancel(gpointer _req) +{ + gnomekeyring_request *req = _req; + PurpleAccount *account; + GError *error; + + g_return_if_fail(req != NULL); + + if (req->handled) { + gnomekeyring_request_free(req); + return; + } + + purple_debug_warning("keyring-gnome", + "operation cancelled (%d %s:%s)\n", req->type, + purple_account_get_protocol_id(req->account), + purple_account_get_username(req->account)); + + account = req->account; + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOCHANNEL, + "Operation cancelled"); + if (req->type == GNOMEKEYRING_REQUEST_READ && req->cb.read) + req->cb.read(account, NULL, error, req->cb_data); + if (req->type == GNOMEKEYRING_REQUEST_SAVE && req->cb.save) + req->cb.save(account, error, req->cb_data); + g_error_free(error); + + gnomekeyring_request_free(req); + gnomekeyring_process_queue(); +} + +static void +gnomekeyring_cancel_queue(void) +{ + GList *cancel_list = request_queue; + + purple_debug_info("gnome-keyring", "cancelling all pending requests\n"); + request_queue = NULL; + + g_list_free_full(cancel_list, gnomekeyring_request_cancel); } static void -keyring_gnome_close(GError **error) +gnomekeyring_process_queue(void) { + gnomekeyring_request *req; + PurpleAccount *account; + GList *first; + + if (request_queue == NULL) + return; + + if (current_request) { + if (purple_debug_is_verbose()) + purple_debug_misc("keyring-gnome", "busy...\n"); + return; + } + + first = g_list_first(request_queue); + req = first->data; + request_queue = g_list_delete_link(request_queue, first); + account = req->account; + + if (purple_debug_is_verbose()) { + purple_debug_misc("keyring-gnome", + "%s password for account %s (%s)\n", + req->type == GNOMEKEYRING_REQUEST_READ ? "reading" : + (req->password == NULL ? "removing" : "updating"), + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } + + if (req->type == GNOMEKEYRING_REQUEST_READ) { + current_request = gnome_keyring_find_password( + GNOME_KEYRING_NETWORK_PASSWORD, gnomekeyring_read_cb, + req, gnomekeyring_request_cancel, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + } else if (req->type == GNOMEKEYRING_REQUEST_SAVE && + req->password != NULL) { + gchar *display_name = g_strdup("test name [TODO]"); + current_request = gnome_keyring_store_password( + GNOME_KEYRING_NETWORK_PASSWORD, GNOME_KEYRING_DEFAULT, + display_name, req->password, gnomekeyring_save_cb, req, + gnomekeyring_request_cancel, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + g_free(display_name); + } else if (req->type == GNOMEKEYRING_REQUEST_SAVE && + req->password == NULL) { + current_request = gnome_keyring_delete_password( + GNOME_KEYRING_NETWORK_PASSWORD, gnomekeyring_save_cb, + req, gnomekeyring_request_cancel, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + } else { + g_return_if_reached(); + } +} + +static void +gnomekeyring_read(PurpleAccount *account, PurpleKeyringReadCallback cb, + gpointer data) +{ + gnomekeyring_request *req; + + g_return_if_fail(account != NULL); + + req = g_new0(gnomekeyring_request, 1); + req->type = GNOMEKEYRING_REQUEST_READ; + req->account = account; + req->cb.read = cb; + req->cb_data = data; + + gnomekeyring_enqueue(req); +} + +static void +gnomekeyring_save(PurpleAccount *account, const gchar *password, + PurpleKeyringSaveCallback cb, gpointer data) +{ + gnomekeyring_request *req; + + g_return_if_fail(account != NULL); + + req = g_new0(gnomekeyring_request, 1); + req->type = GNOMEKEYRING_REQUEST_SAVE; + req->account = account; + req->password = gnome_keyring_memory_strdup(password); + req->cb.save = cb; + req->cb_data = data; + + gnomekeyring_enqueue(req); +} + +static void +gnomekeyring_close(GError **error) +{ + is_closing = TRUE; } static gboolean -keyring_gnome_load(PurplePlugin *plugin) +gnomekeyring_load(PurplePlugin *plugin) { if (!gnome_keyring_is_available()) { purple_debug_info("keyring-gnome", "GNOME Keyring service is " @@ -70,9 +362,9 @@ purple_keyring_set_name(keyring_handler, GNOMEKEYRING_NAME); purple_keyring_set_id(keyring_handler, GNOMEKEYRING_ID); - purple_keyring_set_read_password(keyring_handler, keyring_gnome_read); - purple_keyring_set_save_password(keyring_handler, keyring_gnome_save); - purple_keyring_set_close_keyring(keyring_handler, keyring_gnome_close); + purple_keyring_set_read_password(keyring_handler, gnomekeyring_read); + purple_keyring_set_save_password(keyring_handler, gnomekeyring_save); + purple_keyring_set_close_keyring(keyring_handler, gnomekeyring_close); purple_keyring_register(keyring_handler); @@ -80,7 +372,7 @@ } static gboolean -keyring_gnome_unload(PurplePlugin *plugin) +gnomekeyring_unload(PurplePlugin *plugin) { if (purple_keyring_get_inuse() == keyring_handler) { purple_debug_warning("keyring-gnome", @@ -88,7 +380,7 @@ return FALSE; } - keyring_gnome_close(NULL); + gnomekeyring_close(NULL); purple_keyring_unregister(keyring_handler); purple_keyring_free(keyring_handler); @@ -114,8 +406,8 @@ GNOMEKEYRING_DESCRIPTION, /* description */ GNOMEKEYRING_AUTHOR, /* author */ PURPLE_WEBSITE, /* homepage */ - keyring_gnome_load, /* load */ - keyring_gnome_unload, /* unload */ + gnomekeyring_load, /* load */ + gnomekeyring_unload, /* unload */ NULL, /* destroy */ NULL, /* ui_info */ NULL, /* extra_info */