Fri, 18 Nov 2011 19:54:07 +0000
propagate from branch 'im.pidgin.pidgin' (head 78b626b30c43634f501b1eb61233cef2735ddf6a)
to branch 'im.pidgin.soc.2008.masterpassword' (head 5450e39dd3c5846f7906d6edc6ae29a3dc7ba202)
--- a/COPYRIGHT Fri Nov 18 19:53:46 2011 +0000 +++ b/COPYRIGHT Fri Nov 18 19:54:07 2011 +0000 @@ -52,6 +52,7 @@ Igor Belyi David Benjamin Brian Bernas +Vivien Bernet-Rollande Paul Betts Runa Bhattacharjee Jonas Birmé
--- a/configure.ac Fri Nov 18 19:53:46 2011 +0000 +++ b/configure.ac Fri Nov 18 19:54:07 2011 +0000 @@ -110,6 +110,7 @@ dnl Checks for programs. AC_PROG_CC AM_PROG_CC_C_O +AC_PROG_CXX AC_DISABLE_STATIC AC_PROG_LIBTOOL LIBTOOL="$LIBTOOL --silent" @@ -1338,6 +1339,108 @@ fi dnl ####################################################################### +dnl # Check for GNOME Keyring headers +dnl ####################################################################### + +AC_ARG_ENABLE(gnome-keyring, [AC_HELP_STRING([--disable-gnome-keyring], [disable GNOME Keyring support])], enable_gnome_keyring=$enableval, enable_gnome_keyring=yes) + +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 + +AM_CONDITIONAL(ENABLE_GNOMEKEYRING, test "x$enable_gnome_keyring" = "xyes") + +dnl ####################################################################### +dnl # Check for KWallet headers +dnl ####################################################################### + +AC_ARG_ENABLE(kwallet, [AC_HELP_STRING([--enable-kwallet], [enable KWallet support])], enable_kwallet=$enableval, enable_kwallet=no) +AC_ARG_WITH(kwallet-includes, [AC_HELP_STRING([--with-kwallet-includes=DIR], [compile the KWallet plugin against includes in DIR])], [ac_kwallet_includes="$withval"], [ac_kwallet_includes="no"]) +AC_ARG_WITH(kwallet-libs, [AC_HELP_STRING([--with-kwallet-libs=DIR], [compile the KWallet plugin against the KWallet libs in DIR])], [ac_kwallet_libs="$withval"], [ac_kwallet_libs="no"]) +AC_ARG_WITH(qt4-libs, [AC_HELP_STRING([--with-qt4-libs=DIR], [compile the KWallet plugin against the Qt4 libs in DIR])], [ac_qt4_libs="$withval"], [ac_qt4_libs="no"]) + +if test "x$enable_kwallet" = "xyes"; then + KWALLET_CXXFLAGS="" + KWALLET_LIBS="" + if test -z "$with_kwallet_includes" || test -z "$with_kwallet_libs"; then + AC_CHECK_PROG(KDE4_CONFIG, kde4-config, kde4-config, no) + if test "x$KDE4_CONFIG" = "xno"; then + if test "x$force_deps" = "xyes"; then + AC_MSG_ERROR([ +kde4-config not found. $KDE4_CONFIG +Use --disable-kwallet if you do not need KWallet support. +]) + fi + fi + fi + + AC_LANG_PUSH([C++]) + CPPFLAGS_save="$CPPFLAGS" + LDFLAGS_save="$LDFLAGS" + + dnl Check for kwallet.h; if we don't have it, oh well + if test "$ac_kwallet_includes" != "no"; then + KWALLET_CXXFLAGS="-I$ac_kwallet_includes" + elif test "x$KDE4_CONFIG" != "xno"; then + KWALLET_CXXFLAGS="-I`$KDE4_CONFIG --path include`" + fi + CPPFLAGS="$CPPFLAGS $KWALLET_CXXFLAGS" + AC_CHECK_HEADER([kwallet.h], [true], [if test "x$force_deps" = "xyes"; then +AC_MSG_ERROR([ +KWallet development headers not found. +Use --disable-kwallet if you do not need KWallet support. +]) +fi +]) + + AC_MSG_CHECKING([for KWallet libraries]) + if test "$ac_kwallet_libs" != "no"; then + KWALLET_LIBS="-L$ac_kwallet_libs -lkdeui" + elif test "x$KDE4_CONFIG" != "xno"; then + KWALLET_LIBS="-L`$KDE4_CONFIG --install lib`/kde4/devel -lkdeui" + else + KWALLET_LIBS="-lkdeui" + fi + if test "$ac_qt4_libs" != "no"; then + KWALLET_LIBS="$KWALLET_LIBS -L$ac_qt4_libs -lQtCore" + elif test "x$KDE_CONFIG" != "xno"; then + KWALLET_LIBS="$KWALLET_LIBS -L`$KDE4_CONFIG --qt-libraries` -lQtCore" + else + KWALLET_LIBS="$KWALLET_LIBS -lQtCore" + fi + LDFLAGS="$LDFLAGS $KWALLET_LIBS" + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <kwallet.h>], + [KWallet::Wallet::LocalWallet();])], [AC_MSG_RESULT([yes])], + [if test "x$force_deps" = "xyes"; then +AC_MSG_ERROR([ +KWallet development libraries not found. +Use --disable-kwallet if you do not need KWallet support. +]) +fi +]) + + CPPFLAGS="$CPPFLAGS_save" + LDFLAGS="$LDFLAGS_save" + AC_LANG_POP +fi + +AC_SUBST(KWALLET_LIBS) +AC_SUBST(KWALLET_CXXFLAGS) + +AM_CONDITIONAL(ENABLE_KWALLET, test "x$enable_kwallet" = "xyes") + +dnl ####################################################################### dnl # Check for Python dnl ####################################################################### @@ -2511,6 +2614,7 @@ libpurple/purple-3.pc libpurple/purple-3-uninstalled.pc libpurple/plugins/Makefile + libpurple/plugins/keyrings/Makefile libpurple/plugins/mono/Makefile libpurple/plugins/mono/api/Makefile libpurple/plugins/mono/loader/Makefile @@ -2571,6 +2675,8 @@ fi echo Build with GNU Libidn......... : $enable_idn echo Build with NetworkManager..... : $enable_nm +echo Build with GNOME Keyring...... : $enable_gnome_keyring +echo Build with KWallet............ : $enable_kwallet 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 Nov 18 19:53:46 2011 +0000 +++ b/finch/gntaccount.c Fri Nov 18 19:54:07 2011 +0000 @@ -195,9 +195,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(account, value, NULL, NULL); else - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); /* Mail notification */ purple_account_set_check_mail(account, @@ -534,7 +534,8 @@ } static void -edit_account(PurpleAccount *account) +edit_account_continue(PurpleAccount *account, + const gchar *password, GError *error, gpointer user_data) { GntWidget *window, *hbox; GntWidget *combo, *button, *entry; @@ -617,7 +618,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); @@ -667,6 +668,12 @@ } static void +edit_account(PurpleAccount *account) +{ + purple_account_get_password(account, edit_account_continue, account); +} + +static void add_account_cb(GntWidget *widget, gpointer null) { edit_account(NULL);
--- a/finch/gntprefs.c Fri Nov 18 19:53:46 2011 +0000 +++ b/finch/gntprefs.c Fri Nov 18 19:54:07 2011 +0000 @@ -1,6 +1,7 @@ /** * @file gntprefs.c GNT Preferences API * @ingroup finch + * @todo : add support for master password changing. */ /* finch @@ -73,6 +74,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(); @@ -195,6 +203,11 @@ {PURPLE_PREF_NONE, NULL, NULL, NULL}, }; +static Prefs keyring[] = +{ + {PURPLE_PREF_STRING, "/purple/keyring/active", N_("Active keyring"), get_keyring_options} +}; + static Prefs idle[] = { {PURPLE_PREF_STRING, "/purple/away/idle_reporting", N_("Report Idle time"), get_idle_options}, @@ -250,6 +263,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 Nov 18 19:53:46 2011 +0000 +++ b/libpurple/Makefile.am Fri Nov 18 19:54:07 2011 +0000 @@ -52,6 +52,7 @@ ft.c \ idle.c \ imgstore.c \ + keyring.c \ log.c \ media/backend-fs2.c \ media/backend-iface.c \ @@ -119,6 +120,7 @@ ft.h \ idle.h \ imgstore.h \ + keyring.h \ log.h \ media.h \ media-gst.h \
--- a/libpurple/account.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/account.c Fri Nov 18 19:54:07 2011 +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" @@ -70,6 +71,12 @@ guint ref; } PurpleAccountRequestInfo; +typedef struct +{ + PurpleCallback cb; + gpointer data; +} CbInfo; + static PurpleAccountUiOps *account_ui_ops = NULL; static GList *accounts = NULL; @@ -186,8 +193,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 { @@ -355,8 +361,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"); @@ -366,11 +377,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("account", "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("account", + "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) @@ -840,7 +874,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) @@ -868,15 +906,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)) @@ -940,6 +969,25 @@ 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("account", "Password imported successfully.\n"); + purple_account_set_remember_password(ret, TRUE); + } else { + purple_debug_info("account", "Failed to import password.\n"); + } + g_free(data); + } + return ret; } @@ -1107,6 +1155,17 @@ account->registration_cb_user_data = user_data; } +static void +purple_account_register_got_password_cb(PurpleAccount *account, + const char *password, + GError *error, + gpointer data) +{ + g_return_if_fail(account != NULL); + + _purple_connection_new(account, TRUE, password); +} + void purple_account_register(PurpleAccount *account) { @@ -1115,7 +1174,22 @@ 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(account, purple_account_register_got_password_cb, NULL); +} + +static void +purple_account_unregister_got_password_cb(PurpleAccount *account, + const char *password, + GError *error, + gpointer data) +{ + CbInfo *info = data; + PurpleAccountUnregistrationCb cb; + + cb = (PurpleAccountUnregistrationCb)info->cb; + _purple_connection_new_unregister(account, password, cb, info->data); + + g_free(info); } void @@ -1130,12 +1204,18 @@ void purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data) { + CbInfo *info; + 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); + info = g_new0(CbInfo, 1); + info->cb = PURPLE_CALLBACK(cb); + info->data = user_data; + + purple_keyring_get_password(account, purple_account_unregister_got_password_cb, info); } static void @@ -1153,11 +1233,12 @@ return; } - if(remember) - purple_account_set_remember_password(account, TRUE); - - purple_account_set_password(account, entry); - + if (!remember) + purple_keyring_set_password(account, NULL, NULL, NULL); + + purple_account_set_remember_password(account, remember); + + purple_account_set_password(account, entry, NULL, NULL); _purple_connection_new(account, FALSE, entry); } @@ -1210,11 +1291,27 @@ g_free(primary); } +static void +purple_account_connect_got_password_cb(PurpleAccount *account, + const gchar *password, + GError *error, + gpointer data) +{ + PurplePluginProtocolInfo *prpl_info = data; + + if ((password == NULL) && + !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && + !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) + purple_account_request_password(account, G_CALLBACK(request_password_ok_cb), G_CALLBACK(request_password_cancel_cb), account); + else + _purple_connection_new(account, FALSE, password); +} + void purple_account_connect(PurpleAccount *account) { PurplePlugin *prpl; - const char *password, *username; + const char *username; PurplePluginProtocolInfo *prpl_info; g_return_if_fail(account != NULL); @@ -1241,13 +1338,7 @@ purple_debug_info("account", "Connecting to account %s.\n", username); prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - password = purple_account_get_password(account); - if ((password == NULL) && - !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && - !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) - purple_account_request_password(account, G_CALLBACK(request_password_ok_cb), G_CALLBACK(request_password_cancel_cb), account); - else - _purple_connection_new(account, FALSE, password); + purple_keyring_get_password(account, purple_account_connect_got_password_cb, prpl_info); } void @@ -1267,8 +1358,6 @@ 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; @@ -1278,7 +1367,7 @@ purple_account_is_disconnecting(const PurpleAccount *account) { g_return_val_if_fail(account != NULL, TRUE); - + return account->disconnecting; } @@ -1634,8 +1723,11 @@ blist_ops->save_account(account); } -void -purple_account_set_password(PurpleAccount *account, const char *password) +void +purple_account_set_password(PurpleAccount *account, + const gchar *password, + PurpleKeyringSaveCallback cb, + gpointer data) { g_return_if_fail(account != NULL); @@ -1643,6 +1735,20 @@ account->password = g_strdup(password); schedule_accounts_save(); + + 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(account, password, cb, data); + } } void @@ -2149,12 +2255,59 @@ return account->username; } -const char * -purple_account_get_password(const PurpleAccount *account) +static void +purple_account_get_password_async_finish(PurpleAccount *account, + const char *password, + GError *error, + gpointer data) { - g_return_val_if_fail(account != NULL, NULL); - - return account->password; + CbInfo *info = data; + PurpleKeyringReadCallback cb; + + purple_debug_info("account", + "Read password for account %s (%s) from async keyring.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + g_free(account->password); + account->password = g_strdup(password); + + cb = (PurpleKeyringReadCallback)info->cb; + if (cb != NULL) + cb(account, password, error, info->data); + + g_free(info); +} + +void +purple_account_get_password(PurpleAccount *account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + if (account == NULL) { + cb(NULL, NULL, NULL, data); + return; + } + + if (account->password != NULL) { + purple_debug_info("account", + "Reading password for account %s (%s) from cache.\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + cb(account, account->password, NULL, data); + + } else { + CbInfo *info = g_new0(CbInfo, 1); + info->cb = PURPLE_CALLBACK(cb); + info->data = data; + + purple_debug_info("account", + "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(account, + purple_account_get_password_async_finish, info); + } } const char * @@ -2669,7 +2822,7 @@ PurpleConnection *gc = purple_account_get_connection(account); PurplePlugin *prpl = NULL; - purple_account_set_password(account, new_pw); + purple_account_set_password(account, new_pw, NULL, NULL); if (gc != NULL) prpl = purple_connection_get_prpl(gc); @@ -2820,6 +2973,12 @@ purple_signal_emit(purple_accounts_get_handle(), "account-removed", account); } +static void +purple_accounts_delete_finish(PurpleAccount *account, GError *error, gpointer data) +{ + purple_account_destroy(account); +} + void purple_accounts_delete(PurpleAccount *account) { @@ -2890,7 +3049,11 @@ /* 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(account, NULL, + purple_accounts_delete_finish, NULL); } void
--- a/libpurple/account.h Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/account.h Fri Nov 18 19:54:07 2011 +0000 @@ -50,6 +50,7 @@ #include "proxy.h" #include "prpl.h" #include "status.h" +#include "keyring.h" /** * Account request types. @@ -365,10 +366,20 @@ /** * Sets the account's password. * + * The password in the keyring might not be immediately updated, but the cached + * 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 callback if + * notification of saving to the keyring is not required. + * * @param account The account. * @param password The password. + * @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(PurpleAccount *account, const char *password); +void purple_account_set_password(PurpleAccount *account, + const gchar *password, + PurpleKeyringSaveCallback cb, + gpointer data); /** * Sets the account's alias. @@ -676,13 +687,20 @@ const char *purple_account_get_username(const PurpleAccount *account); /** - * Returns the account's password. + * Reads the password for the account. + * + * This is an asynchronous call, that will return the password in a callback + * once it has been read from the keyring. If the account is connected, and you + * require the password immediately, then consider using @ref + * purple_connection_get_password instead. * * @param account The account. - * - * @return The password. + * @param cb The callback to give the password. + * @param data A pointer passed to the callback. */ -const char *purple_account_get_password(const PurpleAccount *account); +void purple_account_get_password(PurpleAccount *account, + PurpleKeyringReadCallback cb, + gpointer data); /** * Returns the account's alias.
--- a/libpurple/connection.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/connection.c Fri Nov 18 19:54:07 2011 +0000 @@ -451,12 +451,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 : purple_account_get_password(gc->account); + return gc->password; } const char * @@ -474,6 +478,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) @@ -510,7 +522,6 @@ { PurpleAccount *account; PurpleConnection *gc; - char *password; account = data; gc = purple_account_get_connection(account); @@ -518,11 +529,7 @@ if (gc != NULL) 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 Nov 18 19:53:46 2011 +0000 +++ b/libpurple/connection.h Fri Nov 18 19:54:07 2011 +0000 @@ -145,6 +145,7 @@ #include <time.h> #include "account.h" +#include "keyring.h" #include "plugin.h" #include "status.h" #include "sslconn.h" @@ -383,6 +384,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 Nov 18 19:53:46 2011 +0000 +++ b/libpurple/core.c Fri Nov 18 19:54:07 2011 +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 Nov 18 19:53:46 2011 +0000 +++ b/libpurple/example/nullclient.c Fri Nov 18 19:54:07 2011 +0000 @@ -305,7 +305,7 @@ /* Get the password for the account */ password = getpass("Password: "); - purple_account_set_password(account, password); + purple_account_set_password(account, password, 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 Fri Nov 18 19:54:07 2011 +0000 @@ -0,0 +1,967 @@ +/** + * @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; + +/******************************************/ +/** @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; +}; + +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(void) +{ + return g_new0(PurpleKeyring, 1); +} + +/* Destructor */ +void +purple_keyring_free(PurpleKeyring *keyring) +{ + g_free(keyring); +} + +/* 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; +} + +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, const char *name) +{ + g_return_if_fail(keyring != NULL); + + g_free(keyring->name); + keyring->name = g_strdup(name); +} + +void +purple_keyring_set_id(PurpleKeyring *keyring, const char *id) +{ + g_return_if_fail(keyring != NULL); + + g_free(keyring->id); + keyring->id = g_strdup(id); +} + +void +purple_keyring_set_read_password(PurpleKeyring *keyring, PurpleKeyringRead read) +{ + g_return_if_fail(keyring != NULL); + + keyring->read_password = read; +} + +void +purple_keyring_set_save_password(PurpleKeyring *keyring, PurpleKeyringSave save) +{ + g_return_if_fail(keyring != NULL); + + keyring->save_password = save; +} + +void +purple_keyring_set_close_keyring(PurpleKeyring *keyring, PurpleKeyringClose close) +{ + g_return_if_fail(keyring != NULL); + + keyring->close_keyring = close; +} + +void +purple_keyring_set_change_master(PurpleKeyring *keyring, PurpleKeyringChangeMaster change) +{ + g_return_if_fail(keyring != NULL); + + keyring->change_master = change; +} + +void +purple_keyring_set_import_password(PurpleKeyring *keyring, PurpleKeyringImportPassword import) +{ + g_return_if_fail(keyring != NULL); + + keyring->import_password = import; +} + +void +purple_keyring_set_export_password(PurpleKeyring *keyring, PurpleKeyringExportPassword export) +{ + g_return_if_fail(keyring != NULL); + + keyring->export_password = export; +} + +/*@}*/ + + +/***************************************/ +/** @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; + +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_find_keyring_by_id(id); + g_return_if_fail(new != NULL); + + purple_keyring_set_inuse(new, FALSE, NULL, data); +} + +void +purple_keyring_init(void) +{ + 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", PURPLE_DEFAULT_KEYRING); + purple_keyring_to_use = g_strdup(PURPLE_DEFAULT_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); +} + +void +purple_keyring_uninit(void) +{ + g_free(purple_keyring_to_use); + purple_debug_info("keyring", "purple_keyring_uninit() done.\n"); +} + +PurpleKeyring * +purple_keyring_find_keyring_by_id(const 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(void) +{ + return purple_keyring_keyrings; +} + +const PurpleKeyring * +purple_keyring_get_inuse(void) +{ + 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); +} + +static void +purple_keyring_set_inuse_check_error_cb(PurpleAccount *account, + GError *error, + gpointer data) +{ + const char *name; + PurpleKeyringClose close; + 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 == PURPLE_KEYRING_ERROR)) { + tracker->error = error; + + switch(error->code) { + case PURPLE_KEYRING_ERROR_NOCAP: + purple_debug_info("keyring", + "Keyring could not save password for account %s : %s.\n", + name, error->message); + break; + + case PURPLE_KEYRING_ERROR_NOPASSWD: + purple_debug_info("keyring", + "No password found while changing keyring for account %s : %s.\n", + name, error->message); + break; + + case PURPLE_KEYRING_ERROR_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.\n"); + + 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. + * + * 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_remember_password(account, + purple_account_get_remember_password(account)); +} + +static void +purple_keyring_set_inuse_got_pw_cb(PurpleAccount *account, + const 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 == PURPLE_KEYRING_ERROR_NOPASSWD || + error->code == PURPLE_KEYRING_ERROR_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, + purple_keyring_set_inuse_check_error_cb, tracker); + + } else { + error = g_error_new(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOCAP, + "cannot store passwords in new keyring"); + purple_keyring_set_inuse_check_error_cb(account, error, data); + g_error_free(error); + } + } +} + +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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOCAP, + "Existing keyring cannot read passwords"); + */ + purple_debug_info("keyring", "Existing keyring cannot read passwords.\n"); + + /* 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_new(PurpleKeyringChangeTracker, 1); + + 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); + } +} + +GList * +purple_keyring_get_options(void) +{ + 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); + } + + return list; +} + +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.\n", + 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(PURPLE_DEFAULT_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.\n", 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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_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(PURPLE_DEFAULT_KEYRING, realid))) { + + *error = g_error_new(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOCAP, + "Keyring cannot import password info."); + purple_debug_info("Keyring", "Configured keyring cannot import password info. This might be normal.\n"); + 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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOKEYRING, + "No keyring configured, cannot export password info"); + purple_debug_info("keyring", + "No keyring configured, cannot export password info.\n"); + 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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_INVALID, + "Plugin does not have a keyring id"); + purple_debug_info("keyring", + "Configured keyring does not have a keyring id, cannot export password.\n"); + return FALSE; + } + + export = purple_keyring_get_export_password(inuse); + + if (export == NULL) { + *error = g_error_new(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOCAP, + "Keyring cannot export password info."); + purple_debug_info("keyring", + "Keyring cannot export password info. This might be normal.\n"); + return FALSE; + } + + return export(account, mode, data, error, destroy); +} + +void +purple_keyring_get_password(PurpleAccount *account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + GError *error = NULL; + const PurpleKeyring *inuse; + PurpleKeyringRead read; + + if (account == NULL) { + error = g_error_new(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOCAP, + "Keyring cannot read password."); + + if (cb != NULL) + cb(account, NULL, error, data); + + g_error_free(error); + + } else { + read(account, cb, data); + } + } + } +} + +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); +} + +void +purple_keyring_set_password(PurpleAccount *account, + const gchar *password, + 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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOCAP, + "Keyring cannot save password."); + if (cb != NULL) + cb(account, error, data); + g_error_free(error); + + } else { + cbinfo = g_new(PurpleKeyringCbInfo, 1); + cbinfo->cb = cb; + cbinfo->data = data; + save(account, password, purple_keyring_set_password_async_cb, cbinfo); + } + } +} + +void +purple_keyring_close(PurpleKeyring *keyring, GError **error) +{ + PurpleKeyringClose close; + + if (keyring == NULL) { + *error = g_error_new(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_INVALID, + "No keyring passed to the function."); + + } else { + close = purple_keyring_get_close_keyring(keyring); + + if (close == NULL) { + *error = g_error_new(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOCAP, + "Keyring doesn't support being closed."); + + } else { + close(error); + + } + } +} + + +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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_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(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOCAP, + "Keyring doesn't support master passwords."); + if (cb) + cb(FALSE, error, data); + + g_error_free(error); + + } else { + change(cb, data); + + } + } +} + +/*@}*/ + + +/***************************************/ +/** @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 Fri Nov 18 19:54:07 2011 +0000 @@ -0,0 +1,445 @@ +/** + * @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 PURPLE_DEFAULT_KEYRING "core-scrouaf-internalkeyring" + +/*******************************************************/ +/** @name data structures and types */ +/*******************************************************/ +/*@{*/ +typedef struct _PurpleKeyring PurpleKeyring; + +/*@}*/ + +/** + * 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 will be NULL, and the error set. + * + * @param account The account. + * @param password The password. + * @param error Error that may have occurred. + * @param data Data passed to the callback. + */ +typedef void (*PurpleKeyringReadCallback)(PurpleAccount *account, + const gchar *password, + GError *error, + gpointer data); + +/** + * Callback for once a password has been stored. + * + * If there was a problem, the error will be set. + * + * @param account The account. + * @param error Error that may have occurred. + * @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 TRUE if the password has been changed, FALSE otherwise. + * @param error Error that has occurred. + * @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 occurred. + * @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. + * @param cb A callback for 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. + * @param password The password to be stored. If the password is NULL, this + * means that the keyring should forget about that password. + * @param cb A callback for once the password is saved. + * @param data Data to be passed to the callback. + */ +typedef void (*PurpleKeyringSave)(PurpleAccount *account, + const gchar *password, + PurpleKeyringSaveCallback cb, + gpointer data); + +/** + * 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. + * + * @param cb A callback for once the master password has been changed. + * @param data Data to be passed 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. + * @param data Data that was stored. Can be NULL. + * + * @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. + * @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 its keyring id. + * + * @param id The id for the keyring. + * + * @return The keyring, or NULL if not found. + */ +PurpleKeyring *purple_keyring_find_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 force FALSE if the change can be cancelled. If this is TRUE and + * an error occurs, data might be lost. + * @param cb A callback for once the change is complete. + * @param data Data to be passed to the callback. + */ +void +purple_keyring_set_inuse(const PurpleKeyring *newkeyring, + gboolean force, + PurpleKeyringSetInUseCallback cb, + gpointer data); + +/** + * Register a keyring plugin. + * + * @param keyring 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 keyring 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. + * + * @param account The account. + * @param cb A callback for once the password is read. + * @param data Data passed to the callback. + */ +void +purple_keyring_get_password(PurpleAccount *account, + PurpleKeyringReadCallback cb, + gpointer data); + +/** + * Set a password to be remembered. + * + * @param account The account. + * @param password The password to save. + * @param cb A callback for once the password is saved. + * @param data Data to be passed to the callback. + */ +void +purple_keyring_set_password(PurpleAccount *account, + const gchar *password, + PurpleKeyringSaveCallback cb, + gpointer data); + +/** + * 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 for once the master password has been changed. + * @param data Data 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); +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, const char *name); +void purple_keyring_set_id(PurpleKeyring *info, const char *id); +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 PURPLE_KEYRING_ERROR purple_keyring_error_domain() +/** stuff here too */ +GQuark purple_keyring_error_domain(void); + +/** error codes for keyrings. */ +enum PurpleKeyringError +{ + PURPLE_KEYRING_ERROR_OK = 0, /**< No error. */ + PURPLE_KEYRING_ERROR_NOPASSWD = 1, /**< No stored password. */ + PURPLE_KEYRING_ERROR_NOACCOUNT, /**< Account not found. */ + PURPLE_KEYRING_ERROR_WRONGPASS, /**< User submitted wrong password when prompted. */ + PURPLE_KEYRING_ERROR_WRONGFORMAT, /**< Data passed is not in suitable format. */ + PURPLE_KEYRING_ERROR_NOKEYRING, /**< No keyring configured. */ + PURPLE_KEYRING_ERROR_NOCHANNEL, /**< Failed to communicate with the backend */ + PURPLE_KEYRING_ERROR_INVALID, /**< Invalid input */ + PURPLE_KEYRING_ERROR_NOCAP, /**< Keyring doesn't support this */ + PURPLE_KEYRING_ERROR_UNKNOWN /**< Unknown error */ +}; + +/*}@*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _PURPLE_KEYRING_H_ */ +
--- a/libpurple/plugin.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/plugin.c Fri Nov 18 19:54:07 2011 +0000 @@ -477,6 +477,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; @@ -1423,7 +1429,7 @@ } protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin, - (GCompareFunc)compare_prpl); + (GCompareFunc)compare_prpl); } } #endif /* PURPLE_PLUGINS */
--- a/libpurple/plugin.h Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/plugin.h Fri Nov 18 19:54:07 2011 +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 Nov 18 19:53:46 2011 +0000 +++ b/libpurple/plugins/Makefile.am Fri Nov 18 19:54:07 2011 +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 Fri Nov 18 19:54:07 2011 +0000 @@ -0,0 +1,52 @@ +EXTRA_DIST = \ + Makefile.mingw + +plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) + +internalkeyring_la_CFLAGS = $(AM_CPPFLAGS) +internalkeyring_la_LDFLAGS = -module -avoid-version +internalkeyring_la_SOURCES = internalkeyring.c +internalkeyring_la_LIBADD = $(GLIB_LIBS) + +if ENABLE_GNOMEKEYRING + +gnomekeyring_la_CFLAGS = $(AM_CPPFLAGS) $(GNOMEKEYRING_CFLAGS) +gnomekeyring_la_LDFLAGS = -module -avoid-version +gnomekeyring_la_SOURCES = gnomekeyring.c +gnomekeyring_la_LIBADD = $(GLIB_LIBS) $(GNOMEKEYRING_LIBS) + +endif + +if ENABLE_KWALLET + +kwallet_la_CXXFLAGS = $(KWALLET_CXXFLAGS) +kwallet_la_LDFLAGS = -module -avoid-version +kwallet_la_SOURCES = kwallet.cpp +kwallet_la_LIBADD = $(GLIB_LIBS) $(KWALLET_LIBS) + +endif + +if PLUGINS + +plugin_LTLIBRARIES = \ + internalkeyring.la + +if ENABLE_GNOMEKEYRING +plugin_LTLIBRARIES += \ + gnomekeyring.la +endif + +if ENABLE_KWALLET +plugin_LTLIBRARIES += \ + kwallet.la +endif + +endif + +AM_CPPFLAGS = \ + -I$(top_srcdir)/libpurple \ + -I$(top_builddir)/libpurple \ + $(GLIB_CFLAGS) \ + $(DEBUG_CFLAGS) \ + $(PLUGIN_CFLAGS) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/plugins/keyrings/gnomekeyring.c Fri Nov 18 19:54:07 2011 +0000 @@ -0,0 +1,409 @@ +/** + * @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> + +#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 +{ + PurpleAccount *account; + gpointer cb; + gpointer user_data; +}; + +static GQuark gkp_error_domain(void) +{ + return g_quark_from_static_string("Gnome-Keyring plugin"); +} + + +/***********************************************/ +/* Keyring interface */ +/***********************************************/ +static void +gkp_read_continue(GnomeKeyringResult result, + const char *password, + gpointer data) +/* XXX : make sure list is freed on return */ +{ + InfoStorage *storage = data; + PurpleAccount *account = storage->account; + PurpleKeyringReadCallback cb = storage->cb; + GError *error; + + if (result != GNOME_KEYRING_RESULT_OK) { + switch(result) { + case GNOME_KEYRING_RESULT_NO_MATCH: + error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, + PURPLE_KEYRING_ERROR_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, + PURPLE_KEYRING_ERROR_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, + PURPLE_KEYRING_ERROR_NOCHANNEL, + "Unknown error (account : %s).", + purple_account_get_username(account)); + if(cb != NULL) + cb(account, NULL, error, storage->user_data); + g_error_free(error); + return; + } + + } else { + if (cb != NULL) { + cb(account, password, NULL, storage->user_data); + } + } +} + +static void +gkp_read(PurpleAccount *account, PurpleKeyringReadCallback cb, gpointer data) +{ + InfoStorage *storage = g_new0(InfoStorage, 1); + + storage->account = account; + storage->cb = cb; + storage->user_data = data; + + 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_save_continue(GnomeKeyringResult result, gpointer data) +{ + InfoStorage *storage; + PurpleKeyringSaveCallback cb; + GError *error; + PurpleAccount *account; + + storage = data; + g_return_if_fail(storage != NULL); + + account = storage->account; + cb = storage->cb; + + if (result != GNOME_KEYRING_RESULT_OK) { + switch(result) { + case GNOME_KEYRING_RESULT_NO_MATCH: + purple_debug_info("keyring-gnome", + "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, + PURPLE_KEYRING_ERROR_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("keyring-gnome", + "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, + PURPLE_KEYRING_ERROR_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("keyring-gnome", + "Unknown error (account : %s (%s)).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, + PURPLE_KEYRING_ERROR_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("keyring-gnome", "Password for %s updated.\n", + purple_account_get_username(account)); + + if (cb != NULL) + cb(account, NULL, storage->user_data); + } +} + +static void +gkp_save(PurpleAccount *account, + const gchar *password, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + InfoStorage *storage = g_new0(InfoStorage, 1); + + storage->account = account; + storage->cb = cb; + storage->user_data = data; + + if (password != NULL && *password != '\0') { + char *name; + + purple_debug_info("keyring-gnome", + "Updating password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + name = g_strdup_printf("purple-%s", + purple_account_get_username(account)); + gnome_keyring_store_password(GNOME_KEYRING_NETWORK_PASSWORD, + NULL, /*default keyring */ + 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); + g_free(name); + + } else { /* password == NULL, delete password. */ + purple_debug_info("keyring-gnome", + "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); + } +} + +static void +gkp_close(GError **error) +{ +} + +static gboolean +gkp_import_password(PurpleAccount *account, + const char *mode, + const char *data, + GError **error) +{ + purple_debug_info("keyring-gnome", "Importing password.\n"); + return TRUE; +} + +static gboolean +gkp_export_password(PurpleAccount *account, + const char **mode, + char **data, + GError **error, + GDestroyNotify *destroy) +{ + purple_debug_info("keyring-gnome", "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("keyring-gnome", + "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(void) +{ + purple_debug_info("keyring-gnome", "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_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("keyring-gnome", + "Failed to communicate with daemon, not loading.\n"); + return FALSE; + } +} + +static void +gkp_uninit(void) +{ + purple_debug_info("keyring-gnome", "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(); +} + +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 */ + "GNOME 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("keyring-gnome", "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 Fri Nov 18 19:54:07 2011 +0000 @@ -0,0 +1,275 @@ +/** + * @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> + +#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 PURPLE_DEFAULT_KEYRING + +#define ACTIVATE() \ + if (internal_keyring_passwords == NULL) \ + internal_keyring_open(); + +static GHashTable *internal_keyring_passwords = NULL; +static PurpleKeyring *keyring_handler = NULL; + +/***********************************************/ +/* Keyring interface */ +/***********************************************/ +static void +internal_keyring_open(void) +{ + internal_keyring_passwords = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, g_free); +} + +static void +internal_keyring_read(PurpleAccount *account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + const char *password; + GError *error; + + ACTIVATE(); + + purple_debug_info("keyring-internal", + "Reading password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + password = g_hash_table_lookup(internal_keyring_passwords, account); + + if (password != NULL) { + if (cb != NULL) + cb(account, password, NULL, data); + } else { + error = g_error_new(PURPLE_KEYRING_ERROR, + PURPLE_KEYRING_ERROR_NOPASSWD, "Password not found."); + if (cb != NULL) + cb(account, NULL, error, data); + g_error_free(error); + } +} + +static void +internal_keyring_save(PurpleAccount *account, + const gchar *password, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + ACTIVATE(); + + if (password == NULL || *password == '\0') { + g_hash_table_remove(internal_keyring_passwords, account); + purple_debug_info("keyring-internal", + "Deleted password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + } else { + g_hash_table_replace(internal_keyring_passwords, account, g_strdup(password)); + purple_debug_info("keyring-internal", + "Updated password for account %s (%s).\n", + purple_account_get_username(account), + purple_account_get_protocol_id(account)); + + } + + if (cb != NULL) + cb(account, NULL, data); +} + + +static void +internal_keyring_close(GError **error) +{ + g_hash_table_destroy(internal_keyring_passwords); + internal_keyring_passwords = NULL; +} + +static gboolean +internal_keyring_import_password(PurpleAccount *account, + const char *mode, + const char *data, + GError **error) +{ + ACTIVATE(); + + purple_debug_info("keyring-internal", "Importing password.\n"); + + if (account != NULL && + data != NULL && + (mode == NULL || g_strcmp0(mode, "cleartext") == 0)) { + + g_hash_table_replace(internal_keyring_passwords, account, g_strdup(data)); + return TRUE; + + } else { + *error = g_error_new(PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_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("keyring-internal", "Exporting password.\n"); + + password = g_hash_table_lookup(internal_keyring_passwords, account); + + if (password == NULL) { + return FALSE; + } else { + *mode = "cleartext"; + *data = g_strdup(password); + *destroy = g_free; + return TRUE; + } +} + +static void +internal_keyring_init(void) +{ + 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_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(void) +{ + 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(); +} + +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("keyring-internal", "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 Fri Nov 18 19:54:07 2011 +0000 @@ -0,0 +1,387 @@ +/** + * @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> + +#include "account.h" +#include "version.h" +#include "keyring.h" +#include "debug.h" +#include "plugin.h" +#include "internal.h" +#include <kwallet.h> + +#define KWALLET_NAME N_("KWallet") +#define KWALLET_VERSION "0.3b" +#define KWALLET_DESCRIPTION N_("This plugin will store passwords in KWallet.") +#define KWALLET_AUTHOR "Scrouaf (scrouaf[at]soc.pidgin.im)" +#define KWALLET_ID "core-scrouaf-kwallet" + +PurpleKeyring *keyring_handler; + +#define ERR_KWALLETPLUGIN kwallet_plugin_error_domain() + +namespace KWalletPlugin { + +class request +{ + public: + virtual void abort(); + virtual void execute(KWallet::Wallet *wallet); + + protected: + gpointer data; + PurpleAccount *account; + QString password; +}; + +class engine : QObject +{ + public: + engine(); + ~engine(); + void queue(request req); + static engine *Instance(); + + signals: + void walletOpened(bool opened); + + private: + bool connected; + KWallet::Wallet *wallet; + std::list<request> requests; + static engine *pinstance; + +/* KApplication *app; */ + void ExecuteRequests(); +}; + +class save_request : public request +{ + public: + save_request(PurpleAccount *account, const char *password, PurpleKeyringSaveCallback cb, void *data); + void abort(); + void execute(KWallet::Wallet *wallet); + + private: + PurpleKeyringSaveCallback callback; +}; + +class read_request : public request +{ + public: + read_request(PurpleAccount *account, PurpleKeyringReadCallback cb, void *data); + void abort(); + void execute(KWallet::Wallet *wallet); + + private: + PurpleKeyringReadCallback callback; +}; + +static GQuark +kwallet_plugin_error_domain(void) +{ + return g_quark_from_static_string("KWallet keyring"); +} + +} + +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, KWallet::Wallet::Asynchronous); + QObject::connect(wallet, SIGNAL(KWallet::Wallet::walletOpened(bool)), SLOT(walletOpened(bool))); +} + +KWalletPlugin::engine::~engine() +{ + while (!requests.empty()) { + request &req = requests.front(); + req.abort(); + requests.pop_front(); + } + + KWallet::Wallet::closeWallet(KWallet::Wallet::NetworkWallet(), TRUE); + delete wallet; + pinstance = NULL; +} + +KWalletPlugin::engine * +KWalletPlugin::engine::Instance() +{ + if (pinstance == NULL) + pinstance = new engine; + return pinstance; +} + +void +KWalletPlugin::engine::walletOpened(bool opened) +{ + connected = opened; + + if (opened) { + ExecuteRequests(); + } else { + while (!requests.empty()) { + request &req = requests.front(); + req.abort(); + requests.pop_front(); + } + delete this; + } +} + +void +KWalletPlugin::engine::queue(request req) +{ + requests.push_back(req); + ExecuteRequests(); +} + +void +KWalletPlugin::engine::ExecuteRequests() +{ + if (connected) { + while (!requests.empty()) { + request &req = requests.front(); + req.execute(wallet); + requests.pop_front(); + } + } +} + +KWalletPlugin::save_request::save_request(PurpleAccount *acc, const char *pw, PurpleKeyringSaveCallback cb, void *userdata) +{ + account = acc; + data = userdata; + callback = cb; + password = QString(pw); +} + +KWalletPlugin::read_request::read_request(PurpleAccount *acc, PurpleKeyringReadCallback cb, void *userdata) +{ + account = acc; + data = userdata; + callback = cb; + password = QString(); +} + +void +KWalletPlugin::save_request::abort() +{ + GError *error; + if (callback != NULL) { + error = g_error_new(ERR_KWALLETPLUGIN, + PURPLE_KEYRING_ERROR_UNKNOWN, + "Failed to save password"); + callback(account, error, data); + g_error_free(error); + } +} + +void +KWalletPlugin::read_request::abort() +{ + GError *error; + if (callback != NULL) { + error = g_error_new(ERR_KWALLETPLUGIN, + PURPLE_KEYRING_ERROR_UNKNOWN, + "Failed to read password"); + callback(account, NULL, error, data); + g_error_free(error); + } +} + +void +KWalletPlugin::read_request::execute(KWallet::Wallet *wallet) +{ + int result; + QString key; + + key = QString("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, password.toUtf8().constData(), NULL, data); +} + +void +KWalletPlugin::save_request::execute(KWallet::Wallet *wallet) +{ + int result; + QString key; + + key = QString("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, NULL, data); +} + +extern "C" +{ + +static void +kwallet_read(PurpleAccount *account, + PurpleKeyringReadCallback cb, + gpointer data) +{ + KWalletPlugin::read_request req(account, cb, data); + KWalletPlugin::engine::Instance()->queue(req); +} + +static void +kwallet_save(PurpleAccount *account, + const char *password, + PurpleKeyringSaveCallback cb, + gpointer data) +{ + KWalletPlugin::save_request req(account, password, cb, data); + KWalletPlugin::engine::Instance()->queue(req); +} + +static void +kwallet_close(GError **error) +{ + delete KWalletPlugin::engine::Instance(); +} + +static gboolean +kwallet_import(PurpleAccount *account, + const char *mode, + const char *data, + GError **error) +{ + return TRUE; +} + +static gboolean +kwallet_export(PurpleAccount *account, + const char **mode, + char **data, + GError **error, + GDestroyNotify *destroy) +{ + *mode = NULL; + *data = NULL; + destroy = NULL; + + return TRUE; +} + +static gboolean +kwallet_load(PurplePlugin *plugin) +{ + keyring_handler = purple_keyring_new(); + + purple_keyring_set_name(keyring_handler, KWALLET_NAME); + purple_keyring_set_id(keyring_handler, KWALLET_ID); + purple_keyring_set_read_password(keyring_handler, kwallet_read); + purple_keyring_set_save_password(keyring_handler, kwallet_save); + purple_keyring_set_close_keyring(keyring_handler, kwallet_close); +/* purple_keyring_set_change_master(keyring_handler, kwallet_change_master);*/ + purple_keyring_set_import_password(keyring_handler, kwallet_import); + purple_keyring_set_export_password(keyring_handler, kwallet_export); + + purple_keyring_register(keyring_handler); + + return TRUE; +} + +static gboolean +kwallet_unload(PurplePlugin *plugin) +{ + kwallet_close(NULL); + return TRUE; +} + +static void +kwallet_destroy(PurplePlugin *plugin) +{ + kwallet_close(NULL); +} + +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 */ + KWALLET_ID, /* id */ + KWALLET_NAME, /* name */ + KWALLET_VERSION, /* version */ + "KWallet Keyring Plugin", /* summary */ + KWALLET_DESCRIPTION, /* description */ + KWALLET_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, +}; + +void init_plugin(PurplePlugin *plugin); +void +init_plugin(PurplePlugin *plugin) +{ + purple_debug_info("keyring-kwallet", "init plugin called.\n"); +} + +PURPLE_INIT_PLUGIN(kwallet_keyring, init_plugin, plugininfo) + +} /* extern "C" */ +
--- a/libpurple/plugins/one_time_password.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/plugins/one_time_password.c Fri Nov 18 19:54:07 2011 +0000 @@ -46,7 +46,7 @@ purple_account_get_username(account), purple_account_get_protocol_name(account)); - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); /* TODO: Do we need to somehow clear conn->password ? */ } }
--- a/libpurple/plugins/perl/common/Account.xs Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/plugins/perl/common/Account.xs Fri Nov 18 19:54:07 2011 +0000 @@ -1,4 +1,5 @@ #include "module.h" +#include "../perl-handlers.h" MODULE = Purple::Account PACKAGE = Purple::Account PREFIX = purple_account_ PROTOTYPES: ENABLE @@ -44,9 +45,13 @@ const char * username void -purple_account_set_password(account, password) +purple_account_set_password(account, password, func, data = 0) Purple::Account account const char * password + SV *func + SV *data +CODE: + purple_perl_account_set_password(account, password, func, data); void purple_account_set_alias(account, alias) @@ -130,9 +135,13 @@ purple_account_get_username(account) Purple::Account account -const char * -purple_account_get_password(account) +void +purple_account_get_password(account, func, data = 0) Purple::Account account + SV *func + SV *data +CODE: + purple_perl_account_get_password(account, func, data); const char * purple_account_get_alias(account)
--- a/libpurple/plugins/perl/perl-handlers.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/plugins/perl/perl-handlers.c Fri Nov 18 19:54:07 2011 +0000 @@ -845,3 +845,127 @@ destroy_prefs_handler(handler); } } + +static void +perl_account_save_cb(PurpleAccount *account, GError *error, gpointer data) +{ + int count; + SV *accountSV, *errorSV; + PurplePerlAccountPasswordHandler *handler = data; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + + /* Push the account onto the perl stack */ + accountSV = sv_2mortal(purple_perl_bless_object(account, "Purple::Account")); + XPUSHs(accountSV); + + /* Push the error onto the perl stack */ + errorSV = sv_2mortal(purple_perl_bless_object(account, "GLib::Error")); + XPUSHs(errorSV); + + /* Push the data onto the perl stack */ + XPUSHs((SV *)handler->data); + + PUTBACK; + count = call_sv(handler->callback, G_EVAL | G_SCALAR); + + if (count != 0) + croak("call_sv: Did not return the correct number of values.\n"); + + if (SvTRUE(ERRSV)) { + purple_debug_error("perl", + "Perl plugin command function exited abnormally: %s\n", + SvPVutf8_nolen(ERRSV)); + } + + SPAGAIN; + + PUTBACK; + FREETMPS; + LEAVE; + + g_free(handler); +} + +static void +perl_account_read_cb(PurpleAccount *account, const gchar *password, + GError *error, gpointer data) +{ + int count; + SV *accountSV, *passwordSV, *errorSV; + PurplePerlAccountPasswordHandler *handler = data; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + + /* Push the account onto the perl stack */ + accountSV = sv_2mortal(purple_perl_bless_object(account, "Purple::Account")); + XPUSHs(accountSV); + + /* Push the password onto the perl stack */ + passwordSV = newSVpv(password, 0); + passwordSV = sv_2mortal(passwordSV); + XPUSHs(passwordSV); + + /* Push the error onto the perl stack */ + errorSV = sv_2mortal(purple_perl_bless_object(account, "GLib::Error")); + XPUSHs(errorSV); + + /* Push the data onto the perl stack */ + XPUSHs((SV *)handler->data); + + PUTBACK; + count = call_sv(handler->callback, G_EVAL | G_SCALAR); + + if (count != 0) + croak("call_sv: Did not return the correct number of values.\n"); + + if (SvTRUE(ERRSV)) { + purple_debug_error("perl", + "Perl plugin command function exited abnormally: %s\n", + SvPVutf8_nolen(ERRSV)); + } + + SPAGAIN; + + PUTBACK; + FREETMPS; + LEAVE; + + g_free(handler); +} + +void +purple_perl_account_get_password(PurpleAccount *account, SV *func, SV *data) +{ + PurplePerlAccountPasswordHandler *handler; + + handler = g_new0(PurplePerlAccountPasswordHandler, 1); + handler->callback = (func != NULL && + func != &PL_sv_undef ? newSVsv(func) : NULL); + handler->data = (data != NULL && + data != &PL_sv_undef ? newSVsv(data) : NULL); + + purple_account_get_password(account, perl_account_read_cb, data); +} + +void +purple_perl_account_set_password(PurpleAccount *account, const char *password, + SV *func, SV *data) +{ + PurplePerlAccountPasswordHandler *handler; + + handler = g_new0(PurplePerlAccountPasswordHandler, 1); + handler->callback = (func != NULL && + func != &PL_sv_undef ? newSVsv(func) : NULL); + handler->data = (data != NULL && + data != &PL_sv_undef ? newSVsv(data) : NULL); + + purple_account_set_password(account, password, perl_account_save_cb, data); +} +
--- a/libpurple/plugins/perl/perl-handlers.h Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/plugins/perl/perl-handlers.h Fri Nov 18 19:54:07 2011 +0000 @@ -48,6 +48,13 @@ } PurplePerlPrefsHandler; +typedef struct +{ + SV *callback; + SV *data; + +} PurplePerlAccountPasswordHandler; + void purple_perl_plugin_action_cb(PurplePluginAction * gpa); GList *purple_perl_plugin_actions(PurplePlugin *plugin, gpointer context); @@ -82,4 +89,10 @@ void purple_perl_prefs_disconnect_callback(guint callback_id); void purple_perl_pref_cb_clear_for_plugin(PurplePlugin *plugin); +void +purple_perl_account_get_password(PurpleAccount *account, SV *func, SV *data); +void +purple_perl_account_set_password(PurpleAccount *account, const char *password, + SV *func, SV *data); + #endif /* _PURPLE_PERL_HANDLERS_H_ */
--- a/libpurple/protocols/gg/gg.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/gg/gg.c Fri Nov 18 19:54:07 2011 +0000 @@ -386,7 +386,7 @@ t = g_strdup_printf("%u", uin); purple_account_set_username(account, t); /* Save the password if remembering passwords for the account */ - purple_account_set_password(account, p1); + purple_account_set_password(account, p1, NULL, NULL); purple_notify_info(NULL, _("New Gadu-Gadu Account Registered"), _("Registration completed successfully!"), NULL); @@ -650,7 +650,7 @@ if (req->http_req->data != NULL && ((struct gg_pubdir*)req->http_req->data)->success == 1) { - purple_account_set_password(req->account, req->new_password); + purple_account_set_password(req->account, req->new_password, NULL, NULL); purple_notify_info(req->account, messagesTitle, _("Password was changed successfully!"), NULL); goto exit_cleanup; @@ -706,7 +706,7 @@ goto exit_err; } - if (g_utf8_collate(cur, purple_account_get_password(account)) != 0) { + if (g_utf8_collate(cur, purple_connection_get_password(gc)) != 0) { purple_notify_error(account, messagesTitle, _("Your current password is different from the one that" " you specified."), NULL); @@ -723,7 +723,7 @@ mail); h = gg_change_passwd4(ggp_get_uin(account), mail, - purple_account_get_password(account), p1, info->token->id, t, + purple_connection_get_password(gc), p1, info->token->id, t, 1); if (h == NULL) @@ -741,7 +741,7 @@ req->inpa = ggp_http_input_add(h, ggp_callback_change_passwd_handler, req); } - + exit_err: g_free(cur); g_free(p1); @@ -2283,7 +2283,7 @@ purple_connection_set_protocol_data(gc, info); glp->uin = ggp_get_uin(account); - glp->password = charset_convert(purple_account_get_password(account), + glp->password = charset_convert(purple_connection_get_password(gc), "UTF-8", "CP1250"); if (glp->uin == 0) { @@ -2309,7 +2309,7 @@ glp->async = 1; glp->status = ggp_to_gg_status(status, &glp->status_descr); - + encryption_type = purple_account_get_string(account, "encryption", "opportunistic_tls"); purple_debug_info("gg", "Requested encryption type: %s\n", @@ -2333,7 +2333,7 @@ if (!info->status_broadcasting) glp->status = glp->status|GG_STATUS_FRIENDS_MASK; - + address = purple_account_get_string(account, "gg_server", ""); if (address && *address) { /* TODO: Make this non-blocking */
--- a/libpurple/protocols/jabber/auth.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/jabber/auth.c Fri Nov 18 19:54:07 2011 +0000 @@ -110,7 +110,7 @@ if (remember) purple_account_set_remember_password(account, TRUE); - purple_account_set_password(account, entry); + purple_account_set_password(account, entry, NULL, NULL); /* Restart our connection */ jabber_auth_start_old(js); @@ -228,7 +228,7 @@ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; /* Clear the pasword if it isn't being saved */ if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); } purple_connection_error(js->gc, reason, msg); @@ -372,7 +372,7 @@ * password prompting here */ - if (!purple_account_get_password(account)) { + if (!purple_connection_get_password(js->gc)) { purple_account_request_password(account, G_CALLBACK(auth_old_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc); return; }
--- a/libpurple/protocols/jabber/auth_cyrus.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/jabber/auth_cyrus.c Fri Nov 18 19:54:07 2011 +0000 @@ -91,12 +91,10 @@ static int jabber_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret) { JabberStream *js = ctx; - PurpleAccount *account; const char *pw; size_t len; - account = purple_connection_get_account(js->gc); - pw = purple_account_get_password(account); + pw = purple_connection_get_password(js->gc); if (!conn || !secret || id != SASL_CB_PASS) return SASL_BADPARAM; @@ -154,7 +152,7 @@ if (remember) purple_account_set_remember_password(account, TRUE); - purple_account_set_password(account, entry); + purple_account_set_password(account, entry, NULL, NULL); /* Rebuild our callbacks as we now have a password to offer */ jabber_sasl_build_callbacks(js); @@ -231,7 +229,7 @@ * to get one */ - if (!purple_account_get_password(account)) { + if (!purple_connection_get_password(js->gc)) { purple_account_request_password(account, G_CALLBACK(auth_pass_cb), G_CALLBACK(auth_no_pass_cb), js->gc); return JABBER_SASL_STATE_CONTINUE; @@ -352,7 +350,6 @@ static void jabber_sasl_build_callbacks(JabberStream *js) { - PurpleAccount *account; int id; /* Set up our callbacks structure */ @@ -375,8 +372,7 @@ js->sasl_cb[id].context = (void *)js; id++; - account = purple_connection_get_account(js->gc); - if (purple_account_get_password(account) != NULL ) { + if (purple_connection_get_password(js->gc) != NULL) { js->sasl_cb[id].id = SASL_CB_PASS; js->sasl_cb[id].proc = jabber_sasl_cb_secret; js->sasl_cb[id].context = (void *)js;
--- a/libpurple/protocols/jabber/jabber.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/jabber/jabber.c Fri Nov 18 19:54:07 2011 +0000 @@ -1262,7 +1262,7 @@ cbdata->js->user->node = g_strdup(value); } if(cbdata->js->registration && !strcmp(id, "password")) - purple_account_set_password(purple_connection_get_account(cbdata->js->gc), value); + purple_account_set_password(purple_connection_get_account(cbdata->js->gc), value, NULL, NULL); } } } @@ -2484,7 +2484,7 @@ purple_notify_info(js->gc, _("Password Changed"), _("Password Changed"), _("Your password has been changed.")); - purple_account_set_password(purple_connection_get_account(js->gc), (char *)data); + purple_account_set_password(purple_connection_get_account(js->gc), (const char *)data, NULL, NULL); } else { char *msg = jabber_parse_error(js, packet, NULL); @@ -2740,7 +2740,7 @@ SET_REASON(PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED); /* Clear the pasword if it isn't being saved */ if (!purple_account_get_remember_password(purple_connection_get_account(js->gc))) - purple_account_set_password(purple_connection_get_account(js->gc), NULL); + purple_account_set_password(purple_connection_get_account(js->gc), NULL, NULL, NULL); text = _("Not Authorized"); } else if(xmlnode_get_child(packet, "temporary-auth-failure")) { text = _("Temporary Authentication Failure");
--- a/libpurple/protocols/msn/session.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/msn/session.c Fri Nov 18 19:54:07 2011 +0000 @@ -384,7 +384,7 @@ reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; msg = g_strdup(_("You have signed on from another location")); if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); + purple_account_set_password(session->account, NULL, NULL, NULL); break; case MSN_ERROR_SERV_UNAVAILABLE: reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; @@ -404,7 +404,7 @@ _("Unknown error") : info); /* Clear the password if it isn't being saved */ if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); + purple_account_set_password(session->account, NULL, NULL, NULL); break; case MSN_ERROR_BAD_BLIST: reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
--- a/libpurple/protocols/mxit/actions.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/mxit/actions.c Fri Nov 18 19:54:07 2011 +0000 @@ -335,7 +335,7 @@ out: if ( !err ) { /* update PIN in account */ - purple_account_set_password( session->acc, pin ); + purple_account_set_password( session->acc, pin, NULL, NULL ); /* update session object */ g_free( session->encpwd ); @@ -372,18 +372,18 @@ purple_request_fields_add_group( fields, group ); /* pin */ - field = purple_request_field_string_new( "pin", _( "PIN" ), purple_account_get_password( session->acc ), FALSE ); + field = purple_request_field_string_new( "pin", _( "PIN" ), purple_connection_get_password( gc ), FALSE ); purple_request_field_string_set_masked( field, TRUE ); purple_request_field_group_add_field( group, field ); /* verify pin */ - field = purple_request_field_string_new( "pin2", _( "Verify PIN" ), purple_account_get_password( session->acc ), FALSE ); + field = purple_request_field_string_new( "pin2", _( "Verify PIN" ), purple_connection_get_password( gc ), FALSE ); purple_request_field_string_set_masked( field, TRUE ); purple_request_field_group_add_field( group, field ); /* (reference: "libpurple/request.h") */ purple_request_fields( gc, _( "Change PIN" ), _( "Change MXit PIN" ), NULL, fields, _( "Set" ), - G_CALLBACK( mxit_change_pin_cb ), _( "Cancel" ), NULL, purple_connection_get_account( gc ), NULL, NULL, gc ); + G_CALLBACK( mxit_change_pin_cb ), _( "Cancel" ), NULL, session->acc, NULL, NULL, gc ); }
--- a/libpurple/protocols/mxit/cipher.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/mxit/cipher.c Fri Nov 18 19:54:07 2011 +0000 @@ -79,7 +79,7 @@ static char* transport_layer_key( struct MXitSession* session ) { static char key[16 + 1]; - const char* password = purple_account_get_password( session->acc ); + const char* password = purple_connection_get_password( session->con ); int passlen = strlen( password ); /* initialize with initial key */ @@ -125,7 +125,7 @@ /* build the secret data to be encrypted: SECRET_HEADER + password */ pass = g_string_new( SECRET_HEADER ); - g_string_append( pass, purple_account_get_password( session->acc) ); + g_string_append( pass, purple_connection_get_password( session->con ) ); padding_add( pass ); /* add ISO10126 padding */ /* now encrypt the secret. we encrypt each block separately (ECB mode) */
--- a/libpurple/protocols/mxit/login.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/mxit/login.c Fri Nov 18 19:54:07 2011 +0000 @@ -284,7 +284,7 @@ out: if ( !err ) { - purple_account_set_password( session->acc, session->profile->pin ); + purple_account_set_password( session->acc, session->profile->pin, NULL, NULL ); mxit_login_connect( session ); } else {
--- a/libpurple/protocols/myspace/myspace.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/myspace/myspace.c Fri Nov 18 19:54:07 2011 +0000 @@ -705,7 +705,7 @@ purple_connection_update_progress(session->gc, _("Logging in"), 2, 4); response_len = 0; - response = msim_compute_login_response(nc, purple_account_get_username(account), purple_account_get_password(account), &response_len); + response = msim_compute_login_response(nc, purple_account_get_username(account), purple_connection_get_password(session->gc), &response_len); g_free(nc); @@ -1836,9 +1836,9 @@ case MSIM_ERROR_INCORRECT_PASSWORD: /* Incorrect password */ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); + purple_account_set_password(session->account, NULL, NULL, NULL); #ifdef MSIM_MAX_PASSWORD_LENGTH - if (purple_account_get_password(session->account) && (strlen(purple_account_get_password(session->account)) > MSIM_MAX_PASSWORD_LENGTH)) { + if (purple_connection_get_password(session->gc) && (strlen(purple_connection_get_password(session->gc)) > MSIM_MAX_PASSWORD_LENGTH)) { gchar *suggestion; suggestion = g_strdup_printf(_("%s Your password is " @@ -1846,7 +1846,7 @@ "maximum length of %d. Please shorten your " "password at http://profileedit.myspace.com/index.cfm?fuseaction=accountSettings.changePassword and try again."), full_errmsg, - strlen(purple_account_get_password(session->account)), + strlen(purple_connection_get_password(session->gc)), MSIM_MAX_PASSWORD_LENGTH); /* Replace full_errmsg. */ @@ -1861,7 +1861,7 @@ case MSIM_ERROR_LOGGED_IN_ELSEWHERE: /* Logged in elsewhere */ reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); + purple_account_set_password(session->account, NULL, NULL, NULL); break; } purple_connection_error(session->gc, reason, full_errmsg);
--- a/libpurple/protocols/novell/novell.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/novell/novell.c Fri Nov 18 19:54:07 2011 +0000 @@ -132,7 +132,7 @@ * password was invalid. */ if (!purple_account_get_remember_password(purple_connection_get_account(gc))) - purple_account_set_password(purple_connection_get_account(gc), NULL); + purple_account_set_password(purple_connection_get_account(gc), NULL, NULL, NULL); reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; break; default: @@ -2036,7 +2036,7 @@ if (gc) { if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _("You have signed on from another location"));
--- a/libpurple/protocols/oscar/clientlogin.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/oscar/clientlogin.c Fri Nov 18 19:54:07 2011 +0000 @@ -481,7 +481,7 @@ if (status_code == 330 && status_detail_code == 3011) { PurpleAccount *account = purple_connection_get_account(gc); if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password"));
--- a/libpurple/protocols/oscar/flap_connection.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Fri Nov 18 19:54:07 2011 +0000 @@ -20,6 +20,7 @@ #include "oscar.h" +#include "account.h" #include "eventloop.h" #include "proxy.h" @@ -467,7 +468,7 @@ reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; tmp = g_strdup(_("You have signed on from another location")); if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); } else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) tmp = g_strdup(_("Server closed the connection")); else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
--- a/libpurple/protocols/oscar/oscar.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/oscar/oscar.c Fri Nov 18 19:54:07 2011 +0000 @@ -1095,7 +1095,7 @@ case 0x05: /* Incorrect password */ if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_connection_error(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password")); break; case 0x11:
--- a/libpurple/protocols/sametime/sametime.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/sametime/sametime.c Fri Nov 18 19:54:07 2011 +0000 @@ -3727,7 +3727,7 @@ return; } - pass = g_strdup(purple_account_get_password(account)); + pass = g_strdup(purple_connection_get_password(gc)); port = purple_account_get_int(account, MW_KEY_PORT, MW_PLUGIN_DEFAULT_PORT); DEBUG_INFO("user: '%s'\n", user);
--- a/libpurple/protocols/silc/silc.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/silc/silc.c Fri Nov 18 19:54:07 2011 +0000 @@ -481,7 +481,7 @@ if (remember) purple_account_set_remember_password(account, TRUE); - purple_account_set_password(account, password); + purple_account_set_password(account, password, NULL, NULL); /* Load SILC key pair */ g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); @@ -530,7 +530,7 @@ (char *)purple_account_get_string(account, "private-key", prd), (purple_connection_get_password(gc) == NULL) ? "" : purple_connection_get_password(gc), &sg->public_key, &sg->private_key)) { - if (!purple_account_get_password(account)) { + if (!purple_connection_get_password(gc)) { purple_account_request_password(account, G_CALLBACK(silcpurple_got_password_cb), G_CALLBACK(silcpurple_no_password_cb), gc); return;
--- a/libpurple/protocols/simple/simple.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/simple/simple.c Fri Nov 18 19:54:07 2011 +0000 @@ -1122,7 +1122,7 @@ purple_debug_info("simple", "REGISTER retries %d\n", sip->registrar.retries); if(sip->registrar.retries > SIMPLE_REGISTER_RETRY_MAX) { if (!purple_account_get_remember_password(purple_connection_get_account(sip->gc))) - purple_account_set_password(purple_connection_get_account(sip->gc), NULL); + purple_account_set_password(purple_connection_get_account(sip->gc), NULL, NULL, NULL); purple_connection_error(sip->gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password"));
--- a/libpurple/protocols/yahoo/libymsg.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/protocols/yahoo/libymsg.c Fri Nov 18 19:54:07 2011 +0000 @@ -158,7 +158,7 @@ if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _("You have signed on from another location")); return; @@ -1959,7 +1959,7 @@ /* Password incorrect */ /* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */ if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); error_reason = g_strdup(_("Incorrect password")); error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; break; @@ -2256,7 +2256,7 @@ } #endif /* TRY_WEBMESSENGER_LOGIN */ if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); msg = g_strdup(_("Invalid username or password")); reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
--- a/libpurple/prpl.c Fri Nov 18 19:53:46 2011 +0000 +++ b/libpurple/prpl.c Fri Nov 18 19:54:07 2011 +0000 @@ -356,11 +356,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(account, 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/pidgin/gtkaccount.c Fri Nov 18 19:53:46 2011 +0000 +++ b/pidgin/gtkaccount.c Fri Nov 18 19:54:07 2011 +0000 @@ -116,6 +116,7 @@ GtkWidget *login_frame; GtkWidget *protocol_menu; GtkWidget *password_box; + char *password; GtkWidget *username_entry; GtkWidget *password_entry; GtkWidget *alias_entry; @@ -605,10 +606,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), @@ -1243,6 +1244,8 @@ purple_signals_disconnect_by_handle(dialog); + g_free(dialog->password); + g_free(dialog); return FALSE; } @@ -1263,6 +1266,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))); @@ -1368,9 +1372,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(account, NULL, NULL, NULL); + + purple_account_set_remember_password(account, remember); /* Check Mail */ if (dialog->prpl_info && dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK) @@ -1388,9 +1394,9 @@ * don't want to prompt them. */ if ((purple_account_get_remember_password(account) || new_acct) && (*value != '\0')) - purple_account_set_password(account, value); + purple_account_set_password(account, value, NULL, NULL); else - purple_account_set_password(account, NULL); + purple_account_set_password(account, NULL, NULL, NULL); purple_account_set_username(account, username); g_free(username); @@ -1528,10 +1534,13 @@ {"STRING", 0, 2} }; -void -pidgin_account_dialog_show(PidginAccountDialogType type, - PurpleAccount *account) +static void +pidgin_account_dialog_show_continue(PurpleAccount *account, + const char *password, + GError *error, + gpointer data) { + PidginAccountDialogType type = (PidginAccountDialogType)GPOINTER_TO_INT(data); AccountPrefsDialog *dialog; GtkWidget *win; GtkWidget *main_vbox; @@ -1554,9 +1563,10 @@ g_hash_table_insert(account_pref_wins, account, dialog); } - dialog->account = account; - dialog->type = type; - dialog->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + dialog->account = account; + dialog->password = g_strdup(password); + dialog->type = type; + dialog->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); if (dialog->account == NULL) { /* Select the first prpl in the list*/ @@ -1650,6 +1660,15 @@ gtk_widget_grab_focus(dialog->protocol_menu); } +void +pidgin_account_dialog_show(PidginAccountDialogType type, + PurpleAccount *account) +{ + /* this is to make sure the password will be cached */ + purple_account_get_password(account, + pidgin_account_dialog_show_continue, GINT_TO_POINTER(type)); +} + /************************************************************************** * Accounts Dialog **************************************************************************/
--- a/pidgin/gtkconn.c Fri Nov 18 19:53:46 2011 +0000 +++ b/pidgin/gtkconn.c Fri Nov 18 19:54:07 2011 +0000 @@ -196,10 +196,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 Nov 18 19:53:46 2011 +0000 +++ b/pidgin/gtkprefs.c Fri Nov 18 19:54:07 2011 +0000 @@ -42,6 +42,7 @@ #include "upnp.h" #include "util.h" #include "network.h" +#include "keyring.h" #include "gtkblist.h" #include "gtkconv.h" @@ -2346,6 +2347,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_find_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_find_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) @@ -2896,6 +2971,7 @@ prefs_notebook_add_page(_("Logging"), logging_page(), notebook_page++); prefs_notebook_add_page(_("Network"), network_page(), notebook_page++); prefs_notebook_add_page(_("Proxy"), proxy_page(), notebook_page++); + prefs_notebook_add_page(_("Password Storage"), keyring_page(), notebook_page++); prefs_notebook_add_page(_("Sounds"), sound_page(), notebook_page++); prefs_notebook_add_page(_("Status / Idle"), away_page(), notebook_page++);