Sat, 29 Aug 2009 00:45:37 +0000
propagate from branch 'im.pidgin.pidgin' (head 32da7ab6d1092d199f3c54e43644fb6303f82bd2)
to branch 'im.pidgin.soc.2008.masterpassword' (head e0ce5fb11bdba2ac134e9675ce4dffd3f43e0418)
--- a/COPYRIGHT Fri Aug 28 23:27:10 2009 +0000 +++ b/COPYRIGHT Sat Aug 29 00:45:37 2009 +0000 @@ -48,6 +48,7 @@ Dave Bell Igor Belyi Brian Bernas +Vivien Bernet-Rollande Paul Betts Jonas Birmé George-Cristian Bîrzan
--- a/configure.ac Fri Aug 28 23:27:10 2009 +0000 +++ b/configure.ac Sat Aug 29 00:45:37 2009 +0000 @@ -1311,6 +1311,7 @@ AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--disable-dbus], [disable D-Bus support])], , enable_dbus=yes) AC_ARG_ENABLE(nm, [AC_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes) +AC_ARG_ENABLE(gnome-keyring, [AC_HELP_STRING([--disable-gnome-keyring], [disable GNOME Keyring support (requires D-Bus)])], enable_gnome_keyring=$enableval, enable_gnome_keyring=yes) if test "x$enable_dbus" = "xyes" ; then AC_CHECK_PROG(enable_dbus, dbus-binding-tool, yes, no) @@ -1347,10 +1348,33 @@ ]) fi]) fi + + +dnl Check for gnome-keyring.h; if we don't have it, oh well + if test "x$enable_gnome_keyring" = "xyes" ; then + PKG_CHECK_MODULES(GNOMEKEYRING, [gnome-keyring-1], [ + AC_SUBST(GNOMEKEYRING_CFLAGS) + AC_SUBST(GNOMEKEYRING_LIBS) + AC_DEFINE(HAVE_GNOMEKEYRING, 1, [Define if we have GNOME Keyring.]) + ], [ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ +GNOME Keyring development headers not found. +Use --disable-gnome-keyring if you do not need GNOME Keyring support. +]) + fi]) + fi else + enable_gnome_keyring=no enable_nm=no fi +AM_CONDITIONAL(ENABLE_GNOMEKEYRING, test "x$enable_gnome_keyring" = "xyes") + + + + + dnl ####################################################################### dnl # Check for Python dnl ####################################################################### @@ -2496,6 +2520,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 @@ -2557,6 +2582,7 @@ fi echo Build with GNU Libidn......... : $enable_idn echo Build with NetworkManager..... : $enable_nm +echo Build with GNOME Keyring...... : $enable_gnome_keyring echo SSL Library/Libraries......... : $msg_ssl if test "x$SSL_CERTIFICATES_DIR" != "x" ; then eval eval echo SSL CA certificates directory. : $SSL_CERTIFICATES_DIR
--- a/finch/gntaccount.c Fri Aug 28 23:27:10 2009 +0000 +++ b/finch/gntaccount.c Sat Aug 29 00:45:37 2009 +0000 @@ -84,6 +84,10 @@ /* This is necessary to close an edit-dialog when an account is deleted */ static GList *accountdialogs; + +static void edit_account_continue(PurpleAccount * account, + gchar * password, GError * error, gpointer user_data); + static void account_add(PurpleAccount *account) { @@ -174,9 +178,9 @@ gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->remember))); value = gnt_entry_get_text(GNT_ENTRY(dialog->password)); if (value && *value) - purple_account_set_password(account, value); + purple_account_set_password_async(account, g_strdup(value), g_free, NULL, NULL); else - purple_account_set_password(account, NULL); + purple_account_set_password_async(account, NULL, NULL, NULL, NULL); /* Mail notification */ purple_account_set_check_mail(account, @@ -485,6 +489,13 @@ static void edit_account(PurpleAccount *account) { + purple_account_get_password_async(account, edit_account_continue, account); +} + +static void +edit_account_continue(PurpleAccount * account, + gchar * password, GError * error, gpointer user_data) +{ GntWidget *window, *hbox; GntWidget *combo, *button, *entry; GList *list, *iter; @@ -566,7 +577,7 @@ gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Password:"))); gnt_box_add_widget(GNT_BOX(hbox), entry); if (account) - gnt_entry_set_text(GNT_ENTRY(entry), purple_account_get_password(account)); + gnt_entry_set_text(GNT_ENTRY(entry), password); hbox = gnt_hbox_new(TRUE); gnt_box_set_pad(GNT_BOX(hbox), 0);
--- a/finch/gntprefs.c Fri Aug 28 23:27:10 2009 +0000 +++ b/finch/gntprefs.c Sat Aug 29 00:45:37 2009 +0000 @@ -1,6 +1,7 @@ /** * @file gntprefs.c GNT Preferences API * @ingroup finch + * @todo : add support for master password changing. */ /* finch @@ -81,6 +82,13 @@ } Prefs; static GList * +get_keyring_options(void) +{ + return purple_keyring_get_options(); +} + + +static GList * get_log_options(void) { return purple_log_logger_get_options(); @@ -203,6 +211,11 @@ {PURPLE_PREF_NONE, NULL, NULL, NULL}, }; +static Prefs keyring[] = +{ + {PURPLE_PREF_STRING, "/purple/keyring/active", N_("Active keyring"), purple_keyring_get_options} +}; + static Prefs idle[] = { {PURPLE_PREF_STRING, "/purple/away/idle_reporting", N_("Report Idle time"), get_idle_options}, @@ -258,6 +271,7 @@ add_pref_group(fields, _("Buddy List"), blist); add_pref_group(fields, _("Conversations"), convs); + add_pref_group(fields, _("Keyring"), keyring); add_pref_group(fields, _("Logging"), logging); add_pref_group(fields, _("Idle"), idle);
--- a/libpurple/Makefile.am Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/Makefile.am Sat Aug 29 00:45:37 2009 +0000 @@ -52,6 +52,7 @@ ft.c \ idle.c \ imgstore.c \ + keyring.c \ log.c \ media.c \ mediamanager.c \ @@ -115,6 +116,7 @@ gaim-compat.h \ idle.h \ imgstore.h \ + keyring.h \ log.h \ media.h \ media-gst.h \
--- a/libpurple/account.c Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/account.c Sat Aug 29 00:45:37 2009 +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,7 @@ #include "util.h" #include "xmlnode.h" + typedef struct { PurpleConnectionErrorInfo *current_error; @@ -78,6 +80,12 @@ guint ref; } PurpleAccountRequestInfo; +typedef struct +{ + gpointer cb; + gpointer data; +} CbInfo; + static PurpleAccountUiOps *account_ui_ops = NULL; static GList *accounts = NULL; @@ -89,6 +97,20 @@ 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(PurpleAccount * account, + gchar * password, GError * error, gpointer data); + +static void purple_account_get_password_async_finish(PurpleAccount * account, + char * password, GError * error, gpointer data); + +static void purple_accounts_delete_finish(PurpleAccount * account, GError * error, gpointer data); /********************************************************************* * Writing to disk * *********************************************************************/ @@ -194,8 +216,7 @@ gboolean boolean_value = purple_value_get_boolean(attr_value); if (boolean_value == purple_value_get_boolean(default_value)) return NULL; - value = g_strdup(boolean_value ? - "true" : "false"); + value = g_strdup(boolean_value ? "true" : "false"); } else { @@ -364,8 +385,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"); @@ -375,11 +401,34 @@ 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 (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(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"); + if (keyring_id != NULL) + xmlnode_set_attrib(child, "keyring_id", keyring_id); + if (mode != NULL) + xmlnode_set_attrib(child, "mode", mode); + if (data != NULL) + xmlnode_insert_data(child, data, -1); + + if (destroy != NULL) + destroy(data); + } } if ((tmp = purple_account_get_alias(account)) != NULL) @@ -455,6 +504,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); @@ -803,7 +854,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) @@ -831,15 +886,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)) @@ -912,6 +958,26 @@ 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); + } else { + purple_debug_info("accounts", "failed to imported password.\n"); + } + g_free(data); + } + return ret; } @@ -1076,24 +1142,62 @@ 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); + + _purple_connection_new(account, TRUE, password); +} + +typedef struct +{ + PurpleAccountUnregistrationCb cb; + void *user_data; +} UnregisterData; + void purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data) { + UnregisterData * 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_new0(UnregisterData, 1); + 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) +{ + UnregisterData * unregdata; + + g_return_if_fail(account != NULL); + + unregdata = data; + g_return_if_fail(unregdata != NULL); + + _purple_connection_new_unregister(account, password, unregdata->cb, unregdata->user_data); + + g_free(unregdata); } static void request_password_ok_cb(PurpleAccount *account, PurpleRequestFields *fields) { const char *entry; + char * password; gboolean remember; entry = purple_request_fields_get_string(fields, "password"); @@ -1105,12 +1209,16 @@ return; } - if(remember) - purple_account_set_remember_password(account, TRUE); - - purple_account_set_password(account, entry); - + if(!remember) + purple_keyring_set_password_async(account, NULL, NULL, NULL, NULL); + + purple_account_set_remember_password(account, remember); + + password = g_strdup(entry); + purple_account_set_password(account, password); + g_free(password); _purple_connection_new(account, FALSE, entry); + } static void @@ -1193,7 +1301,20 @@ purple_debug_info("account", "Connecting to account %s.\n", username); 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(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)) @@ -1219,10 +1340,8 @@ gc = purple_account_get_connection(account); _purple_connection_destroy(gc); - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_connection(account, NULL); - account->disconnecting = FALSE; } @@ -1556,14 +1675,52 @@ void purple_account_set_password(PurpleAccount *account, const char *password) { + schedule_accounts_save(); + g_return_if_fail(account != NULL); g_free(account->password); + account->password = g_strdup(password); + if (purple_account_get_remember_password(account) == TRUE) + purple_keyring_set_password_async(account, g_strdup(password), g_free, NULL, NULL); +} + +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. + */ schedule_accounts_save(); + + g_return_if_fail(account != NULL); + + g_free(account->password); + + account->password = g_strdup(password); + + 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); + } } - void purple_account_set_alias(PurpleAccount *account, const char *alias) { @@ -1960,13 +2117,92 @@ return account->username; } +#ifndef PURPLE_DISABLE_DEPRECATED +/* XXX will be replaced by the async code in 3.0 */ const char * -purple_account_get_password(const PurpleAccount *account) +purple_account_get_password(PurpleAccount *account) { g_return_val_if_fail(account != NULL, NULL); + if (account->password == NULL) { + purple_debug_info("accounts", + "Reading password for account %s (%s) from sync keyring.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + account->password = g_strdup(purple_keyring_get_password_sync(account)); + + } else { + purple_debug_info("accounts", + "Reading password for account %s (%s) from cached.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } + return account->password; } +#endif + +void +purple_account_get_password_async(PurpleAccount * account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + CbInfo * info = g_new0(CbInfo,1); + info->cb = cb; + info->data = data; + + if(account == NULL) { + cb(NULL, NULL, NULL, data); + return; + } + + if (account->password != NULL) { + purple_debug_info("accounts", + "Reading password for account %s (%s) from cached (async).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + cb(account, account->password, NULL, data); + + } else { + purple_debug_info("accounts", + "Reading password for account %s (%s) from async keyring.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + purple_keyring_get_password_async(account, + purple_account_get_password_async_finish, info); + } +} + +static void +purple_account_get_password_async_finish(PurpleAccount * account, + char * password, + GError * error, + gpointer data) +{ + CbInfo * info; + PurpleKeyringReadCallback cb; + + + + purple_debug_info("accounts", + "Read password for account %s (%s) from async keyring.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + info = data; + + g_return_if_fail(account != NULL); + g_free(account->password); + account->password = g_strdup(password); + g_return_if_fail(info != NULL); + + cb = info->cb; + if (cb != NULL) + cb(account, password, error, info->data); + + g_free(data); +} const char * purple_account_get_alias(const PurpleAccount *account) @@ -2412,7 +2648,7 @@ PurpleConnection *gc = purple_account_get_connection(account); PurplePlugin *prpl = NULL; - purple_account_set_password(account, new_pw); + purple_account_set_password_async(account, g_strdup(new_pw), g_free, NULL, NULL); if (gc != NULL) prpl = purple_connection_get_prpl(gc); @@ -2620,7 +2856,18 @@ /* This will cause the deletion of an old buddy icon. */ purple_buddy_icons_set_account_icon(account, NULL, 0); - purple_account_destroy(account); + /* this is async because we do not want the + * account overwritten before we are done. + */ + purple_keyring_set_password_async(account, NULL, NULL, + purple_accounts_delete_finish, NULL); + +} + +static void +purple_accounts_delete_finish(PurpleAccount * account, GError * error, gpointer data) +{ + purple_account_destroy(account); } void
--- a/libpurple/account.h Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/account.h Sat Aug 29 00:45:37 2009 +0000 @@ -46,6 +46,7 @@ #include "proxy.h" #include "prpl.h" #include "status.h" +#include "keyring.h" /** * Account request types. @@ -334,10 +335,33 @@ * * @param account The account. * @param password The password. + * + * This functions is just a wrapper for the async code anyway. */ void purple_account_set_password(PurpleAccount *account, const char *password); /** + * Set the account's password, and call the callback + * This should be renamed purple_account_set_password() when getting + * to 3.0. This calls the keyring function and syncs the accounts.xml + * + * The password in the keyring might not be immediatly updated, but the cache + * version will be, and it is therefore safe to read the password back before + * the callback has been triggered. One can also set a NULL calback. + * + * @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. @@ -566,14 +590,32 @@ */ const char *purple_account_get_username(const PurpleAccount *account); +#ifndef PURPLE_DISABLE_DEPRECATED /** * Returns the account's password. * * @param account The account. * * @return The password. + * + * @deprecated This might return NULL if the password has not been cached yet, + * and the keyring doesn't support sync access. It might also hang libpurple + * while the keyring is prompting for a password. Use purple_account_get_password_async() + * or purple_connection_get_password() instead (depending on the part of the code you are + * calling from. */ -const char *purple_account_get_password(const PurpleAccount *account); +const char *purple_account_get_password(PurpleAccount *account); +#endif + +/** + * 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 Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/connection.c Sat Aug 29 00:45:37 2009 +0000 @@ -458,12 +458,16 @@ return gc->prpl; } + +/** + * FIXME : all the calling tree needs to be async. + */ const char * purple_connection_get_password(const PurpleConnection *gc) { g_return_val_if_fail(gc != NULL, NULL); - return gc->password ? gc->password : gc->account->password; + return gc->password; } const char * @@ -481,6 +485,14 @@ return connection->proto_data; } +gboolean +purple_connection_had_error(const PurpleConnection *gc) +{ + g_return_val_if_fail(gc != NULL, FALSE); + + return gc->disconnect_timeout != 0; +} + void purple_connection_update_progress(PurpleConnection *gc, const char *text, size_t step, size_t count) @@ -517,18 +529,13 @@ { PurpleAccount *account; PurpleConnection *gc; - char *password; account = data; gc = purple_account_get_connection(account); gc->disconnect_timeout = 0; - password = g_strdup(purple_account_get_password(account)); purple_account_disconnect(account); - purple_account_set_password(account, password); - g_free(password); - return FALSE; }
--- a/libpurple/connection.h Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/connection.h Sat Aug 29 00:45:37 2009 +0000 @@ -146,6 +146,7 @@ #include <time.h> #include "account.h" +#include "keyring.h" #include "plugin.h" #include "status.h" #include "sslconn.h" @@ -416,7 +417,8 @@ PurplePlugin * purple_connection_get_prpl(const PurpleConnection *gc); /** - * Returns the connection's password. + * Returns the connection's password. Deprecated, use + * purple_connection_get_password_async() instead. * * @param gc The connection. * @@ -445,6 +447,15 @@ void *purple_connection_get_protocol_data(const PurpleConnection *connection); /** + * Returns if the connection had an error + * + * @param gc The connection. + * + * @return TRUE if the connection had an error, FALSE otherwise + */ +gboolean purple_connection_had_error(const PurpleConnection *gc); + +/** * Updates the connection progress. * * @param gc The connection.
--- a/libpurple/core.c Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/core.c Sat Aug 29 00:45:37 2009 +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" @@ -134,6 +135,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.
--- a/libpurple/example/nullclient.c Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/example/nullclient.c Sat Aug 29 00:45:37 2009 +0000 @@ -295,7 +295,7 @@ /* Get the password for the account */ password = getpass("Password: "); - purple_account_set_password(account, password); + purple_account_set_password_async(account, g_strdup(password), g_free, NULL, NULL); /* It's necessary to enable the account first. */ purple_account_set_enabled(account, UI_ID, TRUE);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/keyring.c Sat Aug 29 00:45:37 2009 +0000 @@ -0,0 +1,1176 @@ +/** + * @file keyring.c Keyring plugin API + * @ingroup core + * @todo ? : add dummy callback to all calls to prevent poorly written + * plugins from segfaulting on NULL callback ? + */ + +/* 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" +#include "internal.h" + +typedef struct _PurpleKeyringCbInfo PurpleKeyringCbInfo; +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); +static void purple_keyring_set_password_async_cb(PurpleAccount * account, GError * error, gpointer data); + + +/******************************************/ +/** @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; +}; + +struct _PurpleKeyringCbInfo +{ + gpointer cb; + gpointer data; +}; + +/* 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 */ +/***************************************/ +/*@{*/ + +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 */ +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) { + + 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"); + + purple_notify_error(NULL, _("Keyrings"), _("Failed to change the keyring."), + _("Aborting changes.")); + purple_keyring_inuse = tracker->old; + 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_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; + + 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(); + + purple_keyring_inuse = newkeyring; + + 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); + + 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); + 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) { + 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, + const char * keyringid, + const char * mode, + const 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(PurpleAccount * account, + gchar * password, + GDestroyNotify destroypassword, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + GError * error = NULL; + const PurpleKeyring * inuse; + PurpleKeyringSave save; + PurpleKeyringCbInfo * cbinfo; + + g_return_if_fail(account != NULL); + + 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 { + cbinfo = g_malloc(sizeof(PurpleKeyringCbInfo)); + cbinfo->cb = cb; + cbinfo->data = data; + save(account, password, destroypassword, + purple_keyring_set_password_async_cb, data); + } + } + return; +} + +static void +purple_keyring_set_password_async_cb(PurpleAccount * account, + GError * error, + gpointer data) +{ + PurpleKeyringCbInfo * cbinfo; + PurpleKeyringSaveCallback cb; + + g_return_if_fail(data != NULL); + g_return_if_fail(account != NULL); + + cbinfo = data; + cb = cbinfo->cb; + + if (error != NULL) { + purple_notify_error(NULL, _("Keyrings"), + _("Failed to save password in keyring."), + error->message); + } + + if (cb != NULL) + cb(account, error, cbinfo->data); + g_free(data); +} + +/** + * 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; + + g_return_val_if_fail(account != NULL, NULL); + + purple_debug_info("keyring (_sync_)", + "Reading password for account %s (%s)", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + 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; + + purple_debug_info("keyring (_sync_)", + "Setting password for account %s (%s)", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + g_return_if_fail(account != NULL); + + inuse = purple_keyring_get_inuse(); + if (inuse != NULL) { + save = purple_keyring_get_save_sync(inuse); + + if (save != NULL) + return save(account, password); + } + + /* schedule account save */ + purple_account_set_password(NULL, NULL); + + 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."); + if (cb) + 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."); + if (cb) + 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 Sat Aug 29 00:45:37 2009 +0000 @@ -0,0 +1,476 @@ +/** + * @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)(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)(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, + const char * mode, + const 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); + + +/*@}*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/***************************************/ +/** @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, + const char * keyringid, + const char * mode, + const 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); + +/** + * 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(PurpleAccount * account, + gchar * password, + GDestroyNotify destroypassword, + PurpleKeyringSaveCallback cb, + gpointer data); +#ifndef PURPLE_DISABLE_DEPRECATED +/** + * Read a password in a synchronous way. + * + * @param account The account for which we want the password. + * + * @return A pointer to the the password + * + * @deprecated This is here only for compatibility reasons. Keyrings + * are not expected to support this, and you should use + * purple_keyring_get_password_async() instead. + */ +const char * +purple_keyring_get_password_sync(const PurpleAccount * account); +#endif + +#ifndef PURPLE_DISABLE_DEPRECATED +/** + * Save a password in a synchronous way. + * + * @param account The account for which we want the password. + * @param password The password to save. + * + * @deprecated This is here only for compatibility reasons. Keyrings are not + * expected to support this, and you should use + * purple_keyring_set_password_async() instead. + */ +void +purple_keyring_set_password_sync(PurpleAccount * account, + const char *password); +#endif + +/** + * 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 */ +}; + +#ifdef __cplusplus +} +#endif +/*}@*/ +#endif /* _PURPLE_KEYRING_H_ */
--- a/libpurple/plugin.c Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/plugin.c Sat Aug 29 00:45:37 2009 +0000 @@ -492,6 +492,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; @@ -1444,7 +1450,7 @@ } protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin, - (GCompareFunc)compare_prpl); + (GCompareFunc)compare_prpl); } }
--- a/libpurple/plugin.h Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/plugin.h Sat Aug 29 00:45:37 2009 +0000 @@ -67,6 +67,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 Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/plugins/Makefile.am Sat Aug 29 00:45:37 2009 +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/keyrings/Makefile.am Sat Aug 29 00:45:37 2009 +0000 @@ -0,0 +1,54 @@ +EXTRA_DIST = \ + Makefile.mingw + +plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) + + +if ENABLE_GNOMEKEYRING + +gnomekeyring_la_LDFLAGS = -module -avoid-version +GKRSOURCES = gnomekeyring.c +#GNOME_KEYRING_CFLAGS = -I/usr/include/gnome-keyring-1 +#GNOME_KEYRING_LIBS = -lgnome-keyring +GNOME_KEYRING_LA = gnomekeyring.la + +endif #ENABLE_GNOMEKEYRING + +internalkeyring_la_LDFLAGS = -module -avoid-version +IKSOURCES = internalkeyring.c + +if PLUGINS + +plugin_LTLIBRARIES = \ + $(GNOME_KEYRING_LA)\ + internalkeyring.la + + +if ENABLE_GNOMEKEYRING + +gnomekeyring_la_SOURCES = $(GKRSOURCES) +gnomekeyring_la_LIBADD = $(GLIB_LIBS) $(GNOMEKEYRING_LIBS) + +endif #ENABLE_GNOMEKEYRING + +internalkeyring_la_SOURCES = $(IKSOURCES) +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) + +if ENABLE_GNOMEKEYRING +gnomekeyring_la_CFLAGS = $(AM_CPPFLAGS) $(GNOMEKEYRING_CFLAGS) +endif #ENABLE_GNOMEKEYRING + +internalkeyring_la_CFLAGS = $(AM_CPPFLAGS) + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/gnomekeyring.c Sat Aug 29 00:45:37 2009 +0000 @@ -0,0 +1,525 @@ +/** + * @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" +#include "internal.h" + +#define GNOMEKEYRING_NAME N_("Gnome-Keyring") +#define GNOMEKEYRING_VERSION "0.3b" +#define GNOMEKEYRING_DESCRIPTION N_("This plugin will store passwords in Gnome-Keyring.") +#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; + char * name; +}; + +/* 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 *, const char *, const 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 *); + +static void gkp_change_master(PurpleKeyringChangeMasterCallback cb, gpointer data); + +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; + char * copy; + + 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) { + copy = g_strdup(password); + cb(account, copy, NULL, storage->user_data); + g_free(copy); + } + return; + } +} + + +static void +gkp_save(PurpleAccount * account, + gchar * password, + GDestroyNotify destroy, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + InfoStorage * storage = g_new0(InfoStorage,1); + + storage->account = account; + storage->cb = cb; + storage->user_data = data; + storage->name = g_strdup_printf("pidgin-%s", + purple_account_get_username(account)); + + if(password != NULL && *password != '\0') { + + 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 */ + storage->name, + 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; + PurpleKeyringSaveCallback cb; + GError * error; + PurpleAccount * account; + + storage = data; + g_return_if_fail(storage != NULL); + + cb = storage->cb; + account = storage->account; + + g_free(storage->name); + + 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; + + purple_debug_info("Gnome-Keyring plugin (_sync_)", "password for %s was read.\n", + purple_account_get_username(account)); + + 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) +{ + const char * name; + + if(password != NULL && *password != '\0') { + + name =g_strdup_printf("pidgin-%s", purple_account_get_username(account)), + + gnome_keyring_store_password_sync(GNOME_KEYRING_NETWORK_PASSWORD, + NULL, name, password, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + purple_debug_info("GnomeKeyring plugin (_sync_)", + "Updated password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + } else { + gnome_keyring_delete_password_sync(GNOME_KEYRING_NETWORK_PASSWORD, + "user", purple_account_get_username(account), + "protocol", purple_account_get_protocol_id(account), + NULL); + purple_debug_info("GnomeKeyring plugin (_sync_)", + "Deleted password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + } +} + +static void +gkp_close(GError ** error) +{ + return; +} + +static gboolean +gkp_import_password(PurpleAccount * account, + const char * mode, + const 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; +} + +/* this was written just to test the pref change */ +static void +gkp_change_master(PurpleKeyringChangeMasterCallback cb, gpointer data) +{ + purple_debug_info("Gnome-Keyring plugin", + "This keyring does not support master passwords.\n"); + + purple_notify_info(NULL, _("Gnome-Keyring plugin"), + _("Failed to change master password."), + _("This plugin does not really support master passwords, it just pretends to.")); + if(cb) + cb(FALSE, NULL, data); +} + +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, gkp_change_master); + 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("Gnome Keyring plugin", "init plugin called.\n"); +} + +PURPLE_INIT_PLUGIN(gnome_keyring, init_plugin, plugininfo) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/internalkeyring.c Sat Aug 29 00:45:37 2009 +0000 @@ -0,0 +1,370 @@ +/** + * @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" +#include "internal.h" + +#define INTERNALKEYRING_NAME N_("Internal keyring") +#define INTERNALKEYRING_VERSION "0.8b" +#define INTERNALKEYRING_DESCRIPTION N_("This plugin provides the default password storage behaviour for libpurple. Password will be stored unencrypted.") +#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 *, const char *, const 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 || *password == '\0') { + 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 && password) + 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 || *password == '\0') { + 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, + const char * mode, + const 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) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/kwallet.cpp Sat Aug 29 00:45:37 2009 +0000 @@ -0,0 +1,422 @@ +/** + * @file kwallet.cpp KWallet 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> + +#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" +#include "internal.h" + + +static GQuark kwallet_plugin_error_domain(void); + +static void kwallet_read(PurpleAccount * account, PurpleKeyringReadCallback cb, gpointer data); +static void kwallet_save(PurpleAccount * account, gchar * password, GDestroyNotify destroypassword, PurpleKeyringSaveCallback cb, gpointer data); +static void kwallet_close(GError ** error); +static void kwallet_import(PurpleAccount * account, const char * mode, const char * data, GError ** error); +static void kwallet_export(PurpleAccount * account, const char ** mode, char ** data, GError ** error, GDestroyNotify * destroy); + +static gboolean kwallet_load(PurplePlugin *plugin); +static gboolean kwallet_unload(PurplePlugin *plugin); +static void kwallet_destroy(PurplePlugin *plugin); +static void init_plugin(PurplePlugin *plugin) + +PurpleKeyring * keyring_handler; + +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 */ + kwallet_load, /* load */ + kwallet_unload, /* unload */ + kwallet_destroy, /* destroy */ + NULL, /* ui_info */ + NULL, /* extra_info */ + NULL, /* prefs_info */ + NULL, /* actions */ + NULL, /* padding... */ + NULL, + NULL, + NULL, +}; + + +extern "C" +{ +PURPLE_INIT_PLUGIN(kwallet_keyring, init_plugin, plugininfo) +} + + + + +#define ERR_KWALLETPLUGIN kwallet_plugin_error_domain() + + + +class KWalletPlugin::engine +{ + public : + engine(); + ~engine(); + void queue(request req); + static engine * Instance(); + + signal : + void walletopened(bool opened); + + private : + bool connected; + KWallet::wallet * wallet; + std::list<request> requests; + static engine * pinstance; + + KApplication *app; + void ExecuteRequests(); +}; + +KWalletPlugin::engine::engine() +{ + KAboutData aboutData("libpurple_plugin", N_("LibPurple KWallet Plugin"), "", "", KAboutData::License_GPL, ""); + KCmdLineArgs::init( &aboutData ); + app = new KApplication(false, false); + + connected = FALSE; + wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, Asynchronous); + QObject::connect(wallet, SIGNAL(KWallet::Wallet::walletOpened(bool)), SLOT(walletopened(bool))); +} + +KWalletPlugin::engine::~engine() +{ + <list<request>>::iterator it; + + for(it = requests.begin(); it != requests.end ; it++) { + it->abort(); + requests.pop_front(); + } + + KWallet::closeWallet(KWallet::Wallet::NetworkWallet(), TRUE) + delete wallet; + pinstance = NULL; +} + +engine * +KWalletPlugin::engine::Instance() +{ + if (pinstance == NULL) + pinstance = new engine; + return pinstance; +} + +void +KWalletPlugin::engine::walletopened(bool opened) +{ + connected = opened; + + if(opened) { + ExecuteRequests(); + } else { + for(it = requests.begin(); it != requests.end ; it++) { + it->abort(); + requests.pop_front(); + } + delete this; + } +} + +void +KWalletPlugin::engine::queue(request req) +{ + requests.push_back(req); + ExecuteRequests(); +} + +void +KWalletPlugin::engine::ExecuteRequests() +{ + if(connected) + for(it = requests.begin(); it != requests.end() ; it++) { + it->execute(); + requests.pop_front(); + delete it; + } +} + +class KWalletPlugin::request +{ + public : + virtual void abort(); + virtual void execute(KWallet::wallet * wallet); + + private : + gpointer data; + PurpleAccount * account + QString password; +} + +class KWalletPlugin::save_request : public request +{ + public : + request(PurpleAccount * account, char * password, void * cb, void * data); + void abort(); + void execute(KWallet::wallet * wallet); + + private : + PurpleKeyringReadCallback callback; +} + +class KWalletPlugin::read_request : public request +{ + public : + request(PurpleAccount * account, void * cb, void * data); + void abort(); + void execute(KWallet::wallet * wallet); + + private : + PurpleKeyringSaveCallback callback; +} + + + +KWalletPlugin::save_request::save_request(PurpleAccount * acc, char * pw, void * cb, void * userdata) +{ + account = acc; + data = userdata; + callback = cb; + password = pw; +} + +KWalletPlugin::read_request::read_request(PurpleAccount * acc, void * cb, void * userdata) +{ + account = acc; + data = userdata; + callback = cb; + password = NULL; +} + +void +KWalletPlugin::save_request::abort() +{ + GError * error; + if (cb != NULL) { + error = g_error_new(ERR_KWALLETPLUGIN, + ERR_UNKNOWN, + "Failed to save password"); + cb(account, error, data); + g_error_free(error); + } +} + +void +KWalletPlugin::read_request::abort() +{ + GError * error; + if (callback != NULL) { + error = g_error_new(ERR_KWALLETPLUGIN, + ERR_UNKNOWN, + "Failed to save password"); + cb(account, NULL, error, data); + g_error_free(error); + } +} + +void +KWalletPlugin::read_request::execute(KWallet::wallet * wallet) +{ + int result; + GString key; + + key = "purple-" + purple_account_get_username(account) + " " + purple_account_get_protocol_id(account); + result = wallet.readPassword(key, password); + + if(result != 0) + abort(); + else + if (callback != NULL) + callback(account, (const char *)password, NULL, data); +} + +void +KWalletPlugin::save_request::execute(KWallet::wallet * wallet) +{ + int result; + GString key; + + key = "purple-" + purple_account_get_username(account) + " " + purple_account_get_protocol_id(account); + result = wallet.writePassword(key, password); + + if(result != 0) + abort(); + else + if (callback != NULL) + callback(account, (const char *)password, NULL, data); +} + + + + + + + + + + +void +kwallet_read(PurpleAccount * account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + KWalletPlugin::read_request req(account, cb, data); + KWalletPlugin::engine::instance()->queue(req); +} + + +void +kwallet_save(PurpleAccount * account, + char * password, + GDestroyNotify destroypassword, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + KWalletPlugin::read_request req(account, password, cb, data); + KWalletPlugin::engine::instance()->queue(req); + if (destroypassword) + destroypassword(password); +} + + +void +kwallet_close(GError ** error) +{ + delete KWalletPlugin::engine::instance(); +} + +void +kwallet_import(PurpleAccount * account, + const char * mode, + const char * data, + GError ** error) +{ + return TRUE; +} + +void +kwallet_export(PurpleAccount * account, + const char ** mode, + char ** data, + GError ** error, + GDestroyNotify * destroy) +{ + *mode = NULL; + *data = NULL; + destroy = NULL; +} + +gboolean +kwallet_load(PurplePlugin *plugin) +{ + 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, gkp_change_master); + 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; +} + +gboolean +kwallet_unload(PurplePlugin *plugin) +{ + kwallet_close(); + return TRUE; +} + +void +gkp_destroy(PurplePlugin *plugin) +{ + kwallet_close(); + return; +} + +void +init_plugin(PurplePlugin *plugin) +{ + purple_debug_info("KWallet plugin", "init plugin called.\n"); +} + +GQuark kwallet_plugin_error_domain(void) +{ + return g_quark_from_static_string("KWallet keyring"); +} + + + + +
--- a/libpurple/protocols/oscar/flap_connection.c Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Sat Aug 29 00:45:37 2009 +0000 @@ -20,6 +20,7 @@ #include "oscar.h" +#include "account.h" #include "eventloop.h" #include "proxy.h"
--- a/libpurple/protocols/qq/qq_network.c Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/protocols/qq/qq_network.c Sat Aug 29 00:45:37 2009 +0000 @@ -732,7 +732,7 @@ #endif /* now generate md5 processed passwd */ - passwd = purple_account_get_password(purple_connection_get_account(gc)); + passwd = purple_connection_get_password(gc); /* use twice-md5 of user password as session key since QQ 2003iii */ dest = qd->ld.pwd_md5;
--- a/libpurple/prpl.c Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/prpl.c Sat Aug 29 00:45:37 2009 +0000 @@ -343,11 +343,17 @@ if (!purple_status_is_online(new_status)) { + /* Clear out the unsaved password if the disconnect was initiated + by the user */ + if (!purple_account_get_remember_password(account)) { + PurpleConnection *gc = purple_account_get_connection(account); + if (gc && purple_connection_had_error(gc)) + purple_account_set_password_async(account, NULL, NULL, NULL, NULL); + } + if (!purple_account_is_disconnected(account)) purple_account_disconnect(account); - /* Clear out the unsaved password if we're already disconnected and we switch to offline status */ - else if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + return; }
--- a/libpurple/xmlnode.h Fri Aug 28 23:27:10 2009 +0000 +++ b/libpurple/xmlnode.h Sat Aug 29 00:45:37 2009 +0000 @@ -163,7 +163,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 * * @deprecated Use xmlnode_set_attrib_full instead.
--- a/pidgin/gtkaccount.c Fri Aug 28 23:27:10 2009 +0000 +++ b/pidgin/gtkaccount.c Sat Aug 29 00:45:37 2009 +0000 @@ -115,6 +115,7 @@ GtkWidget *login_frame; GtkWidget *protocol_menu; GtkWidget *password_box; + char *password; GtkWidget *username_entry; GtkWidget *password_entry; GtkWidget *alias_entry; @@ -145,6 +146,7 @@ } AccountPrefsDialog; + static AccountsWindow *accounts_window = NULL; static GHashTable *account_pref_wins; @@ -155,6 +157,7 @@ /************************************************************************** * Add/Modify Account dialog **************************************************************************/ +static void pidgin_account_dialog_show_continue(PurpleAccount * account, char * password, GError * error, gpointer data); static void add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent); static void add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent); static void add_protocol_options(AccountPrefsDialog *dialog); @@ -584,10 +587,10 @@ /* Set the fields. */ if (dialog->account != NULL) { - if (purple_account_get_password(dialog->account) && + if (dialog->password && purple_account_get_remember_password(dialog->account)) gtk_entry_set_text(GTK_ENTRY(dialog->password_entry), - purple_account_get_password(dialog->account)); + dialog->password); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(dialog->remember_pass_check), @@ -1187,6 +1190,7 @@ char *tmp; gboolean new_acct = FALSE, icon_change = FALSE; PurpleAccount *account; + gboolean remember; /* Build the username string. */ username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->username_entry))); @@ -1292,9 +1296,11 @@ /* Remember Password */ - purple_account_set_remember_password(account, - gtk_toggle_button_get_active( - GTK_TOGGLE_BUTTON(dialog->remember_pass_check))); + remember = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->remember_pass_check)); + if(!remember) + purple_keyring_set_password_async(account, NULL, NULL, NULL, NULL); + + purple_account_set_remember_password(account, remember); /* Check Mail */ if (dialog->prpl_info && dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK) @@ -1302,6 +1308,7 @@ gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(dialog->new_mail_check))); + /* Password */ value = gtk_entry_get_text(GTK_ENTRY(dialog->password_entry)); @@ -1311,10 +1318,11 @@ * the account editor (but has not checked the 'save' box), then we * don't want to prompt them. */ - if ((purple_account_get_remember_password(account) || new_acct) && (*value != '\0')) + if ((purple_account_get_remember_password(account) || new_acct) && (*value != '\0')) { purple_account_set_password(account, value); - else + } else { purple_account_set_password(account, NULL); + } purple_account_set_username(account, username); g_free(username); @@ -1446,10 +1454,23 @@ {"STRING", 0, 2} }; + void pidgin_account_dialog_show(PidginAccountDialogType type, - PurpleAccount *account) + PurpleAccount *account) { + /* this is to make sure the password will be cached */ + purple_account_get_password_async(account, + pidgin_account_dialog_show_continue, (void*)type); +} + +static void +pidgin_account_dialog_show_continue(PurpleAccount * account, + char * password, + GError * error, + gpointer data) +{ + PidginAccountDialogType type = (PidginAccountDialogType)data; AccountPrefsDialog *dialog; GtkWidget *win; GtkWidget *main_vbox; @@ -1473,6 +1494,7 @@ } dialog->account = account; + dialog->password = g_strdup(password); dialog->type = type; dialog->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); @@ -1513,6 +1535,7 @@ /* Setup the top frames. */ add_login_options(dialog, vbox); + add_user_options(dialog, vbox); button = gtk_check_button_new_with_mnemonic( @@ -2066,7 +2089,7 @@ if (global_buddyicon != NULL) g_object_unref(G_OBJECT(global_buddyicon)); - return ret; + return ret; } #if !GTK_CHECK_VERSION(2,2,0)
--- a/pidgin/gtkconn.c Fri Aug 28 23:27:10 2009 +0000 +++ b/pidgin/gtkconn.c Sat Aug 29 00:45:37 2009 +0000 @@ -208,10 +208,7 @@ while (l) { PurpleAccount *a = (PurpleAccount*)l->data; if (!purple_account_is_disconnected(a)) { - char *password = g_strdup(purple_account_get_password(a)); purple_account_disconnect(a); - purple_account_set_password(a, password); - g_free(password); } l = l->next; }
--- a/pidgin/gtkprefs.c Fri Aug 28 23:27:10 2009 +0000 +++ b/pidgin/gtkprefs.c Sat Aug 29 00:45:37 2009 +0000 @@ -42,6 +42,7 @@ #include "upnp.h" #include "util.h" #include "network.h" +#include "keyring.h" #include "gtkblist.h" #include "gtkconv.h" @@ -2242,6 +2243,80 @@ return ret; } +static void +change_master_password_cb(GtkWidget * button, gpointer ptr) +{ + purple_keyring_change_master(NULL, NULL); +} + +static void +keyring_page_pref_changed(const char *name, PurplePrefType type, gconstpointer val, gpointer data) +{ + GtkWidget * button = data; + PurpleKeyring * keyring; + + g_return_if_fail(type == PURPLE_PREF_STRING); + g_return_if_fail(g_strcmp0(name,"/purple/keyring/active") == 0); + + /** + * This part is annoying. + * Since we do not know if purple_keyring_inuse was changed yet, + * as we do not know the order the callbacks are called in, we + * have to rely on the prefs system, and find the keyring that + * is (or will be) used, from there. + */ + + keyring = purple_keyring_get_keyring_by_id(val); + + if (purple_keyring_get_change_master(keyring)) + gtk_widget_set_sensitive(button,TRUE); + else + gtk_widget_set_sensitive(button,FALSE); +} + + +static GtkWidget * +keyring_page(void) +{ + GtkWidget *ret; + GtkWidget *vbox; + GtkWidget * button; + GList *names; + void * prefs; + const char * keyring_id; + PurpleKeyring * keyring; + + keyring_id = purple_prefs_get_string("/purple/keyring/active"); + keyring = purple_keyring_get_keyring_by_id(keyring_id); + + prefs = purple_prefs_get_handle(); + + ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); + gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER); + + /* Keyring selection */ + 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); + + /* Change master password */ + button = gtk_button_new_with_mnemonic(_("_Change master password.")); + + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(change_master_password_cb), NULL); + purple_prefs_connect_callback (prefs, "/purple/keyring/active", keyring_page_pref_changed, button); + + if (purple_keyring_get_change_master(keyring)) + gtk_widget_set_sensitive(button,TRUE); + else + gtk_widget_set_sensitive(button,FALSE); + + gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 1); + gtk_widget_show_all(ret); + return ret; +} + #ifndef _WIN32 static gint sound_cmd_yeah(GtkEntry *entry, gpointer d) @@ -2806,6 +2881,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++); }