Wed, 13 Aug 2008 21:46:17 +0000
propagate from branch 'im.pidgin.pidgin' (head a9f4343f9aad8830875ced7e6469f1fec95a3542)
to branch 'im.pidgin.soc.2008.masterpassword' (head 9beebdbf44d6fa37e0fb6b25511cbe755bd79b58)
| COPYRIGHT | file | annotate | diff | comparison | revisions |
--- a/COPYRIGHT Wed Aug 13 19:38:25 2008 +0000 +++ b/COPYRIGHT Wed Aug 13 21:46:17 2008 +0000 @@ -30,6 +30,7 @@ Dave Bell Igor Belyi Brian Bernas +Vivien Bernet-Rollande Paul Betts Jonas Birmé George-Cristian Bîrzan
--- a/configure.ac Wed Aug 13 19:38:25 2008 +0000 +++ b/configure.ac Wed Aug 13 21:46:17 2008 +0000 @@ -2391,6 +2391,7 @@ libpurple/purple.pc libpurple/purple-uninstalled.pc libpurple/plugins/Makefile + libpurple/plugins/keyrings/Makefile libpurple/plugins/mono/Makefile libpurple/plugins/mono/api/Makefile libpurple/plugins/mono/loader/Makefile
--- a/libpurple/Makefile.am Wed Aug 13 19:38:25 2008 +0000 +++ b/libpurple/Makefile.am Wed Aug 13 21:46:17 2008 +0000 @@ -50,6 +50,7 @@ ft.c \ idle.c \ imgstore.c \ + keyring.c \ log.c \ mime.c \ nat-pmp.c \ @@ -103,6 +104,7 @@ gaim-compat.h \ idle.h \ imgstore.h \ + keyring.h \ log.h \ mime.h \ nat-pmp.h \
--- a/libpurple/account.c Wed Aug 13 19:38:25 2008 +0000 +++ b/libpurple/account.c Wed Aug 13 21:46:17 2008 +0000 @@ -28,6 +28,7 @@ #include "core.h" #include "dbus-maybe.h" #include "debug.h" +#include "keyring.h" #include "network.h" #include "notify.h" #include "pounce.h" @@ -41,6 +42,12 @@ #include "util.h" #include "xmlnode.h" +/** + * TODO : + * - grab Trannie's code for async connection + */ + + typedef struct { PurpleConnectionErrorInfo *current_error; @@ -88,6 +95,15 @@ static void set_current_error(PurpleAccount *account, PurpleConnectionErrorInfo *new_err); +static void purple_account_register_got_password_cb(PurpleAccount * account, + char * password, GError * error, gpointer data); + + +static void purple_account_unregister_got_password_cb(PurpleAccount * account, + char * password, GError * error, gpointer data); + +static void purple_account_connect_got_password_cb(const PurpleAccount * account, + gchar * password, GError * error, gpointer data); /********************************************************************* * Writing to disk * *********************************************************************/ @@ -360,8 +376,13 @@ xmlnode *node, *child; const char *tmp; + const char *keyring_id; + const char *mode; + char *data; PurplePresence *presence; PurpleProxyInfo *proxy_info; + GError * error = NULL; + GDestroyNotify destroy; node = xmlnode_new("account"); @@ -371,11 +392,30 @@ child = xmlnode_new_child(node, "name"); xmlnode_insert_data(child, purple_account_get_username(account), -1); - if (purple_account_get_remember_password(account) && - ((tmp = purple_account_get_password(account)) != NULL)) + if (purple_account_get_remember_password(account)) { - child = xmlnode_new_child(node, "password"); - xmlnode_insert_data(child, tmp, -1); + purple_debug_info("accounts", "Exporting password for account %s.\n", + purple_account_get_username(account)); + purple_keyring_export_password(account, &keyring_id, + &mode, &data, &error, &destroy); + + if (error != NULL) { + + purple_debug_info("accounts", + "failed to export password for account %s : %s.\n", + purple_account_get_username(account), + error->message); + + } else { + + child = xmlnode_new_child(node, "password"); + xmlnode_set_attrib(child, "keyring_id", keyring_id); + xmlnode_set_attrib(child, "mode", mode); + xmlnode_insert_data(child, data, -1); + + if (destroy != NULL) + destroy(data); + } } if ((tmp = purple_account_get_alias(account)) != NULL) @@ -451,6 +491,8 @@ return; } + purple_debug_info("account", "Syncing accounts.\n"); + node = accounts_to_xmlnode(); data = xmlnode_to_formatted_str(node, NULL); purple_util_write_data_to_file("accounts.xml", data, -1); @@ -765,7 +807,11 @@ xmlnode *child; char *protocol_id = NULL; char *name = NULL; - char *data; + const char *keyring_id = NULL; + const char *mode = NULL; + char *data = NULL; + gboolean result = FALSE; + GError * error = NULL; child = xmlnode_get_child(node, "protocol"); if (child != NULL) @@ -793,15 +839,6 @@ g_free(name); g_free(protocol_id); - /* Read the password */ - child = xmlnode_get_child(node, "password"); - if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL)) - { - purple_account_set_remember_password(ret, TRUE); - purple_account_set_password(ret, data); - g_free(data); - } - /* Read the alias */ child = xmlnode_get_child(node, "alias"); if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL)) @@ -874,6 +911,31 @@ parse_current_error(child, ret); } + /* Read the password */ + + child = xmlnode_get_child(node, "password"); + if (child != NULL) + { + keyring_id = xmlnode_get_attrib(child, "keyring_id"); + mode = xmlnode_get_attrib(child, "mode"); + data = xmlnode_get_data(child); + + result = purple_keyring_import_password(ret, keyring_id, mode, data, &error); + + if (result == TRUE) { + purple_debug_info("accounts", "password imported successfully.\n"); + purple_account_set_remember_password(ret, TRUE); + /* + g_free(keyring_id); TODO : + g_free(mode); This was commented becaus eof a double free. + g_free(data); I should figure out which one causes the bug to avoid leaks + */ + } else { + purple_debug_info("accounts", "failed to imported password.\n"); + /* TODO handle error */ + } + } + return ret; } @@ -1035,21 +1097,59 @@ purple_debug_info("account", "Registering account %s\n", purple_account_get_username(account)); - purple_connection_new(account, TRUE, purple_account_get_password(account)); + purple_keyring_get_password_async(account, purple_account_register_got_password_cb, NULL); } + +static void +purple_account_register_got_password_cb(PurpleAccount * account, char * password, GError * error, gpointer data) +{ + g_return_if_fail(account != NULL); + + /* FIXME : handle error properly */ + + purple_connection_new(account, TRUE, password); +} + +struct _unregister_data +{ + PurpleAccountUnregistrationCb cb; + void *user_data; +}; + void purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data) { + struct _unregister_data * data; + g_return_if_fail(account != NULL); purple_debug_info("account", "Unregistering account %s\n", purple_account_get_username(account)); - purple_connection_new_unregister(account, purple_account_get_password(account), cb, user_data); + data = g_malloc(sizeof(struct _unregister_data)); + data->cb = cb; + data->user_data = user_data; + + purple_keyring_get_password_async(account, purple_account_unregister_got_password_cb, data); + } static void +purple_account_unregister_got_password_cb(PurpleAccount * account, char * password, GError * error, gpointer data) +{ + struct _unregister_data * unregdata; + + g_return_if_fail(account != NULL); + + /* FIXME : handle error properly */ + + unregdata = data; + purple_connection_new_unregister(account, password, unregdata->cb, unregdata->user_data); +} + + +static void request_password_ok_cb(PurpleAccount *account, PurpleRequestFields *fields) { const char *entry; @@ -1149,7 +1249,20 @@ } prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - password = purple_account_get_password(account); + purple_keyring_get_password_async(account, purple_account_connect_got_password_cb, prpl_info); + +} + +static void +purple_account_connect_got_password_cb(const PurpleAccount * account, + gchar * password, + GError * error, + gpointer data) +{ + PurplePluginProtocolInfo *prpl_info; + + prpl_info = data; + if ((password == NULL) && !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) @@ -1494,12 +1607,50 @@ { g_return_if_fail(account != NULL); - g_free(account->password); - account->password = g_strdup(password); + if (account->password != NULL) + g_free(account->password); + + if (purple_account_get_remember_password(account) == FALSE) + account->password = g_strdup(password); + + else + purple_keyring_set_password_sync(account, password); schedule_accounts_save(); } +void +purple_account_set_password_async(PurpleAccount * account, + gchar * password, + GDestroyNotify destroypassword, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + /** + * This is so we can force an account sync by calling + * it with account == NULL. + */ + if(account != NULL) { + + if (purple_account_get_remember_password(account) == FALSE) { + + account->password = g_strdup(password); + purple_debug_info("account", + "Password for %s set, not sent to keyring.\n", + purple_account_get_username(account)); + + if (cb != NULL) + cb(account, NULL, data); + + } else { + + purple_keyring_set_password_async(account, password, + destroypassword, cb, data); + + } + } + schedule_accounts_save(); +} void purple_account_set_alias(PurpleAccount *account, const char *alias) { @@ -1567,6 +1718,10 @@ account->gc = gc; } +/** + * FIXME : + * This should add/remove the password to/from the keyring + */ void purple_account_set_remember_password(PurpleAccount *account, gboolean value) { @@ -1887,14 +2042,34 @@ return account->username; } +/* XXX will be replaced by the async code in 3.0 */ const char * purple_account_get_password(const PurpleAccount *account) { g_return_val_if_fail(account != NULL, NULL); - return account->password; + if (account->password != NULL) { + + purple_debug_info("keyring", "password was read from stored\n"); + return account->password; + + + } else { + purple_debug_info("keyring", "reading password from keyring\n"); + return purple_keyring_get_password_sync(account); + } } +void +purple_account_get_password_async(PurpleAccount * account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + purple_keyring_get_password_async(account, cb, data); +} + + + const char * purple_account_get_alias(const PurpleAccount *account) {
--- a/libpurple/account.h Wed Aug 13 19:38:25 2008 +0000 +++ b/libpurple/account.h Wed Aug 13 21:46:17 2008 +0000 @@ -46,6 +46,7 @@ #include "proxy.h" #include "prpl.h" #include "status.h" +#include "keyring.h" /** * Account request types. @@ -330,6 +331,22 @@ void purple_account_set_password(PurpleAccount *account, const char *password); /** + * Set a password to be remembered. + * This should be renamed purple_account_set_password() when getting + * to 3.0. This calls the keyring function and syncs the accounts.xml + * @param account The account for which the password is to be saved. + * @param password The password to save. + * @param destroypassword A function called to free the password. Can be NULL. + * @param cb A callback for once the password is saved. + * @param data A pointer to be passed to the callback. + */ +void purple_account_set_password_async(PurpleAccount * account, + gchar * password, + GDestroyNotify destroypassword, + PurpleKeyringSaveCallback cb, + gpointer data); + +/** * Sets the account's alias. * * @param account The account. @@ -549,7 +566,7 @@ const char *purple_account_get_username(const PurpleAccount *account); /** - * Returns the account's password. + * Returns the account's password (deprecated, use async code instead). * * @param account The account. * @@ -557,6 +574,17 @@ */ const char *purple_account_get_password(const PurpleAccount *account); + +/** + * Reads the password for the account and passes it to the callback + * + * @param account The account to read the password for. + * @param cb The callback to pass the password to. + * @param data A pointer passed to the callback. + */ +void purple_account_get_password_async(PurpleAccount * account, + PurpleKeyringReadCallback cb, gpointer data); + /** * Returns the account's alias. *
--- a/libpurple/connection.c Wed Aug 13 19:38:25 2008 +0000 +++ b/libpurple/connection.c Wed Aug 13 21:46:17 2008 +0000 @@ -39,6 +39,8 @@ #include "util.h" #define KEEPALIVE_INTERVAL 30 +static void purple_connection_disconnect_got_pw_cb(PurpleAccount * account, + gchar * password, GError * error, gpointer data); static GList *connections = NULL; static GList *connections_connecting = NULL; @@ -483,11 +485,23 @@ purple_connection_disconnect_cb(gpointer data) { PurpleAccount *account = data; - char *password = g_strdup(purple_account_get_password(account)); + purple_account_get_password_async(account, + purple_connection_disconnect_got_pw_cb, NULL); + + return FALSE; +} + +static void +purple_connection_disconnect_got_pw_cb(PurpleAccount * account, + gchar * password, + GError * error, + gpointer data) +{ + /* FIXME : handle error */ + char * pw = g_strdup(password); + purple_account_disconnect(account); - purple_account_set_password(account, password); - g_free(password); - return FALSE; + purple_account_set_password(account, pw); } void
--- a/libpurple/core.c Wed Aug 13 19:38:25 2008 +0000 +++ b/libpurple/core.c Wed Aug 13 21:46:17 2008 +0000 @@ -35,6 +35,7 @@ #include "ft.h" #include "idle.h" #include "imgstore.h" +#include "keyring.h" #include "network.h" #include "notify.h" #include "plugin.h" @@ -132,6 +133,7 @@ purple_ciphers_init(); purple_cmds_init(); + purple_keyring_init(); /* Since plugins get probed so early we should probably initialize their * subsystem right away too.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/keyring.c Wed Aug 13 21:46:17 2008 +0000 @@ -0,0 +1,1165 @@ +/** + * @file keyring.c Keyring plugin API + * @ingroup core + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include <glib.h> +#include <string.h> +#include "account.h" +#include "keyring.h" +#include "signals.h" +#include "core.h" +#include "debug.h" + +typedef struct _PurpleKeyringChangeTracker PurpleKeyringChangeTracker; + +static void purple_keyring_pref_cb(const char *, PurplePrefType, gconstpointer, gpointer); +static PurpleKeyring * purple_keyring_find_keyring_by_id(char * id); +static void purple_keyring_drop_passwords(const PurpleKeyring * keyring); +static void purple_keyring_set_inuse_check_error_cb(const PurpleAccount *,GError *,gpointer); +static void purple_keyring_set_inuse_got_pw_cb(PurpleAccount *, gchar *, GError *, gpointer); + + +/******************************************/ +/** @name PurpleKeyring */ +/******************************************/ +/*@{*/ + +struct _PurpleKeyring +{ + char * name; // a user friendly name + char * id; // same as plugin id + PurpleKeyringRead read_password; + PurpleKeyringSave save_password; + PurpleKeyringClose close_keyring; + PurpleKeyringChangeMaster change_master; + PurpleKeyringImportPassword import_password; + PurpleKeyringExportPassword export_password; + PurpleKeyringReadSync read_sync; + PurpleKeyringSaveSync save_sync; + gpointer r1; /* RESERVED */ + gpointer r2; /* RESERVED */ + gpointer r3; /* RESERVED */ +}; + +struct _PurpleKeyringChangeTracker +{ + GError * error; /* could probably be dropped */ + PurpleKeyringSetInUseCallback cb; + gpointer data; + const PurpleKeyring * new; + const PurpleKeyring * old; /* we are done when : finished == TRUE && read_outstanding == 0 */ + int read_outstanding; + gboolean finished; + gboolean abort; + gboolean force; +}; + +/* Constructor */ +PurpleKeyring * +purple_keyring_new() +{ + return g_malloc0(sizeof(PurpleKeyring)); +} + +/* Destructor */ +void +purple_keyring_free(PurpleKeyring * keyring) +{ + g_free(keyring); + return; +} + +/* Accessors */ +const char * +purple_keyring_get_name(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->name; +} + +const char * +purple_keyring_get_id(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->id; +} + +PurpleKeyringRead +purple_keyring_get_read_password(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->read_password; +} + +PurpleKeyringSave +purple_keyring_get_save_password(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->save_password; +} + +PurpleKeyringReadSync +purple_keyring_get_read_sync(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->read_sync; +} + +PurpleKeyringSaveSync +purple_keyring_get_save_sync(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->save_sync; +} + +PurpleKeyringClose +purple_keyring_get_close_keyring(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->close_keyring; +} + +PurpleKeyringChangeMaster +purple_keyring_get_change_master(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->change_master; +} + +PurpleKeyringImportPassword +purple_keyring_get_import_password(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->import_password; +} + +PurpleKeyringExportPassword +purple_keyring_get_export_password(const PurpleKeyring * keyring) +{ + g_return_val_if_fail(keyring != NULL, NULL); + + return keyring->export_password; +} + +void +purple_keyring_set_name(PurpleKeyring * keyring, char * name) +{ + g_return_if_fail(keyring != NULL); + + g_free(keyring->name); + keyring->name = g_strdup(name); + + return; +} + +void +purple_keyring_set_id(PurpleKeyring * keyring, char * id) +{ + g_return_if_fail(keyring != NULL); + + g_free(keyring->id); + keyring->id = g_strdup(id); + + return; +} + +void +purple_keyring_set_read_password(PurpleKeyring * keyring, PurpleKeyringRead read) +{ + g_return_if_fail(keyring != NULL); + + keyring->read_password = read; + + return; +} + +void +purple_keyring_set_save_password(PurpleKeyring * keyring, PurpleKeyringSave save) +{ + g_return_if_fail(keyring != NULL); + + keyring->save_password = save; + + return; +} + +void +purple_keyring_set_read_sync(PurpleKeyring * keyring, PurpleKeyringReadSync read) +{ + g_return_if_fail(keyring != NULL); + + keyring->read_sync = read; + + return; +} + +void +purple_keyring_set_save_sync(PurpleKeyring * keyring, PurpleKeyringSaveSync save) +{ + g_return_if_fail(keyring != NULL); + + keyring->save_sync = save; + + return; +} + +void +purple_keyring_set_close_keyring(PurpleKeyring * keyring, PurpleKeyringClose close) +{ + g_return_if_fail(keyring != NULL); + + keyring->close_keyring = close; + + return; +} + +void +purple_keyring_set_change_master(PurpleKeyring * keyring, PurpleKeyringChangeMaster change) +{ + g_return_if_fail(keyring != NULL); + + keyring->change_master = change; + + return; +} + +void +purple_keyring_set_import_password(PurpleKeyring * keyring, PurpleKeyringImportPassword import) +{ + g_return_if_fail(keyring != NULL); + + keyring->import_password = import; + + return; +} + +void +purple_keyring_set_export_password(PurpleKeyring * keyring, PurpleKeyringExportPassword export) +{ + g_return_if_fail(keyring != NULL); + + keyring->export_password = export; + + return; +} + +/*@}*/ + + +/***************************************/ +/** @name Keyring API */ +/** @todo (maybe) */ +/** - rename as purple_keyrings */ +/***************************************/ +/*@{*/ + +static GList * purple_keyring_keyrings; /* list of available keyrings */ +static const PurpleKeyring * purple_keyring_inuse; /* keyring being used */ +static char * purple_keyring_to_use; +static guint purple_keyring_pref_cb_id; + +void +purple_keyring_init() +{ + PurpleCore * core; + const char * touse; + + /* Make sure we don't have junk */ + purple_keyring_keyrings = NULL; + purple_keyring_inuse = NULL; + + /* register signals */ + core = purple_get_core(); + + purple_signal_register(core, "keyring-register", + purple_marshal_VOID__POINTER_POINTER, + NULL, 2, + purple_value_new(PURPLE_TYPE_STRING), /* keyring ID */ + purple_value_new(PURPLE_TYPE_BOXED, "PurpleKeyring *")); /* a pointer to the keyring */ + + purple_signal_register(core, "keyring-unregister", + purple_marshal_VOID__POINTER_POINTER, + NULL, 2, + purple_value_new(PURPLE_TYPE_STRING), /* keyring ID */ + purple_value_new(PURPLE_TYPE_BOXED, "PurpleKeyring *")); /* a pointer to the keyring */ + + /* see what keyring we want to use */ + touse = purple_prefs_get_string("/purple/keyring/active"); + + if (touse == NULL) { + purple_prefs_add_none("/purple/keyring"); + purple_prefs_add_string("/purple/keyring/active", FALLBACK_KEYRING); + purple_keyring_to_use = g_strdup(FALLBACK_KEYRING); + + } else { + + purple_keyring_to_use = g_strdup(touse); + } + + purple_keyring_pref_cb_id = purple_prefs_connect_callback(NULL, + "/purple/keyring/active", purple_keyring_pref_cb, NULL); + + purple_debug_info("keyring", "purple_keyring_init() done, selected keyring is : %s.\n", + purple_keyring_to_use); + + return; +} + +void +purple_keyring_uninit() +{ + g_free(purple_keyring_to_use); + purple_debug_info("keyring", "purple_keyring_uninit() done.\n"); +} + +static PurpleKeyring * +purple_keyring_find_keyring_by_id(char * id) +{ + GList * l; + PurpleKeyring * keyring; + const char * curr_id; + + for (l = purple_keyring_keyrings; l != NULL; l = l->next) { + keyring = l->data; + curr_id = purple_keyring_get_id(keyring); + + if (g_strcmp0(id, curr_id) == 0) + return keyring; + } + + return NULL; +} + +const GList * +purple_keyring_get_keyrings() +{ + return purple_keyring_keyrings; +} + +const PurpleKeyring * +purple_keyring_get_inuse() +{ + return purple_keyring_inuse; +} + + +/* fire and forget */ +/** @todo ? : add dummy callback ? */ +static void +purple_keyring_drop_passwords(const PurpleKeyring * keyring) +{ + GList * cur; + PurpleKeyringSave save; + + save = purple_keyring_get_save_password(keyring); + + g_return_if_fail(save != NULL); + + for (cur = purple_accounts_get_all(); + cur != NULL; + cur = cur->next) + save(cur->data, NULL, NULL, NULL, NULL); + + return; +} + + + +static void +purple_keyring_set_inuse_check_error_cb(const PurpleAccount * account, + GError * error, + gpointer data) +{ + + const char * name; + PurpleKeyringClose close; + struct _PurpleKeyringChangeTracker * tracker; + + tracker = (PurpleKeyringChangeTracker *)data; + + g_return_if_fail(tracker->abort == FALSE); + + tracker->read_outstanding--; + + name = purple_account_get_username(account);; + + + if ((error != NULL) && (error->domain == ERR_PIDGINKEYRING)) { + + tracker->error = error; + + switch(error->code) { + + case ERR_NOCAP : + purple_debug_info("keyring", + "Keyring could not save password for account %s : %s\n", + name, error->message); + break; + + case ERR_NOPASSWD : + purple_debug_info("keyring", + "No password found while changing keyring for account %s : %s\n", + name, error->message); + break; + + case ERR_NOCHANNEL : + purple_debug_info("keyring", + "Failed to communicate with backend while changing keyring for account %s : %s Aborting changes.\n", + name, error->message); + tracker->abort = TRUE; + break; + + default : + purple_debug_info("keyring", + "Unknown error while changing keyring for account %s : %s\n", + name, error->message); + break; + } + } + /* if this was the last one */ + if (tracker->finished == TRUE && tracker->read_outstanding == 0) { + + if (tracker->abort == TRUE && tracker->force == FALSE) { + /** + * TODO : + * - create faulty keyring to test this code. + */ + if (tracker->cb != NULL) + tracker->cb(tracker->old, FALSE, tracker->error, tracker->data); + + purple_keyring_drop_passwords(tracker->new); + + close = purple_keyring_get_close_keyring(tracker->new); + if (close != NULL) + close(NULL); + + purple_debug_info("keyring", + "Failed to change keyring, aborting"); + + /* FIXME : this should maybe be in a callback + * cancel the prefs change + */ + purple_prefs_disconnect_callback(purple_keyring_pref_cb_id); + purple_prefs_set_string("/purple/keyring/active", + purple_keyring_get_id(tracker->old)); + purple_keyring_pref_cb_id = purple_prefs_connect_callback(NULL, + "/purple/keyring/active", purple_keyring_pref_cb, NULL); + + + } else { + close = purple_keyring_get_close_keyring(tracker->old); + if (close != NULL) + close(&error); + + purple_keyring_inuse = tracker->new; + purple_keyring_drop_passwords(tracker->old); + + purple_debug_info("keyring", + "Successfully changed keyring.\n"); + + if (tracker->cb != NULL) + tracker->cb(tracker->new, TRUE, error, tracker->data); + } + + g_free(tracker); + } + /** + * This is kind of hackish. It will schedule an account save, + * and then return because account == NULL. + * Another way to do this would be to expose the + * schedule_accounts_save() function, but other such functions + * are not exposed. So these was done for consistency. + */ + purple_account_set_password_async(NULL, NULL, NULL, NULL, NULL); + + return; +} + + +static void +purple_keyring_set_inuse_got_pw_cb(PurpleAccount * account, + gchar * password, + GError * error, + gpointer data) +{ + const PurpleKeyring * new; + PurpleKeyringSave save; + PurpleKeyringChangeTracker * tracker; + + tracker = (PurpleKeyringChangeTracker *)data; + new = tracker->new; + + g_return_if_fail(tracker->abort == FALSE); + + if (error != NULL) { + + if (error->code == ERR_NOPASSWD || + error->code == ERR_NOACCOUNT || + tracker->force == TRUE) { + + /* don't save password, and directly trigger callback */ + purple_keyring_set_inuse_check_error_cb(account, error, data); + + } else { + + /* fatal error, abort all */ + tracker->abort = TRUE; + } + + } else { + + + save = purple_keyring_get_save_password(new); + + if (save != NULL) { + /* this test is probably totally useless, since there's no point + * in having a keyring that can't store passwords, but it + * will prevent crash with invalid keyrings + */ + save(account, password, NULL, + purple_keyring_set_inuse_check_error_cb, tracker); + + } else { + error = g_error_new(ERR_PIDGINKEYRING , ERR_NOCAP, + "cannot store passwords in new keyring"); + purple_keyring_set_inuse_check_error_cb(account, error, data); + g_error_free(error); + } + } + + return; +} + + + + +void +purple_keyring_set_inuse(const PurpleKeyring * newkeyring, + gboolean force, + PurpleKeyringSetInUseCallback cb, + gpointer data) +{ + GList * cur; + const PurpleKeyring * oldkeyring; + PurpleKeyringRead read = NULL; + PurpleKeyringClose close; + PurpleKeyringChangeTracker * tracker; + GError * error = NULL; + + if (newkeyring != NULL) + purple_debug_info("keyring", "Attempting to set new keyring : %s.\n", + newkeyring->id); + else + purple_debug_info("keyring", "Attempting to set new keyring : NULL.\n"); + + oldkeyring = purple_keyring_get_inuse(); + + if (oldkeyring != NULL) { + read = purple_keyring_get_read_password(oldkeyring); + + if (read == NULL) { + /* + error = g_error_new(ERR_PIDGINKEYRING , ERR_NOCAP, + "Existing keyring cannot read passwords"); + */ + purple_debug_info("keyring", "Existing keyring cannot read passwords"); + + /* at this point, we know the keyring won't let us + * read passwords, so there no point in copying them. + * therefore we just cleanup the old and setup the new + * one later. + */ + + purple_keyring_drop_passwords(oldkeyring); + + close = purple_keyring_get_close_keyring(oldkeyring); + + if (close != NULL) + close(NULL); /* we can't do much about errors at this point*/ + + } else { + tracker = g_malloc(sizeof(PurpleKeyringChangeTracker)); + oldkeyring = purple_keyring_get_inuse(); + + tracker->cb = cb; + tracker->data = data; + tracker->new = newkeyring; + tracker->old = oldkeyring; + tracker->read_outstanding = 0; + tracker->finished = FALSE; + tracker->abort = FALSE; + tracker->force = force; + tracker->error = NULL; + + for (cur = purple_accounts_get_all(); + (cur != NULL) && (tracker->abort == FALSE); + cur = cur->next) { + + tracker->read_outstanding++; + + if (cur->next == NULL) + tracker->finished = TRUE; + + read(cur->data, purple_keyring_set_inuse_got_pw_cb, tracker); + } + } + + } else { /* no keyring was set before. */ + purple_debug_info("keyring", "Setting keyring for the first time : %s.\n", + newkeyring->id); + purple_keyring_inuse = newkeyring; + + if (cb != NULL) + cb(newkeyring, TRUE, NULL, data); + + return; + } +} + + + +static void +purple_keyring_pref_cb(const char *pref, + PurplePrefType type, + gconstpointer id, + gpointer data) +{ + PurpleKeyring * new; + + g_return_if_fail(g_strcmp0(pref, "/purple/keyring/active") == 0); + g_return_if_fail(type == PURPLE_PREF_STRING); + g_return_if_fail(id != NULL); + + new = purple_keyring_get_keyring_by_id(id); + g_return_if_fail(new != NULL); + + purple_keyring_set_inuse(new, FALSE, NULL, data); /* FIXME Maybe this should have a callback that can cancel the action */ + + return; +} + +GList * +purple_keyring_get_options() +{ + const GList * keyrings; + PurpleKeyring * keyring; + GList * list = NULL; + + for (keyrings = purple_keyring_get_keyrings(); + keyrings != NULL; + keyrings = keyrings->next) { + + keyring = keyrings->data; + list = g_list_append(list, keyring->name); + list = g_list_append(list, keyring->id); + purple_debug_info("keyring", "adding pair : %s, %s.\n", + keyring->name, keyring->id); + } + + return list; +} + +PurpleKeyring * +purple_keyring_get_keyring_by_id(const char * id) +{ + const GList * keyrings; + PurpleKeyring * keyring; + + for (keyrings = purple_keyring_get_keyrings(); + keyrings != NULL; + keyrings = keyrings->next) { + + keyring = keyrings->data; + if (g_strcmp0(id, keyring->id) == 0) + return keyring; + + } + return NULL; +} + + + +void +purple_keyring_register(PurpleKeyring * keyring) +{ + const char * keyring_id; + PurpleCore * core; + + g_return_if_fail(keyring != NULL); + + keyring_id = purple_keyring_get_id(keyring); + + /* keyring with no ID. Add error handling ? */ + g_return_if_fail(keyring_id != NULL); + + + purple_debug_info("keyring", "Registering keyring : %s\n", + keyring->id); + + /* If this is the configured keyring, use it. */ + if (purple_keyring_inuse == NULL && + g_strcmp0(keyring_id, purple_keyring_to_use) == 0) { + + purple_debug_info("keyring", "Keyring %s matches keyring to use, using it.\n", + keyring->id); + /** @todo add callback to make sure all is ok */ + purple_keyring_set_inuse(keyring, TRUE, NULL, NULL); + + } + + core = purple_get_core(); + + purple_signal_emit(core, "keyring-register", keyring_id, keyring); + purple_debug_info("keyring", "registered keyring : %s.\n", keyring_id); + + purple_keyring_keyrings = g_list_prepend(purple_keyring_keyrings, + keyring); +} + + +void +purple_keyring_unregister(PurpleKeyring * keyring) +{ + PurpleCore * core; + const PurpleKeyring * inuse; + PurpleKeyring * fallback; + const char * keyring_id; + + g_return_if_fail(keyring != NULL); + + purple_debug_info("keyring", + "Unregistering keyring %s", + purple_keyring_get_id(keyring)); + + core = purple_get_core(); + keyring_id = purple_keyring_get_id(keyring); + purple_signal_emit(core, "keyring-unregister", keyring_id, keyring); + + inuse = purple_keyring_get_inuse(); + fallback = purple_keyring_find_keyring_by_id(FALLBACK_KEYRING); + + if (inuse == keyring) { + if (inuse != fallback) { + /* TODO : check result ? */ + purple_keyring_set_inuse(fallback, TRUE, NULL, NULL); + + } else { + fallback = NULL; + purple_keyring_set_inuse(NULL, TRUE, NULL, NULL); + } + } + + purple_keyring_keyrings = g_list_remove(purple_keyring_keyrings, + keyring); + + //purple_debug_info("keyring", "Keyring %s unregistered", keyring->id); +} + + +/*@}*/ + + +/***************************************/ +/** @name Keyring plugin wrappers */ +/***************************************/ +/*@{*/ + + +gboolean +purple_keyring_import_password(PurpleAccount * account, + char * keyringid, + char * mode, + char * data, + GError ** error) +{ + const PurpleKeyring * inuse; + PurpleKeyringImportPassword import; + const char * realid; + + purple_debug_info("keyring", "Importing password for account %s (%s) to keyring %s.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account), + keyringid); + + inuse = purple_keyring_get_inuse(); + + if (inuse == NULL) { + *error = g_error_new(ERR_PIDGINKEYRING , ERR_NOKEYRING, + "No keyring configured, cannot import password info"); + purple_debug_info("Keyring", + "No keyring configured, cannot import password info for account %s (%s).\n", + purple_account_get_username(account), purple_account_get_protocol_id(account)); + return FALSE; + } + + realid = purple_keyring_get_id(inuse); + /* + * we want to be sure that either : + * - there is a keyringid specified and it matches the one configured + * - or the configured keyring is the fallback, compatible one. + */ + if ((keyringid != NULL && g_strcmp0(realid, keyringid) != 0) || + (keyringid == NULL && g_strcmp0(FALLBACK_KEYRING, realid))) { + + *error = g_error_new(ERR_PIDGINKEYRING , ERR_INVALID, + "Specified keyring id does not match the configured one."); + purple_debug_info("keyring", + "Specified keyring id does not match the configured one (%s vs. %s). Data will be lost.\n", + keyringid, realid); + return FALSE; + } + + import = purple_keyring_get_import_password(inuse); + if (import == NULL) { + *error = g_error_new(ERR_PIDGINKEYRING , ERR_NOCAP, + "Keyring cannot import password info."); + purple_debug_info("Keyring", "Configured keyring cannot import password info. This might be normal."); + return FALSE; + } + + return import(account, mode, data, error); +} + +gboolean +purple_keyring_export_password(PurpleAccount * account, + const char ** keyringid, + const char ** mode, + char ** data, + GError ** error, + GDestroyNotify * destroy) +{ + const PurpleKeyring * inuse; + PurpleKeyringExportPassword export; + + inuse = purple_keyring_get_inuse(); + + if (inuse == NULL) { + *error = g_error_new(ERR_PIDGINKEYRING , ERR_NOKEYRING, + "No keyring configured, cannot export password info"); + purple_debug_info("keyring", + "No keyring configured, cannot export password info"); + return FALSE; + } + + *keyringid = purple_keyring_get_id(inuse); + + purple_debug_info("keyring", + "Exporting password for account %s (%s) from keyring %s.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account), + *keyringid); + + if (*keyringid == NULL) { + *error = g_error_new(ERR_PIDGINKEYRING , ERR_INVALID, + "Plugin does not have a keyring id"); + purple_debug_info("keyring", + "Configured keyring does not have a keyring id, cannot export password"); + return FALSE; + } + + export = purple_keyring_get_export_password(inuse); + + if (export == NULL) { + *error = g_error_new(ERR_PIDGINKEYRING , ERR_NOCAP, + "Keyring cannot export password info."); + purple_debug_info("keyring", + "Keyring cannot export password info. This might be normal"); + return FALSE; + } + + return export(account, mode, data, error, destroy); +} + + +/** + * This should be renamed purple_keyring_get_password() when getting + * to 3.0, while dropping purple_keyring_get_password_sync(). + */ +void +purple_keyring_get_password_async(PurpleAccount * account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + GError * error = NULL; + const PurpleKeyring * inuse; + PurpleKeyringRead read; + + if (account == NULL) { + error = g_error_new(ERR_PIDGINKEYRING, ERR_INVALID, + "No account passed to the function."); + + if (cb != NULL) + cb(account, NULL, error, data); + + g_error_free(error); + + } else { + inuse = purple_keyring_get_inuse(); + + if (inuse == NULL) { + error = g_error_new(ERR_PIDGINKEYRING, ERR_NOKEYRING, + "No keyring configured."); + + if (cb != NULL) + cb(account, NULL, error, data); + + g_error_free(error); + + } else { + read = purple_keyring_get_read_password(inuse); + + if (read == NULL) { + error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP, + "Keyring cannot read password."); + + if (cb != NULL) + cb(account, NULL, error, data); + + g_error_free(error); + + } else { + read(account, cb, data); + } + } + } + + return; +} + +/** + * This should be renamed purple_keyring_set_password() when getting + * to 3.0, while dropping purple_keyring_set_password_sync(). + */ +void +purple_keyring_set_password_async(const PurpleAccount * account, + gchar * password, + GDestroyNotify destroypassword, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + GError * error = NULL; + const PurpleKeyring * inuse; + PurpleKeyringSave save; + + if (account == NULL) { + error = g_error_new(ERR_PIDGINKEYRING, ERR_INVALID, + "No account passed to the function."); + + if (cb != NULL) + cb(account, error, data); + + g_error_free(error); + + } else { + inuse = purple_keyring_get_inuse(); + + if (inuse == NULL) { + error = g_error_new(ERR_PIDGINKEYRING, ERR_NOKEYRING, + "No keyring configured."); + + if (cb != NULL) + cb(account, error, data); + + g_error_free(error); + + } else { + save = purple_keyring_get_save_password(inuse); + + if (save == NULL) { + error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP, + "Keyring cannot save password."); + + if (cb != NULL) + cb(account, error, data); + + g_error_free(error); + + } else { + save(account, password, destroypassword, cb, data); + } + } + } + + return; +} + + +/** + * This should be dropped at 3.0 (it's here for compatibility) + */ +const char * +purple_keyring_get_password_sync(const PurpleAccount * account) +{ + PurpleKeyringReadSync read; + const PurpleKeyring * inuse; + + if (account == NULL) { + return NULL; + + } else { + + inuse = purple_keyring_get_inuse(); + + if (inuse == NULL) { + + return NULL; + + } else { + + read = purple_keyring_get_read_sync(inuse); + + if (read == NULL){ + + return NULL; + + } else { + + return read(account); + + } + } + } +} + +/** + * This should be dropped at 3.0 (it's here for compatibility) + */ +void +purple_keyring_set_password_sync(PurpleAccount * account, + const char *password) +{ + PurpleKeyringSaveSync save; + const PurpleKeyring * inuse; + + if (account != NULL) { + + inuse = purple_keyring_get_inuse(); + + if (inuse != NULL) { + + save = purple_keyring_get_save_sync(inuse); + + if (save != NULL){ + + return save(account, password); + + } + } + } + + return; +} + +void +purple_keyring_close(PurpleKeyring * keyring, + GError ** error) +{ + PurpleKeyringClose close; + + if (keyring == NULL) { + *error = g_error_new(ERR_PIDGINKEYRING, ERR_INVALID, + "No keyring passed to the function."); + + return; + + } else { + close = purple_keyring_get_close_keyring(keyring); + + if (close == NULL) { + *error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP, + "Keyring doesn't support being closed."); + + } else { + close(error); + + } + } + + return; +} + + +void +purple_keyring_change_master(PurpleKeyringChangeMasterCallback cb, + gpointer data) +{ + GError * error = NULL; + PurpleKeyringChangeMaster change; + const PurpleKeyring * inuse; + + inuse = purple_keyring_get_inuse(); + + if (inuse == NULL) { + error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP, + "Keyring doesn't support master passwords."); + + cb(FALSE, error, data); + + g_error_free(error); + + } else { + + change = purple_keyring_get_change_master(inuse); + + if (change == NULL) { + error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP, + "Keyring doesn't support master passwords."); + + cb(FALSE, error, data); + + g_error_free(error); + + } else { + change(cb, data); + + } + } + return; +} + +/*@}*/ + + +/***************************************/ +/** @name Error Codes */ +/***************************************/ +/*@{*/ + +GQuark purple_keyring_error_domain(void) +{ + return g_quark_from_static_string("Libpurple keyring"); +} + +/*}@*/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/keyring.h Wed Aug 13 21:46:17 2008 +0000 @@ -0,0 +1,474 @@ +/** + * @file keyring.h Keyring plugin API + * @ingroup core + * + * @todo + * - Offer a way to prompt the user for a password or for a password change. + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_KEYRING_H_ +#define _PURPLE_KEYRING_H_ + +#include <glib.h> +#include "account.h" + +/** + * Default keyring + */ +#define FALLBACK_KEYRING "core-scrouaf-internalkeyring" + +/*******************************************************/ +/** @name data structures and types */ +/*******************************************************/ +/*@{*/ +typedef struct _PurpleKeyring PurpleKeyring; /* public (for now) */ + +/*@}*/ + +/** + * XXX maybe strip a couple GError* if they're not used, + * since they should only be interresting for the callback + * --> ability to forward errors ? + * + */ + +/********************************************************/ +/** @name Callbacks for basic keyring operation */ +/********************************************************/ +/*@{*/ + +/** + * Callback for once a password is read. If there was a problem, the password should + * be NULL, and the error set. + * + * @param account The account of which the password was asked. + * @param password The password that was read. This should be freed when the callback returns. + * @param error Error that could have occured. Must be freed by the calling function. + * @param data Data passed to the callback. + */ +typedef void (*PurpleKeyringReadCallback)(const PurpleAccount * account, + const gchar * password, + GError * error, + gpointer data); + +/** + * Callback for once a password has been stored. If there was a problem, the error will be set. + * + * @param error Error that could have occured. Must be freed by the calling function. + * @param account The account of which the password was saved. + * @param data Data passed to the callback. + */ +typedef void (*PurpleKeyringSaveCallback)(const PurpleAccount * account, + GError * error, + gpointer data); + +/** + * Callback for once the master password for a keyring has been changed. + * + * @param result Will be TRUE if the password has been changed, false otherwise. + * @param error Error that has occured. Must be freed if non NULL. + * @param data Data passed to the callback. + */ +typedef void (*PurpleKeyringChangeMasterCallback)(gboolean result, + GError * error, + gpointer data); + +/** + * Callback for when we change the keyring. + * + * @param keyring The keyring that is in use. + * @param result TRUE if the keyring was changed, FALSE otherwise. + * @param error An error that might have occured. + * @param data A pointer to user supplied data. + */ +typedef void (*PurpleKeyringSetInUseCallback)(const PurpleKeyring * keyring, + gboolean result, + GError * error, + gpointer data); + +/*@}*/ + +/********************************************************/ +/** @name pointers to the keyring's functions */ +/********************************************************/ +/*@{*/ + +/** + * Read the password for an account + * @param account The account which's password we want. + * @param cb A callback to be used once the password is found. + * @param data Data to be passed to the callback. + */ +typedef void (*PurpleKeyringRead)(PurpleAccount * account, + PurpleKeyringReadCallback cb, + gpointer data); + +/** + * Store a password in the keyring. + * @param account The account for which the password is to be stored. + * @param password The password to be stored. This password will be freed once + * the function returns, so one must take care to make a copy if they call other + * async functions later. If the password is NULL, this means that the keyring + * should forget about that password. + * @param destroypassword A function that will be called to free the password. + * @param cb A callback to be called once the password is saved. + * @param data A pointer to be passed to the callback + */ +typedef void (*PurpleKeyringSave)(PurpleAccount * account, + gchar * password, + GDestroyNotify destroypassword, + PurpleKeyringSaveCallback cb, + gpointer data); + +/** + * Read a password in an sync way. This is only used for API + * compatibility, and plugins are not expected to support this. + * Also, this should be dropped in 3.0. + * @param account The account. + * @return The password for the account. + */ +typedef const char * (*PurpleKeyringReadSync)(const PurpleAccount * account); + +/** + * Read a password in an sync way. This is only used for API + * compatibility, and plugins are not expected to support this. + * Also, this should be dropped in 3.0. + * @param account The account. + * @param password The password to save. + */ +typedef void (*PurpleKeyringSaveSync)(PurpleAccount * account, + const char * password); + +/** + * Close the keyring. + * This will be called so the keyring can do any cleanup it wants. + * @param error An error that may occur. + */ +typedef void (*PurpleKeyringClose)(GError ** error); + +/** + * Change the master password for the keyring. If NULL, it means the Keyring + * has no master password capabilities. + * @param error An error that may occur. + * @param cb A callback to call once the master password has been changed. + * @param data A pointer to pass to the callback. + */ +typedef void (*PurpleKeyringChangeMaster)(PurpleKeyringChangeMasterCallback cb, + gpointer data); + +/** + * Import info on an XML node. + * This is not async because it is not meant to prompt for a master password and decrypt passwords. + * @param account The account. + * @param mode A keyring specific option that was stored. Can be NULL, will be freed when function returns. + * @param data Data that was stored, Can be NULL, will be freed when function returns (so copy it if you need it). + * @return TRUE on success, FALSE on failure. + */ +typedef gboolean (*PurpleKeyringImportPassword)(PurpleAccount * account, + char * mode, + char * data, + GError ** error); + +/** + * Export information that will be stored in an XML node. + * This is not async because it is not meant to prompt for a master password or encrypt passwords. + * @param account The account for which we want the info. + * @param mode An option field that can be used by the plugin. This is expected to be a static string. + * @param data The data to be stored in the XML node. This string will be freed using destroy() once not needed anymore. + * @param error Will be set if a problem occured. + * @param destroy A function to be called, if non NULL, to free data. + * @return TRUE on success, FALSE on failure. + */ +typedef gboolean (*PurpleKeyringExportPassword)(PurpleAccount * account, + const char ** mode, + char ** data, + GError ** error, + GDestroyNotify * destroy); + + +/*@}*/ + + +/***************************************/ +/** @name Keyring API */ +/***************************************/ +/*@{*/ + +/** + * Find a keyring from it's keyring id. + * @param id The id for the keyring. + * @return The keyring, or NULL if not found. + */ +PurpleKeyring * purple_keyring_get_keyring_by_id(const char * id); + +/** + * Get a list of id/name pairs (for preferences) + * @return The list. + */ +GList * purple_keyring_get_options(void); + +/** + * Prepare stuff at startup. + */ +void purple_keyring_init(void); + +/** + * Do some cleanup. + */ +void purple_keyring_uninit(void); + +/** + * Get the keyring list. Used by the UI. + */ +const GList * +purple_keyring_get_keyrings(void); + +/** + * Get the keyring being used. + */ +const PurpleKeyring * +purple_keyring_get_inuse(void); + +/** + * Set the keyring to use. This function will move all passwords from + * the old keyring to the new one. If it fails, it will cancel all changes, + * close the new keyring, and notify the callback. If it succeeds, it will + * remove all passwords from the old safe and close that safe. + * @param newkeyring The new keyring to use. + * @param error Error that may occur. + * @param cb The callback to call once the change is done. + * @param data A pointer that will be passed to the callback. + * @param force FALSE if the change can be cancelled. If this is TRUE and an + * error occurs, data might be lost. + */ +void +purple_keyring_set_inuse(const PurpleKeyring * newkeyring, + gboolean force, + PurpleKeyringSetInUseCallback cb, + gpointer data); + + +/** + * Register a keyring plugin. + * @param keyrint The keyring to register. + */ +void +purple_keyring_register(PurpleKeyring * keyring); + +/** + * Unregister a keyring plugin. In case the keyring is in use, + * passwords will be moved to a fallback safe, and the keyring + * to unregister will be properly closed. + * @param keyrint The keyring to unregister. + */ +void +purple_keyring_unregister(PurpleKeyring * keyring); + +/*@}*/ + + +/***************************************/ +/** @name Keyring plugin wrappers */ +/***************************************/ +/*@{*/ + +/** + * used by account.c while reading a password from xml. + * @param account The account. + * @param keyringid The plugin ID that was stored in the xml file. Can be NULL. + * @param mode A keyring specific option that was stored. Can be NULL. + * @param data Data that was stored, can be NULL. + * @return TRUE if the input was accepted, FALSE otherwise. + */ +gboolean purple_keyring_import_password(PurpleAccount * account, + char * keyringid, + char * mode, + char * data, + GError ** error); + +/** + * used by account.c while syncing accounts + * @param account The account for which we want the info. + * @param keyringid The plugin id to be stored in the XML node. This will be NULL or a string that can be considered static. + * @param mode An option field that can be used by the plugin. This will be NULL or a string that can be considered static. + * @param data The data to be stored in the XML node. This string must be freed using destroy() once not needed anymore if it is not NULL. + * @param error Will be set if a problem occured. + * @param destroy A function to be called, if non NULL, to free data. + * @return TRUE if the info was exported successfully, FALSE otherwise. + */ +gboolean +purple_keyring_export_password(PurpleAccount * account, + const char ** keyringid, + const char ** mode, + char ** data, + GError ** error, + GDestroyNotify * destroy); + + + +/** + * functions called from the code to access passwords (account.h): + * purple_account_get_password() + * purple_account_set_password() + * @todo : + * - rewrite these functions to use the sync functions for compatibility, + * - build an async way to access the async functions, and patch all libpurple + * code that calls the accessors to use new ones. + */ + +/** + * Read a password from the active safe. + * This should be renamed purple_keyring_get_password() when getting + * to 3.0, while dropping purple_keyring_get_password_sync(). + * @param account The account for which we want the password. + * @param cb The callback to be called. + * @param data A pointer passed to the callback. + */ +void +purple_keyring_get_password_async(PurpleAccount * account, + PurpleKeyringReadCallback cb, + gpointer data); + +/** + * Set a password to be remembered. + * This should be renamed purple_keyring_set_password() when getting + * to 3.0, while dropping purple_keyring_set_password_sync().keyring + * @param account The account for which the password is to be saved. + * @param password The password to save. + * @param destroypassword A function called to free the password. Can be NULL. + * @param cb A callback for once the password is saved. + * @param data A pointer to be passed to the callback. + */ +void +purple_keyring_set_password_async(const PurpleAccount * account, + gchar * password, + GDestroyNotify destroypassword, + PurpleKeyringSaveCallback cb, + gpointer data); + +/** + * Read a password in a synchronous way. + * This is here only for compatibility reasons. Keyrings are not + * expected to support this (and shouldn't), and new code should + * use purple_keyring_get_password_async() instead. + * @param account The account for which we want the password. + * @return A pointer to the the password. + */ +const char * +purple_keyring_get_password_sync(const PurpleAccount * account); + +/** + * Save a password in a synchronous way. + * This is here only for compatibility reasons. Keyrings are not + * expected to support this (and shouldn't), and new code should + * use purple_keyring_set_password_async() instead. + * @param account The account for which we want the password. + * @param password The password to save. + */ +void +purple_keyring_set_password_sync(PurpleAccount * account, + const char *password); + +/** + * Close a safe. + * @param keyring The safe to close. + * @param error Error that might occur. + */ +void +purple_keyring_close(PurpleKeyring * keyring, + GError ** error); + +/** + * Change the master password for a safe (if the safe supports it). + * @param cb A callback to notify once we're done. + * @param data A pointer to be passed to the callback. + */ +void +purple_keyring_change_master(PurpleKeyringChangeMasterCallback cb, + gpointer data); + +/*@}*/ + +/***************************************/ +/** @name PurpleKeyring Accessors */ +/***************************************/ +/*@{*/ + +PurpleKeyring * purple_keyring_new(void); +void purple_keyring_free(PurpleKeyring * keyring); + +const char * purple_keyring_get_name(const PurpleKeyring * info); +const char * purple_keyring_get_id(const PurpleKeyring * info); +PurpleKeyringRead purple_keyring_get_read_password(const PurpleKeyring * info); +PurpleKeyringSave purple_keyring_get_save_password(const PurpleKeyring * info); +PurpleKeyringReadSync purple_keyring_get_read_sync(const PurpleKeyring * info); +PurpleKeyringSaveSync purple_keyring_get_save_sync(const PurpleKeyring * info); +PurpleKeyringClose purple_keyring_get_close_keyring(const PurpleKeyring * info); +PurpleKeyringChangeMaster purple_keyring_get_change_master(const PurpleKeyring * info); +PurpleKeyringImportPassword purple_keyring_get_import_password(const PurpleKeyring * info); +PurpleKeyringExportPassword purple_keyring_get_export_password(const PurpleKeyring * info); + +void purple_keyring_set_name(PurpleKeyring * info, char * name); +void purple_keyring_set_id(PurpleKeyring * info, char * id); +void purple_keyring_set_read_sync(PurpleKeyring * info, PurpleKeyringReadSync read); +void purple_keyring_set_save_sync(PurpleKeyring * info, PurpleKeyringSaveSync save); +void purple_keyring_set_read_password(PurpleKeyring * info, PurpleKeyringRead read); +void purple_keyring_set_save_password(PurpleKeyring * info, PurpleKeyringSave save); +void purple_keyring_set_close_keyring(PurpleKeyring * info, PurpleKeyringClose close); +void purple_keyring_set_change_master(PurpleKeyring * info, PurpleKeyringChangeMaster change_master); +void purple_keyring_set_import_password(PurpleKeyring * info, PurpleKeyringImportPassword import_password); +void purple_keyring_set_export_password(PurpleKeyring * info, PurpleKeyringExportPassword export_password); + +/*@}*/ + +/***************************************/ +/** @name Error Codes */ +/***************************************/ +/*@{*/ + +/** + * Error domain GQuark. + * See @ref purple_keyring_error_domain . + */ +#define ERR_PIDGINKEYRING purple_keyring_error_domain() +/** stuff here too */ +GQuark purple_keyring_error_domain(void); + +/** error codes for keyrings. */ +enum PurpleKeyringError +{ + ERR_OK = 0, /**< no error. */ + ERR_NOPASSWD = 1, /**< no stored password. */ + ERR_NOACCOUNT, /**< account not found. */ + ERR_WRONGPASS, /**< user submitted wrong password when prompted. */ + ERR_WRONGFORMAT, /**< data passed is not in suitable format. */ + ERR_NOKEYRING, /**< no keyring configured. */ + ERR_NOCHANNEL, /**< failed to communicate with the backend */ + ERR_INVALID, /**< invalid input */ + ERR_NOCAP, /**< keyring doesn't support this */ + ERR_UNKNOWN /**< unknown error */ +}; + +/*}@*/ +#endif /* _PURPLE_KEYRING_H_ */
--- a/libpurple/plugin.c Wed Aug 13 19:38:25 2008 +0000 +++ b/libpurple/plugin.c Wed Aug 13 21:46:17 2008 +0000 @@ -489,6 +489,12 @@ } } + if (plugin->info->flags & PURPLE_PLUGIN_FLAG_AUTOLOAD) { + purple_debug_info("plugins", "Loading autoload plugin %s\n", + plugin->path); + purple_plugin_load(plugin); // + } + return plugin; #else return NULL; @@ -1406,7 +1412,7 @@ } protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin, - (GCompareFunc)compare_prpl); + (GCompareFunc)compare_prpl); } }
--- a/libpurple/plugin.h Wed Aug 13 19:38:25 2008 +0000 +++ b/libpurple/plugin.h Wed Aug 13 21:46:17 2008 +0000 @@ -62,6 +62,7 @@ #define PURPLE_PRIORITY_LOWEST -9999 #define PURPLE_PLUGIN_FLAG_INVISIBLE 0x01 +#define PURPLE_PLUGIN_FLAG_AUTOLOAD 0x02 #define PURPLE_PLUGIN_MAGIC 5 /* once we hit 6.0.0 I think we can remove this */
--- a/libpurple/plugins/Makefile.am Wed Aug 13 19:38:25 2008 +0000 +++ b/libpurple/plugins/Makefile.am Wed Aug 13 21:46:17 2008 +0000 @@ -1,4 +1,4 @@ -DIST_SUBDIRS = mono perl ssl tcl +DIST_SUBDIRS = mono perl ssl tcl keyrings if USE_PERL PERL_DIR = perl @@ -20,7 +20,8 @@ $(MONO_DIR) \ $(PERL_DIR) \ ssl \ - $(TCL_DIR) + $(TCL_DIR) \ + keyrings plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/internalkeyring.c Wed Aug 13 21:46:17 2008 +0000 @@ -0,0 +1,304 @@ +/** + * @file internalkeyring.c internal keyring + * @ingroup plugins + * + * @todo + * cleanup error handling and reporting + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifndef PURPLE_PLUGINS +# define PURPLE_PLUGINS +#endif + +#include <glib.h> + +#ifndef G_GNUC_NULL_TERMINATED +# if __GNUC__ >= 4 +# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) +# else +# define G_GNUC_NULL_TERMINATED +# endif +#endif + +#include "account.h" +#include "version.h" +#include "keyring.h" +#include "debug.h" +#include "plugin.h" + +#define INTERNALKEYRING_NAME "Internal keyring" +#define INTERNALKEYRING_VERSION "0.7" +#define INTERNALKEYRING_DESCRIPTION "This plugin provides the default password storage behaviour for libpurple." +#define INTERNALKEYRING_AUTHOR "Scrouaf (scrouaf[at]soc.pidgin.im)" +#define INTERNALKEYRING_ID FALLBACK_KEYRING + + +#define GET_PASSWORD(account) \ + g_hash_table_lookup (internal_keyring_passwords, account) +#define SET_PASSWORD(account, password) \ + g_hash_table_replace(internal_keyring_passwords, account, password) + + +GHashTable * internal_keyring_passwords = NULL; +static PurpleKeyring * keyring_handler = NULL; + +/* a few prototypes : */ +static void internal_keyring_read(const PurpleAccount *, PurpleKeyringReadCallback, gpointer); +static void internal_keyring_save(const PurpleAccount *, gchar *, GDestroyNotify, PurpleKeyringSaveCallback, gpointer); +static const char * internal_keyring_read_sync(const PurpleAccount *); +static void internal_keyring_save_sync(PurpleAccount *, const gchar *); +static void internal_keyring_close(GError **); +static gboolean internal_keyring_import_password(PurpleAccount *, char *, char *, GError **); +static gboolean internal_keyring_export_password(PurpleAccount *, const char **, char **, GError **, GDestroyNotify *); +static void internal_keyring_init(void); +static void internal_keyring_uninit(void); +static gboolean internal_keyring_load(PurplePlugin *); +static gboolean internal_keyring_unload(PurplePlugin *); +static void internal_keyring_destroy(PurplePlugin *); + +/***********************************************/ +/* Keyring interface */ +/***********************************************/ +static void +internal_keyring_read(const PurpleAccount * account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + char * password; + GError * error; + + password = GET_PASSWORD(account); + + if (password != NULL) { + cb(account, password, NULL, data); + } else { + error = g_error_new(ERR_PIDGINKEYRING, + ERR_NOPASSWD, "password not found"); + cb(account, NULL, error, data); + g_error_free(error); + } + return; +} + +static void +internal_keyring_save(const PurpleAccount * account, + gchar * password, + GDestroyNotify destroy, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + gchar * copy; + + if (password == NULL) { + g_hash_table_remove(internal_keyring_passwords, account); + } else { + copy = g_strdup(password); + SET_PASSWORD((void *)account, copy); /* cast prevents warning because account is const */ + } + + if(destroy != NULL) + destroy(password); + + cb(account, NULL, data); + return; +} + + +static const char * +internal_keyring_read_sync(const PurpleAccount * account) +{ + purple_debug_info("keyring", "password was read\n"); + return GET_PASSWORD(account); +} + +static void +internal_keyring_save_sync(PurpleAccount * account, + const char * password) +{ + gchar * copy; + + if (password == NULL) { + g_hash_table_remove(internal_keyring_passwords, account); + } else { + copy = g_strdup(password); + SET_PASSWORD(account, copy); + } + purple_debug_info("keyring", "password was set\n"); + return; +} + +static void +internal_keyring_close(GError ** error) +{ + internal_keyring_uninit(); +} + +static gboolean +internal_keyring_import_password(PurpleAccount * account, + char * mode, + char * data, + GError ** error) +{ + gchar * copy; + + if (account != NULL && + data != NULL && + (mode == NULL || g_strcmp0(mode, "cleartext") == 0)) { + + copy = g_strdup(data); + SET_PASSWORD(account, copy); + return TRUE; + + } else { + + *error = g_error_new(ERR_PIDGINKEYRING, ERR_NOPASSWD, "no password for account"); + return FALSE; + + } +} + +static gboolean +internal_keyring_export_password(PurpleAccount * account, + const char ** mode, + char ** data, + GError ** error, + GDestroyNotify * destroy) +{ + gchar * password; + + password = GET_PASSWORD(account); + + if (password == NULL) { + return FALSE; + } else { + *mode = "cleartext"; + *data = g_strdup(password); + *destroy = g_free; + return TRUE; + } +} + + + + +static void +internal_keyring_init() +{ + keyring_handler = purple_keyring_new(); + + purple_keyring_set_name(keyring_handler, INTERNALKEYRING_NAME); + purple_keyring_set_id(keyring_handler, INTERNALKEYRING_ID); + purple_keyring_set_read_sync(keyring_handler, internal_keyring_read_sync); + purple_keyring_set_save_sync(keyring_handler, internal_keyring_save_sync); + purple_keyring_set_read_password(keyring_handler, internal_keyring_read); + purple_keyring_set_save_password(keyring_handler, internal_keyring_save); + purple_keyring_set_close_keyring(keyring_handler, internal_keyring_close); + purple_keyring_set_change_master(keyring_handler, NULL); + purple_keyring_set_import_password(keyring_handler, internal_keyring_import_password); + purple_keyring_set_export_password(keyring_handler, internal_keyring_export_password); + + purple_keyring_register(keyring_handler); + + internal_keyring_passwords = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); +} + +static void +internal_keyring_uninit() +{ + purple_keyring_free(keyring_handler); + keyring_handler = NULL; + + g_hash_table_destroy(internal_keyring_passwords); + internal_keyring_passwords = NULL; +} + + + +/***********************************************/ +/* Plugin interface */ +/***********************************************/ + +static gboolean +internal_keyring_load(PurplePlugin *plugin) +{ + internal_keyring_init(); + return TRUE; +} + +static gboolean +internal_keyring_unload(PurplePlugin *plugin) +{ + internal_keyring_uninit(); + return TRUE; +} + +static void +internal_keyring_destroy(PurplePlugin *plugin) +{ + internal_keyring_uninit(); + return; +} + +PurplePluginInfo plugininfo = +{ + PURPLE_PLUGIN_MAGIC, /* magic */ + PURPLE_MAJOR_VERSION, /* major_version */ + PURPLE_MINOR_VERSION, /* minor_version */ + PURPLE_PLUGIN_STANDARD, /* type */ + NULL, /* ui_requirement */ + PURPLE_PLUGIN_FLAG_INVISIBLE|PURPLE_PLUGIN_FLAG_AUTOLOAD, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + INTERNALKEYRING_ID, /* id */ + INTERNALKEYRING_NAME, /* name */ + INTERNALKEYRING_VERSION, /* version */ + "Internal Keyring Plugin", /* summary */ + INTERNALKEYRING_DESCRIPTION, /* description */ + INTERNALKEYRING_AUTHOR, /* author */ + "N/A", /* homepage */ + internal_keyring_load, /* load */ + internal_keyring_unload, /* unload */ + internal_keyring_destroy, /* destroy */ + NULL, /* ui_info */ + NULL, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, /* padding... */ + NULL, + NULL, + NULL, +}; + +static void +init_plugin(PurplePlugin *plugin) +{ + purple_debug_info("internalkeyring", "init plugin called.\n"); +} + +PURPLE_INIT_PLUGIN(internal_keyring, init_plugin, plugininfo) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/Makefile.am Wed Aug 13 21:46:17 2008 +0000 @@ -0,0 +1,41 @@ +EXTRA_DIST = \ + Makefile.mingw + +plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) + +gnomekeyring_la_LDFLAGS = -module -avoid-version +internalkeyring_la_LDFLAGS = -module -avoid-version + +GNOME_KEYRING_CFLAGS = -I/usr/include/gnome-keyring-1 +GNOME_KEYRING_LIBS = -lgnome-keyring + +GKRSOURCES = gnomekeyring.c +IKSOURCES = internalkeyring.c + +if PLUGINS + +plugin_LTLIBRARIES = \ + gnomekeyring.la \ + internalkeyring.la + +gnomekeyring_la_SOURCES = gnomekeyring.c +internalkeyring_la_SOURCES = internalkeyring.c + +gnomekeyring_la_LIBADD = $(GLIB_LIBS) $(GNOME_KEYRING_LIBS) +internalkeyring_la_LIBADD = $(GLIB_LIBS) + +endif #PLUGINS + +AM_CPPFLAGS = \ + -I$(top_srcdir)/libpurple \ + -I$(top_builddir)/libpurple \ + $(GLIB_CFLAGS) \ + $(DEBUG_CFLAGS) \ + $(PLUGIN_CFLAGS) \ + $(GNOME_KEYRING_CFLAGS) + +gnomekeyring_la_CFLAGS = $(AM_CPPFLAGS) $(GNOME_KEYRING_CFLAGS) +internalkeyring_la_CFLAGS = $(AM_CPPFLAGS) + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/gnomekeyring.c Wed Aug 13 21:46:17 2008 +0000 @@ -0,0 +1,473 @@ +/** + * @file gnomekeyring.c Gnome keyring password storage + * @ingroup plugins + * + * @todo + * cleanup error handling and reporting + * refuse unloading when active (in internal keyring too) + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifndef PURPLE_PLUGINS +# define PURPLE_PLUGINS +#endif + +#include <glib.h> +#include <gnome-keyring.h> + +#ifndef G_GNUC_NULL_TERMINATED +# if __GNUC__ >= 4 +# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) +# else +# define G_GNUC_NULL_TERMINATED +# endif +#endif + +#include "account.h" +#include "version.h" +#include "keyring.h" +#include "debug.h" +#include "plugin.h" + +#define GNOMEKEYRING_NAME "Gnome-Keyring" +#define GNOMEKEYRING_VERSION "0.2" +#define GNOMEKEYRING_DESCRIPTION "This plugin provides the default password storage behaviour for libpurple." +#define GNOMEKEYRING_AUTHOR "Scrouaf (scrouaf[at]soc.pidgin.im)" +#define GNOMEKEYRING_ID "core-scrouaf-gnomekeyring" + +#define ERR_GNOMEKEYRINGPLUGIN gkp_error_domain() + +static PurpleKeyring * keyring_handler = NULL; + +typedef struct _InfoStorage InfoStorage; + +struct _InfoStorage +{ + gpointer cb; + gpointer user_data; + PurpleAccount * account; +}; + + + + +/* a few prototypes : */ +static GQuark gkp_error_domain(void); +static void gkp_read(PurpleAccount *, PurpleKeyringReadCallback, gpointer); +static void gkp_read_continue(GnomeKeyringResult, const char *, gpointer); +static void gkp_save(PurpleAccount *, gchar *, GDestroyNotify, PurpleKeyringSaveCallback, gpointer); +static void gkp_save_continue(GnomeKeyringResult, gpointer); +static const char * gkp_read_sync(const PurpleAccount *); +static void gkp_save_sync(PurpleAccount *, const gchar *); +static void gkp_close(GError **); +static gboolean gkp_import_password(PurpleAccount *, char *, char *, GError **); +static gboolean gkp_export_password(PurpleAccount *, const char **, char **, GError **, GDestroyNotify *); +static gboolean gkp_init(void); +static void gkp_uninit(void); +static gboolean gkp_load(PurplePlugin *); +static gboolean gkp_unload(PurplePlugin *); +static void gkp_destroy(PurplePlugin *); + +GQuark gkp_error_domain(void) +{ + return g_quark_from_static_string("Gnome-Keyring plugin"); +} + + +/***********************************************/ +/* Keyring interface */ +/***********************************************/ +static void +gkp_read(PurpleAccount * account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + InfoStorage * storage = g_malloc(sizeof(InfoStorage)); + + storage->cb = cb; + storage->user_data = data; + storage->account = account; + + gnome_keyring_find_password(GNOME_KEYRING_NETWORK_PASSWORD, + gkp_read_continue, + storage, + g_free, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); +} + +static void gkp_read_continue(GnomeKeyringResult result, + const char *password, + gpointer data) +/* XXX : make sure list is freed on return */ +{ + InfoStorage * storage = data; + PurpleAccount * account =storage->account; + PurpleKeyringReadCallback cb = storage->cb; + GError * error; + + if (result != GNOME_KEYRING_RESULT_OK) { + + switch(result) + { + case GNOME_KEYRING_RESULT_NO_MATCH : + error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, + ERR_NOPASSWD, "no password found for account : %s", + purple_account_get_username(account)); + if(cb != NULL) + cb(account, NULL, error, storage->user_data); + g_error_free(error); + return; + + case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON : + case GNOME_KEYRING_RESULT_IO_ERROR : + error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, + ERR_NOCHANNEL, "Failed to communicate with gnome keyring (account : %s).", + purple_account_get_username(account)); + if(cb != NULL) + cb(account, NULL, error, storage->user_data); + g_error_free(error); + return; + + default : + error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, + ERR_NOCHANNEL, "Unknown error (account : %s).", + purple_account_get_username(account)); + if(cb != NULL) + cb(account, NULL, error, storage->user_data); + g_error_free(error); + return; + } + + } else { + + if(cb != NULL) + cb(account, password, NULL, storage->user_data); + return; + + } + +} + + +static void +gkp_save(PurpleAccount * account, + gchar * password, + GDestroyNotify destroy, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + InfoStorage * storage = g_malloc(sizeof(InfoStorage)); + + storage->account = account; + storage->cb = cb; + storage->user_data = data; + + if(password != NULL) { + + purple_debug_info("Gnome keyring plugin", + "Updating password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + gnome_keyring_store_password(GNOME_KEYRING_NETWORK_PASSWORD, + NULL, /*default keyring */ + g_strdup_printf("pidgin-%s", purple_account_get_username(account)), + password, + gkp_save_continue, + storage, + g_free, /* function to free storage */ + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + + if (destroy) + destroy(password); + + } else { /* password == NULL, delete password. */ + + purple_debug_info("Gnome keyring plugin", + "Forgetting password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + gnome_keyring_delete_password(GNOME_KEYRING_NETWORK_PASSWORD, + gkp_save_continue, + storage, + g_free, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + + } + return; +} + +static void +gkp_save_continue(GnomeKeyringResult result, + gpointer data) +{ + InfoStorage * storage = data; + PurpleKeyringSaveCallback cb = storage->cb; + GError * error; + PurpleAccount * account = storage->account; + + if (result != GNOME_KEYRING_RESULT_OK) { + switch(result) + { + case GNOME_KEYRING_RESULT_NO_MATCH : + purple_debug_info("Gnome keyring plugin", + "Could not update password for %s (%s) : not found.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, + ERR_NOPASSWD, "Could not update password for %s : not found", + purple_account_get_username(account)); + if(cb != NULL) + cb(account, error, storage->user_data); + g_error_free(error); + return; + + case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON : + case GNOME_KEYRING_RESULT_IO_ERROR : + purple_debug_info("Gnome keyring plugin", + "Failed to communicate with gnome keyring (account : %s (%s)).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, + ERR_NOCHANNEL, "Failed to communicate with gnome keyring (account : %s).", + purple_account_get_username(account)); + if(cb != NULL) + cb(account, error, storage->user_data); + g_error_free(error); + return; + + default : + purple_debug_info("Gnome keyring plugin", + "Unknown error (account : %s (%s)).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, + ERR_NOCHANNEL, "Unknown error (account : %s).", + purple_account_get_username(account)); + if(cb != NULL) + cb(account, error, storage->user_data); + g_error_free(error); + return; + } + + } else { + + purple_debug_info("gnome-keyring-plugin", "password for %s updated.\n", + purple_account_get_username(account)); + + if(cb != NULL) + cb(account, NULL, storage->user_data); + return; + + } +} + +static const char * +gkp_read_sync(const PurpleAccount * account) +/** + * This is tricky, since the calling function will not free the password. + * Since we free the password on the next request, the last request will leak. + * But that part of the code shouldn't be used anyway. It might however be an issue + * if another call is made and the old pointer still used. + * Elegant solution would include a hashtable or a linked list, but would be + * complex. Also, this might just be dropped at 1.0 of the plugin + */ +{ + GnomeKeyringResult result; + static char * password = NULL; + + gnome_keyring_free_password(password); + + result = gnome_keyring_find_password_sync(GNOME_KEYRING_NETWORK_PASSWORD, + &password, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + + return password; +} + +static void +gkp_save_sync(PurpleAccount * account, + const char * password) +{ + char * copy = g_strdup(password); + gkp_save(account, copy, NULL, NULL, NULL); + g_free(copy); +} + +static void +gkp_close(GError ** error) +{ + return; +} + +static gboolean +gkp_import_password(PurpleAccount * account, + char * mode, + char * data, + GError ** error) +{ + purple_debug_info("Gnome Keyring plugin", + "Importing password.\n"); + return TRUE; +} + +static gboolean +gkp_export_password(PurpleAccount * account, + const char ** mode, + char ** data, + GError ** error, + GDestroyNotify * destroy) +{ + purple_debug_info("Gnome Keyring plugin", + "Exporting password.\n"); + *data = NULL; + *mode = NULL; + *destroy = NULL; + + return TRUE; +} + + + + +static gboolean +gkp_init() +{ + purple_debug_info("gnome-keyring-plugin", "init.\n"); + + if (gnome_keyring_is_available()) { + + keyring_handler = purple_keyring_new(); + + purple_keyring_set_name(keyring_handler, GNOMEKEYRING_NAME); + purple_keyring_set_id(keyring_handler, GNOMEKEYRING_ID); + purple_keyring_set_read_sync(keyring_handler, gkp_read_sync); + purple_keyring_set_save_sync(keyring_handler, gkp_save_sync); + purple_keyring_set_read_password(keyring_handler, gkp_read); + purple_keyring_set_save_password(keyring_handler, gkp_save); + purple_keyring_set_close_keyring(keyring_handler, gkp_close); + purple_keyring_set_change_master(keyring_handler, NULL); + purple_keyring_set_import_password(keyring_handler, gkp_import_password); + purple_keyring_set_export_password(keyring_handler, gkp_export_password); + + purple_keyring_register(keyring_handler); + + return TRUE; + + } else { + + purple_debug_info("gnome-keyring-plugin", + "failed to communicate with daemon, not loading."); + return FALSE; + } +} + +static void +gkp_uninit() +{ + purple_debug_info("gnome-keyring-plugin", "uninit.\n"); + gkp_close(NULL); + purple_keyring_unregister(keyring_handler); + purple_keyring_free(keyring_handler); + keyring_handler = NULL; +} + + + +/***********************************************/ +/* Plugin interface */ +/***********************************************/ + +static gboolean +gkp_load(PurplePlugin *plugin) +{ + return gkp_init(); +} + +static gboolean +gkp_unload(PurplePlugin *plugin) +{ + gkp_uninit(); + return TRUE; +} + +static void +gkp_destroy(PurplePlugin *plugin) +{ + gkp_uninit(); + return; +} + +PurplePluginInfo plugininfo = +{ + PURPLE_PLUGIN_MAGIC, /* magic */ + PURPLE_MAJOR_VERSION, /* major_version */ + PURPLE_MINOR_VERSION, /* minor_version */ + PURPLE_PLUGIN_STANDARD, /* type */ + NULL, /* ui_requirement */ + PURPLE_PLUGIN_FLAG_INVISIBLE|PURPLE_PLUGIN_FLAG_AUTOLOAD, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + GNOMEKEYRING_ID, /* id */ + GNOMEKEYRING_NAME, /* name */ + GNOMEKEYRING_VERSION, /* version */ + "Internal Keyring Plugin", /* summary */ + GNOMEKEYRING_DESCRIPTION, /* description */ + GNOMEKEYRING_AUTHOR, /* author */ + "N/A", /* homepage */ + gkp_load, /* load */ + gkp_unload, /* unload */ + gkp_destroy, /* destroy */ + NULL, /* ui_info */ + NULL, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, /* padding... */ + NULL, + NULL, + NULL, +}; + +static void +init_plugin(PurplePlugin *plugin) +{ + purple_debug_info("internalkeyring", "init plugin called.\n"); +} + +PURPLE_INIT_PLUGIN(internal_keyring, init_plugin, plugininfo) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/internalkeyring.c Wed Aug 13 21:46:17 2008 +0000 @@ -0,0 +1,369 @@ +/** + * @file internalkeyring.c internal keyring + * @ingroup plugins + * + * @todo + * cleanup error handling and reporting + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program ; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifndef PURPLE_PLUGINS +# define PURPLE_PLUGINS +#endif + +#include <glib.h> + +#ifndef G_GNUC_NULL_TERMINATED +# if __GNUC__ >= 4 +# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__)) +# else +# define G_GNUC_NULL_TERMINATED +# endif +#endif + +#include "account.h" +#include "version.h" +#include "keyring.h" +#include "debug.h" +#include "plugin.h" + +#define INTERNALKEYRING_NAME "Internal keyring" +#define INTERNALKEYRING_VERSION "0.7" +#define INTERNALKEYRING_DESCRIPTION "This plugin provides the default password storage behaviour for libpurple." +#define INTERNALKEYRING_AUTHOR "Scrouaf (scrouaf[at]soc.pidgin.im)" +#define INTERNALKEYRING_ID FALLBACK_KEYRING + + +#define GET_PASSWORD(account) \ + g_hash_table_lookup (internal_keyring_passwords, account) +#define SET_PASSWORD(account, password) \ + g_hash_table_replace(internal_keyring_passwords, account, password) +#define ACTIVATE()\ + if (internal_keyring_is_active == FALSE)\ + internal_keyring_open(); + + +static GHashTable * internal_keyring_passwords = NULL; +static PurpleKeyring * keyring_handler = NULL; +static gboolean internal_keyring_is_active = FALSE; + +/* a few prototypes : */ +static void internal_keyring_read(PurpleAccount *, PurpleKeyringReadCallback, gpointer); +static void internal_keyring_save(PurpleAccount *, gchar *, GDestroyNotify, PurpleKeyringSaveCallback, gpointer); +static const char * internal_keyring_read_sync(const PurpleAccount *); +static void internal_keyring_save_sync(PurpleAccount *, const gchar *); +static void internal_keyring_close(GError **); +static void internal_keyring_open(void); +static gboolean internal_keyring_import_password(PurpleAccount *, char *, char *, GError **); +static gboolean internal_keyring_export_password(PurpleAccount *, const char **, char **, GError **, GDestroyNotify *); +static void internal_keyring_init(void); +static void internal_keyring_uninit(void); +static gboolean internal_keyring_load(PurplePlugin *); +static gboolean internal_keyring_unload(PurplePlugin *); +static void internal_keyring_destroy(PurplePlugin *); + +/***********************************************/ +/* Keyring interface */ +/***********************************************/ +static void +internal_keyring_read(PurpleAccount * account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + char * password; + GError * error; + + ACTIVATE(); + + purple_debug_info("Internal Keyring", + "Reading password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + password = GET_PASSWORD(account); + + if (password != NULL) { + if(cb != NULL) + cb(account, password, NULL, data); + } else { + error = g_error_new(ERR_PIDGINKEYRING, + ERR_NOPASSWD, "password not found"); + if(cb != NULL) + cb(account, NULL, error, data); + g_error_free(error); + } + return; +} + +static void +internal_keyring_save(PurpleAccount * account, + gchar * password, + GDestroyNotify destroy, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + gchar * copy; + + ACTIVATE(); + + if (password == NULL) { + g_hash_table_remove(internal_keyring_passwords, account); + purple_debug_info("Internal Keyring", + "Deleted password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } else { + copy = g_strdup(password); + SET_PASSWORD((void *)account, copy); /* cast prevents warning because account is const */ + purple_debug_info("Internal Keyring", + "Updated password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + } + + if(destroy != NULL) + destroy(password); + + if (cb != NULL) + cb(account, NULL, data); + return; +} + + +static const char * +internal_keyring_read_sync(const PurpleAccount * account) +{ + ACTIVATE(); + + purple_debug_info("Internal Keyring (sync)", + "Password for %s (%s) was read.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + return GET_PASSWORD(account); +} + +static void +internal_keyring_save_sync(PurpleAccount * account, + const char * password) +{ + gchar * copy; + + ACTIVATE(); + + if (password == NULL) { + g_hash_table_remove(internal_keyring_passwords, account); + purple_debug_info("Internal Keyring (sync)", + "Password for %s (%s) was deleted.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } else { + copy = g_strdup(password); + SET_PASSWORD(account, copy); + purple_debug_info("Internal Keyring (sync)", + "Password for %s (%s) was set.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } + + return; +} + +static void +internal_keyring_close(GError ** error) +{ + internal_keyring_is_active = FALSE; + + g_hash_table_destroy(internal_keyring_passwords); + internal_keyring_passwords = NULL; +} + +static void +internal_keyring_open() +{ + internal_keyring_passwords = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, g_free); + internal_keyring_is_active = TRUE; +} + +static gboolean +internal_keyring_import_password(PurpleAccount * account, + char * mode, + char * data, + GError ** error) +{ + gchar * copy; + + ACTIVATE(); + + purple_debug_info("Internal keyring", + "Importing password"); + + if (account != NULL && + data != NULL && + (mode == NULL || g_strcmp0(mode, "cleartext") == 0)) { + + copy = g_strdup(data); + SET_PASSWORD(account, copy); + return TRUE; + + } else { + + *error = g_error_new(ERR_PIDGINKEYRING, ERR_NOPASSWD, "no password for account"); + return FALSE; + + } + + return TRUE; +} + +static gboolean +internal_keyring_export_password(PurpleAccount * account, + const char ** mode, + char ** data, + GError ** error, + GDestroyNotify * destroy) +{ + gchar * password; + + ACTIVATE(); + + purple_debug_info("Internal keyring", + "Exporting password"); + + password = GET_PASSWORD(account); + + if (password == NULL) { + return FALSE; + } else { + *mode = "cleartext"; + *data = g_strdup(password); + *destroy = g_free; + return TRUE; + } +} + + + + +static void +internal_keyring_init() +{ + keyring_handler = purple_keyring_new(); + + purple_keyring_set_name(keyring_handler, INTERNALKEYRING_NAME); + purple_keyring_set_id(keyring_handler, INTERNALKEYRING_ID); + purple_keyring_set_read_sync(keyring_handler, internal_keyring_read_sync); + purple_keyring_set_save_sync(keyring_handler, internal_keyring_save_sync); + purple_keyring_set_read_password(keyring_handler, internal_keyring_read); + purple_keyring_set_save_password(keyring_handler, internal_keyring_save); + purple_keyring_set_close_keyring(keyring_handler, internal_keyring_close); + purple_keyring_set_change_master(keyring_handler, NULL); + purple_keyring_set_import_password(keyring_handler, internal_keyring_import_password); + purple_keyring_set_export_password(keyring_handler, internal_keyring_export_password); + + purple_keyring_register(keyring_handler); +} + +static void +internal_keyring_uninit() +{ + internal_keyring_close(NULL); + purple_keyring_unregister(keyring_handler); + +} + + + +/***********************************************/ +/* Plugin interface */ +/***********************************************/ + +static gboolean +internal_keyring_load(PurplePlugin *plugin) +{ + internal_keyring_init(); + return TRUE; +} + +static gboolean +internal_keyring_unload(PurplePlugin *plugin) +{ + internal_keyring_uninit(); + + purple_keyring_free(keyring_handler); + keyring_handler = NULL; + + return TRUE; +} + +static void +internal_keyring_destroy(PurplePlugin *plugin) +{ + internal_keyring_uninit(); + return; +} + +PurplePluginInfo plugininfo = +{ + PURPLE_PLUGIN_MAGIC, /* magic */ + PURPLE_MAJOR_VERSION, /* major_version */ + PURPLE_MINOR_VERSION, /* minor_version */ + PURPLE_PLUGIN_STANDARD, /* type */ + NULL, /* ui_requirement */ + PURPLE_PLUGIN_FLAG_INVISIBLE|PURPLE_PLUGIN_FLAG_AUTOLOAD, /* flags */ + NULL, /* dependencies */ + PURPLE_PRIORITY_DEFAULT, /* priority */ + INTERNALKEYRING_ID, /* id */ + INTERNALKEYRING_NAME, /* name */ + INTERNALKEYRING_VERSION, /* version */ + "Internal Keyring Plugin", /* summary */ + INTERNALKEYRING_DESCRIPTION, /* description */ + INTERNALKEYRING_AUTHOR, /* author */ + "N/A", /* homepage */ + internal_keyring_load, /* load */ + internal_keyring_unload, /* unload */ + internal_keyring_destroy, /* destroy */ + NULL, /* ui_info */ + NULL, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, /* padding... */ + NULL, + NULL, + NULL, +}; + +static void +init_plugin(PurplePlugin *plugin) +{ + purple_debug_info("internalkeyring", "init plugin called.\n"); +} + +PURPLE_INIT_PLUGIN(internal_keyring, init_plugin, plugininfo) +
--- a/libpurple/xmlnode.h Wed Aug 13 19:38:25 2008 +0000 +++ b/libpurple/xmlnode.h Wed Aug 13 21:46:17 2008 +0000 @@ -160,7 +160,7 @@ * * @param node The node to set an attribute for. * @param attr The name of the attribute to set - * @param prefix The prefix of the attribute to ste + * @param prefix The prefix of the attribute to set * @param value The value of the attribute */ void xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value);
--- a/pidgin/gtkaccount.c Wed Aug 13 19:38:25 2008 +0000 +++ b/pidgin/gtkaccount.c Wed Aug 13 21:46:17 2008 +0000 @@ -402,6 +402,7 @@ GList *user_splits; GList *l, *l2; char *username = NULL; + char *password = NULL; if (dialog->protocol_menu != NULL) { @@ -561,9 +562,10 @@ /* Set the fields. */ if (dialog->account != NULL) { - if (purple_account_get_password(dialog->account)) - gtk_entry_set_text(GTK_ENTRY(dialog->password_entry), - purple_account_get_password(dialog->account)); + password = purple_account_get_password(dialog->account); + + if (password) + gtk_entry_set_text(GTK_ENTRY(dialog->password_entry), password); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(dialog->remember_pass_check),
--- a/pidgin/gtkprefs.c Wed Aug 13 19:38:25 2008 +0000 +++ b/pidgin/gtkprefs.c Wed Aug 13 21:46:17 2008 +0000 @@ -37,6 +37,7 @@ #include "sound.h" #include "util.h" #include "network.h" +#include "keyring.h" #include "gtkblist.h" #include "gtkconv.h" @@ -1631,6 +1632,37 @@ return ret; } + +static GtkWidget * +keyring_page(void) +{ + GtkWidget *ret; + GtkWidget *vbox; + GtkWidget *menu; + GtkWidget *changemaster; + GList *names; + + + purple_debug_info("prefs", "drawing keyring prefs page.\n"); + ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); + gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER); + + + vbox = pidgin_make_frame (ret, _("Keyring")); + + names = purple_keyring_get_options(); + + pidgin_prefs_dropdown_from_list(vbox, _("Keyring"), PURPLE_PREF_STRING, + "/purple/keyring/active", names); + + g_list_free(names); + + + gtk_widget_show_all(ret); + + return ret; +} + #ifndef _WIN32 static gint sound_cmd_yeah(GtkEntry *entry, gpointer d) { @@ -2153,6 +2185,7 @@ } #endif prefs_notebook_add_page(_("Logging"), logging_page(), notebook_page++); + prefs_notebook_add_page(_("Password Storage"), keyring_page(), notebook_page++); prefs_notebook_add_page(_("Status / Idle"), away_page(), notebook_page++); }