Mon, 11 Aug 2008 00:49:06 +0000
propagate from branch 'im.pidgin.pidgin' (head 94f8b49d1192a4470f2af0eab91c15442e9ea1a4)
to branch 'im.pidgin.soc.2008.masterpassword' (head 317cd0a252c217ae4851872482e2d3acc1db51c9)
| COPYRIGHT | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/qq/crypt.c | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/qq/crypt.h | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/qq/group_network.c | file | annotate | diff | comparison | revisions | |
| libpurple/protocols/qq/group_network.h | file | annotate | diff | comparison | revisions |
--- a/COPYRIGHT Sun Aug 10 21:49:23 2008 +0000 +++ b/COPYRIGHT Mon Aug 11 00:49:06 2008 +0000 @@ -30,6 +30,7 @@ Dave Bell Igor Belyi Brian Bernas +Vivien Bernet-Rollande Paul Betts Jonas Birmé George-Cristian Bîrzan
--- a/libpurple/Makefile.am Sun Aug 10 21:49:23 2008 +0000 +++ b/libpurple/Makefile.am Mon Aug 11 00:49:06 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 Sun Aug 10 21:49:23 2008 +0000 +++ b/libpurple/account.c Mon Aug 11 00:49:06 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 asynch 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; + GDestroyNotify destroy; node = xmlnode_new("account"); @@ -371,11 +392,26 @@ 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.\n"); + purple_keyring_export_password(account, &keyring_id, + &mode, &data, &error, &destroy); + + if (error != NULL) { + + /* Output debug info */ + + } else { + + child = xmlnode_new_child(node, "password"); + xmlnode_set_attrib(child, "keyringid", 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) @@ -765,7 +801,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; + GError * error = NULL; child = xmlnode_get_child(node, "protocol"); if (child != NULL) @@ -793,15 +833,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 +905,28 @@ 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); + g_free(mode); + g_free(data); + } else { + purple_debug_info("accounts", "failed to imported password.\n"); + // FIXME handle error + } + } + return ret; } @@ -1035,21 +1088,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 +1240,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,8 +1598,14 @@ { 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(); } @@ -1887,14 +1997,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 Sun Aug 10 21:49:23 2008 +0000 +++ b/libpurple/account.h Mon Aug 11 00:49:06 2008 +0000 @@ -46,6 +46,7 @@ #include "proxy.h" #include "prpl.h" #include "status.h" +#include "keyring.h" /** * Account request types. @@ -549,7 +550,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 +558,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 Sun Aug 10 21:49:23 2008 +0000 +++ b/libpurple/connection.c Mon Aug 11 00:49:06 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 Sun Aug 10 21:49:23 2008 +0000 +++ b/libpurple/core.c Mon Aug 11 00:49:06 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 Mon Aug 11 00:49:06 2008 +0000 @@ -0,0 +1,1094 @@ +/** + * @file keyring.c Keyring plugin API + * @todo + * - purple_keyring_() + * - loading : find a way to fallback + */ + +/* 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" + +/******************************************/ +/** @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 */ +}; + + +/* 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; + + +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 */ + + purple_prefs_add_string("/purple/keyring/active", "txt"); + purple_prefs_connect_callback(NULL, "/purple/keyring/active", + purple_keyring_pref_cb, NULL); + + + touse = purple_prefs_get_string("/purple/keyring/active"); + + if (touse == NULL) { + 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_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); + + return; +} + +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; +}; + +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 = (struct _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 : + g_debug("Keyring could not save password for account %s : %s", name, error->message); + break; + + case ERR_NOPASSWD : + g_debug("No password found while changing keyring for account %s : %s", + name, error->message); + break; + + case ERR_NOCHANNEL : + g_debug("Failed to communicate with backend while changing keyring for account %s : %s Aborting changes.", + name, error->message); + tracker->abort = TRUE; + break; + + default : + g_debug("Unknown error while changing keyring for account %s : %s", 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) { + + 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); + + } else { + close = purple_keyring_get_close_keyring(tracker->old); + close(&error); + + tracker->cb(tracker->new, TRUE, error, tracker->data); + } + + g_free(tracker); + } + return; +} + + +static void +purple_keyring_set_inuse_got_pw_cb(const PurpleAccount * account, + gchar * password, + GError * error, + gpointer data) +{ + const PurpleKeyring * new; + PurpleKeyringSave save; + struct _PurpleKeyringChangeTracker * tracker; + + tracker = (struct _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; + struct _PurpleKeyringChangeTracker * tracker; + GError * error = NULL; + + purple_debug_info("keyring", "Attempting to set new keyring : %s.\n", + newkeyring->id); + + 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"); + g_debug("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(struct _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; + } +} + + + +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, /* XXX */NULL, data); /* FIXME This should have a callback that can cancel the action */ + + return; +} + +GList * +purple_keyring_get_options() +{ + const GList * keyrings; + PurpleKeyring * keyring; + static 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); + } + + 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; + + 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(); + + if (inuse == keyring) { + fallback = purple_keyring_find_keyring_by_id(FALLBACK_KEYRING); + + /* this is problematic. If it fails, we won't detect it */ + purple_keyring_set_inuse(fallback, 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).\n", + purple_account_get_username(account), purple_account_get_protocol_id(account)); + + 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) || + 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. Data will be lost."); + 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; + + purple_debug_info("keyring", "exporting password.\n"); + + inuse = purple_keyring_get_inuse(); + + if (inuse == NULL) { + *error = g_error_new(ERR_PIDGINKEYRING , ERR_NOKEYRING, + "No keyring configured, cannot import password info"); + g_debug("No keyring configured, cannot import password info"); + return FALSE; + } + + *keyringid = purple_keyring_get_id(inuse); + + if (*keyringid == NULL) { + *error = g_error_new(ERR_PIDGINKEYRING , ERR_INVALID, + "Plugin does not have a keyring id"); + g_debug("Plugin does not have a keyring id"); + 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."); + g_debug("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 Mon Aug 11 00:49:06 2008 +0000 @@ -0,0 +1,473 @@ +/** + * @file keyring.h Keyring plugin API + * @ingroup core + * + * @todo + * - Offer a way to prompt the user for a password or for a password change. + * - write accessors and types for sync access. + */ + +/* 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, + 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)(const 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)(const 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 */ +/***************************************/ +/*@{*/ + +PurpleKeyring * +purple_keyring_get_keyring_by_id(const char * id); +GList * +purple_keyring_get_options(void); +void +purple_keyring_pref_cb(const char *pref, + PurplePrefType type, + gconstpointer name, + gpointer data); +/** + * 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 Sun Aug 10 21:49:23 2008 +0000 +++ b/libpurple/plugin.c Mon Aug 11 00:49:06 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 Sun Aug 10 21:49:23 2008 +0000 +++ b/libpurple/plugin.h Mon Aug 11 00:49:06 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 Sun Aug 10 21:49:23 2008 +0000 +++ b/libpurple/plugins/Makefile.am Mon Aug 11 00:49:06 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) @@ -41,6 +42,7 @@ signals_test_la_LDFLAGS = -module -avoid-version simple_la_LDFLAGS = -module -avoid-version statenotify_la_LDFLAGS = -module -avoid-version +internalkeyring_la_LDFLAGS = -module -avoid-version # this can't be in a conditional otherwise automake 1.4 yells dbus_example_la_LDFLAGS = -module -avoid-version @@ -57,7 +59,8 @@ offlinemsg.la \ psychic.la \ statenotify.la \ - $(DBUS_LTLIB) + $(DBUS_LTLIB) \ + internalkeyring.la noinst_LTLIBRARIES = \ ciphertest.la \ @@ -86,6 +89,7 @@ signals_test_la_SOURCES = signals-test.c simple_la_SOURCES = simple.c statenotify_la_SOURCES = statenotify.c +internalkeyring_la_SOURCES = internalkeyring.c autoaccept_la_LIBADD = $(GLIB_LIBS) buddynote_la_LIBADD = $(GLIB_LIBS) @@ -102,6 +106,7 @@ signals_test_la_LIBADD = $(GLIB_LIBS) simple_la_LIBADD = $(GLIB_LIBS) statenotify_la_LIBADD = $(GLIB_LIBS) +internalkeyring_la_LIBADD = $(GLIB_LIBS) if ENABLE_DBUS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/internalkeyring.c Mon Aug 11 00:49:06 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) +
--- a/libpurple/xmlnode.h Sun Aug 10 21:49:23 2008 +0000 +++ b/libpurple/xmlnode.h Mon Aug 11 00:49:06 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 Sun Aug 10 21:49:23 2008 +0000 +++ b/pidgin/gtkaccount.c Mon Aug 11 00:49:06 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 Sun Aug 10 21:49:23 2008 +0000 +++ b/pidgin/gtkprefs.c Mon Aug 11 00:49:06 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"); + 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++); }