propagate from branch 'im.pidgin.pidgin' (head 78b626b30c43634f501b1eb61233cef2735ddf6a) soc.2008.masterpassword

Fri, 18 Nov 2011 19:54:07 +0000

author
Elliott Sales de Andrade <qulogic@pidgin.im>
date
Fri, 18 Nov 2011 19:54:07 +0000
branch
soc.2008.masterpassword
changeset 34074
6cc03d25d5f2
parent 34072
5450e39dd3c5 (diff)
parent 32730
78b626b30c43 (current diff)
child 34075
b94bb43be5d2

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++);

mercurial