propagate from branch 'im.pidgin.pidgin' (head 32da7ab6d1092d199f3c54e43644fb6303f82bd2) soc.2008.masterpassword

Sat, 29 Aug 2009 00:45:37 +0000

author
Paul Aurich <darkrain42@pidgin.im>
date
Sat, 29 Aug 2009 00:45:37 +0000
branch
soc.2008.masterpassword
changeset 34021
25c14927f826
parent 28412
32da7ab6d109 (current diff)
parent 34020
e0ce5fb11bdb (diff)
child 34022
c49f6e9ea27d

propagate from branch 'im.pidgin.pidgin' (head 32da7ab6d1092d199f3c54e43644fb6303f82bd2)
to branch 'im.pidgin.soc.2008.masterpassword' (head e0ce5fb11bdba2ac134e9675ce4dffd3f43e0418)

.todo file | annotate | diff | comparison | revisions
COPYRIGHT file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
finch/gntaccount.c file | annotate | diff | comparison | revisions
finch/gntprefs.c file | annotate | diff | comparison | revisions
libpurple/Makefile.am file | annotate | diff | comparison | revisions
libpurple/account.c file | annotate | diff | comparison | revisions
libpurple/account.h file | annotate | diff | comparison | revisions
libpurple/connection.c file | annotate | diff | comparison | revisions
libpurple/connection.h file | annotate | diff | comparison | revisions
libpurple/core.c file | annotate | diff | comparison | revisions
libpurple/plugin.c file | annotate | diff | comparison | revisions
libpurple/plugin.h file | annotate | diff | comparison | revisions
libpurple/plugins/Makefile.am file | annotate | diff | comparison | revisions
libpurple/protocols/oscar/flap_connection.c file | annotate | diff | comparison | revisions
libpurple/protocols/qq/qq_define.c file | annotate | diff | comparison | revisions
libpurple/protocols/qq/qq_define.h file | annotate | diff | comparison | revisions
libpurple/protocols/qq/qq_network.c file | annotate | diff | comparison | revisions
libpurple/protocols/toc/Makefile.am file | annotate | diff | comparison | revisions
libpurple/protocols/toc/Makefile.mingw file | annotate | diff | comparison | revisions
libpurple/protocols/toc/PROTOCOL file | annotate | diff | comparison | revisions
libpurple/protocols/toc/toc.c file | annotate | diff | comparison | revisions
libpurple/protocols/yahoo/libymsg.c file | annotate | diff | comparison | revisions
libpurple/protocols/yahoo/libymsg.h file | annotate | diff | comparison | revisions
libpurple/protocols/yahoo/yahoo.c file | annotate | diff | comparison | revisions
libpurple/protocols/yahoo/yahoo.h file | annotate | diff | comparison | revisions
libpurple/protocols/yahoo/yahoo_auth.c file | annotate | diff | comparison | revisions
libpurple/protocols/yahoo/yahoo_auth.h file | annotate | diff | comparison | revisions
libpurple/protocols/yahoo/yahoo_crypt.c file | annotate | diff | comparison | revisions
libpurple/protocols/yahoo/yahoo_crypt.h file | annotate | diff | comparison | revisions
libpurple/prpl.c file | annotate | diff | comparison | revisions
libpurple/xmlnode.h file | annotate | diff | comparison | revisions
pidgin/gtkaccount.c file | annotate | diff | comparison | revisions
pidgin/gtkconn.c file | annotate | diff | comparison | revisions
pidgin/gtkprefs.c file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/64/scalable/auth.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/64/scalable/cool.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/64/scalable/dialog.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/64/scalable/error.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/64/scalable/info.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/64/scalable/mail.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/64/scalable/question.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/64/scalable/warning.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/scalable/auth.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/scalable/cool.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/scalable/dialog.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/scalable/error.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/scalable/info.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/scalable/mail.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/scalable/question.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/dialogs/scalable/warning.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/aol-client.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/birthday.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/blocked.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/bot.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/external.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/female.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/free-for-chat.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/game.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/male.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/mobile.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/music.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/not-authorized.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/qq-member.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/secure.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/unavailable.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/video.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/16/scalable/voice.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/aol-client.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/birthday.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/blocked.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/bot.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/external.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/female.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/free-for-chat.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/game.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/male.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/mobile.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/music.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/not-authorized.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/qq-member.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/secure.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/unavailable.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/video.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/emblems/scalable/voice.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/icons/hicolor/48x48/apps/scalable/pidgin.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/icons/hicolor/scalable/apps/pidgin.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/logo-nonvv.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/aim.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/bonjour.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/gadu-gadu.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/google-talk.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/icq.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/irc.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/jabber.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/meanwhile.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/msn.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/novell.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/qq.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/silc.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/simple.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/yahoo.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/48/scalable/zephyr.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/aim.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/bonjour.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/gadu-gadu.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/google-talk.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/icq.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/irc.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/jabber.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/meanwhile.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/msn.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/novell.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/qq.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/silc.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/simple.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/yahoo.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/protocols/scalable/zephyr.svg file | annotate | diff | comparison | revisions
pidgin/pixmaps/status/11/invisible.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/status/16/invisible.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/status/22/invisible.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/status/32/invisible.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/status/48/invisible.png file | annotate | diff | comparison | revisions
--- a/COPYRIGHT	Fri Aug 28 23:27:10 2009 +0000
+++ b/COPYRIGHT	Sat Aug 29 00:45:37 2009 +0000
@@ -48,6 +48,7 @@
 Dave Bell
 Igor Belyi
 Brian Bernas
+Vivien Bernet-Rollande
 Paul Betts
 Jonas Birmé
 George-Cristian Bîrzan
--- a/configure.ac	Fri Aug 28 23:27:10 2009 +0000
+++ b/configure.ac	Sat Aug 29 00:45:37 2009 +0000
@@ -1311,6 +1311,7 @@
 
 AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--disable-dbus], [disable D-Bus support])], , enable_dbus=yes)
 AC_ARG_ENABLE(nm, [AC_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes)
+AC_ARG_ENABLE(gnome-keyring, [AC_HELP_STRING([--disable-gnome-keyring], [disable GNOME Keyring support (requires D-Bus)])], enable_gnome_keyring=$enableval, enable_gnome_keyring=yes)
 
 if test "x$enable_dbus" = "xyes" ; then
 	AC_CHECK_PROG(enable_dbus, dbus-binding-tool, yes, no)
@@ -1347,10 +1348,33 @@
 ])
 			fi])
 	fi
+
+
+dnl Check for gnome-keyring.h; if we don't have it, oh well
+	if test "x$enable_gnome_keyring" = "xyes" ; then
+		PKG_CHECK_MODULES(GNOMEKEYRING, [gnome-keyring-1], [
+			AC_SUBST(GNOMEKEYRING_CFLAGS)
+			AC_SUBST(GNOMEKEYRING_LIBS)
+			AC_DEFINE(HAVE_GNOMEKEYRING, 1, [Define if we have GNOME Keyring.])
+		], [
+			if test "x$force_deps" = "xyes" ; then
+				AC_MSG_ERROR([
+GNOME Keyring development headers not found.
+Use --disable-gnome-keyring if you do not need GNOME Keyring support.
+])
+			fi])
+	fi
 else
+	enable_gnome_keyring=no
 	enable_nm=no
 fi
 
+AM_CONDITIONAL(ENABLE_GNOMEKEYRING, test "x$enable_gnome_keyring" = "xyes")
+
+
+
+
+
 dnl #######################################################################
 dnl # Check for Python
 dnl #######################################################################
@@ -2496,6 +2520,7 @@
 		   libpurple/purple.pc
 		   libpurple/purple-uninstalled.pc
 		   libpurple/plugins/Makefile
+		   libpurple/plugins/keyrings/Makefile
 		   libpurple/plugins/mono/Makefile
 		   libpurple/plugins/mono/api/Makefile
 		   libpurple/plugins/mono/loader/Makefile
@@ -2557,6 +2582,7 @@
 fi
 echo Build with GNU Libidn......... : $enable_idn
 echo Build with NetworkManager..... : $enable_nm
+echo Build with GNOME Keyring...... : $enable_gnome_keyring
 echo SSL Library/Libraries......... : $msg_ssl
 if test "x$SSL_CERTIFICATES_DIR" != "x" ; then
 	eval eval echo SSL CA certificates directory. : $SSL_CERTIFICATES_DIR
--- a/finch/gntaccount.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/finch/gntaccount.c	Sat Aug 29 00:45:37 2009 +0000
@@ -84,6 +84,10 @@
 /* This is necessary to close an edit-dialog when an account is deleted */
 static GList *accountdialogs;
 
+
+static void edit_account_continue(PurpleAccount * account, 
+	gchar * password, GError * error, gpointer user_data);
+
 static void
 account_add(PurpleAccount *account)
 {
@@ -174,9 +178,9 @@
 			gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->remember)));
 	value = gnt_entry_get_text(GNT_ENTRY(dialog->password));
 	if (value && *value)
-		purple_account_set_password(account, value);
+		purple_account_set_password_async(account, g_strdup(value), g_free, NULL, NULL);
 	else
-		purple_account_set_password(account, NULL);
+		purple_account_set_password_async(account, NULL, NULL, NULL, NULL);
 
 	/* Mail notification */
 	purple_account_set_check_mail(account,
@@ -485,6 +489,13 @@
 static void
 edit_account(PurpleAccount *account)
 {
+	purple_account_get_password_async(account, edit_account_continue, account);
+}
+
+static void
+edit_account_continue(PurpleAccount * account, 
+	gchar * password, GError * error, gpointer user_data)
+{
 	GntWidget *window, *hbox;
 	GntWidget *combo, *button, *entry;
 	GList *list, *iter;
@@ -566,7 +577,7 @@
 	gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Password:")));
 	gnt_box_add_widget(GNT_BOX(hbox), entry);
 	if (account)
-		gnt_entry_set_text(GNT_ENTRY(entry), purple_account_get_password(account));
+		gnt_entry_set_text(GNT_ENTRY(entry), password);
 
 	hbox = gnt_hbox_new(TRUE);
 	gnt_box_set_pad(GNT_BOX(hbox), 0);
--- a/finch/gntprefs.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/finch/gntprefs.c	Sat Aug 29 00:45:37 2009 +0000
@@ -1,6 +1,7 @@
 /**
  * @file gntprefs.c GNT Preferences API
  * @ingroup finch
+ * @todo : add support for master password changing.
  */
 
 /* finch
@@ -81,6 +82,13 @@
 } Prefs;
 
 static GList *
+get_keyring_options(void)
+{
+	return purple_keyring_get_options();
+}
+
+
+static GList *
 get_log_options(void)
 {
 	return purple_log_logger_get_options();
@@ -203,6 +211,11 @@
 	{PURPLE_PREF_NONE, NULL, NULL, NULL},
 };
 
+static Prefs keyring[] =
+{
+	{PURPLE_PREF_STRING, "/purple/keyring/active", N_("Active keyring"), purple_keyring_get_options}
+};
+
 static Prefs idle[] =
 {
 	{PURPLE_PREF_STRING, "/purple/away/idle_reporting", N_("Report Idle time"), get_idle_options},
@@ -258,6 +271,7 @@
 
 	add_pref_group(fields, _("Buddy List"), blist);
 	add_pref_group(fields, _("Conversations"), convs);
+	add_pref_group(fields, _("Keyring"), keyring);
 	add_pref_group(fields, _("Logging"), logging);
 	add_pref_group(fields, _("Idle"), idle);
 
--- a/libpurple/Makefile.am	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/Makefile.am	Sat Aug 29 00:45:37 2009 +0000
@@ -52,6 +52,7 @@
 	ft.c \
 	idle.c \
 	imgstore.c \
+	keyring.c \
 	log.c \
 	media.c \
 	mediamanager.c \
@@ -115,6 +116,7 @@
 	gaim-compat.h \
 	idle.h \
 	imgstore.h \
+	keyring.h \
 	log.h \
 	media.h \
 	media-gst.h \
--- a/libpurple/account.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/account.c	Sat Aug 29 00:45:37 2009 +0000
@@ -28,6 +28,7 @@
 #include "core.h"
 #include "dbus-maybe.h"
 #include "debug.h"
+#include "keyring.h"
 #include "network.h"
 #include "notify.h"
 #include "pounce.h"
@@ -41,6 +42,7 @@
 #include "util.h"
 #include "xmlnode.h"
 
+
 typedef struct
 {
 	PurpleConnectionErrorInfo *current_error;
@@ -78,6 +80,12 @@
 	guint ref;
 } PurpleAccountRequestInfo;
 
+typedef struct
+{
+	gpointer cb;
+	gpointer data;
+} CbInfo;
+
 static PurpleAccountUiOps *account_ui_ops = NULL;
 
 static GList   *accounts = NULL;
@@ -89,6 +97,20 @@
 static void set_current_error(PurpleAccount *account,
 	PurpleConnectionErrorInfo *new_err);
 
+static void purple_account_register_got_password_cb(PurpleAccount * account, 
+	char * password, GError * error, gpointer data);
+
+
+static void purple_account_unregister_got_password_cb(PurpleAccount * account,
+	char * password, GError * error, gpointer data);
+
+static void purple_account_connect_got_password_cb(PurpleAccount * account,
+       gchar * password, GError * error, gpointer data);
+
+static void purple_account_get_password_async_finish(PurpleAccount * account,
+	char * password, GError * error, gpointer data);
+
+static void purple_accounts_delete_finish(PurpleAccount * account, GError * error, gpointer data);
 /*********************************************************************
  * Writing to disk                                                   *
  *********************************************************************/
@@ -194,8 +216,7 @@
 		gboolean boolean_value = purple_value_get_boolean(attr_value);
 		if (boolean_value == purple_value_get_boolean(default_value))
 			return NULL;
-		value = g_strdup(boolean_value ?
-								"true" : "false");
+		value = g_strdup(boolean_value ? "true" : "false");
 	}
 	else
 	{
@@ -364,8 +385,13 @@
 
 	xmlnode *node, *child;
 	const char *tmp;
+	const char *keyring_id;
+	const char *mode;
+	char *data;
 	PurplePresence *presence;
 	PurpleProxyInfo *proxy_info;
+	GError * error = NULL;
+	GDestroyNotify destroy;
 
 	node = xmlnode_new("account");
 
@@ -375,11 +401,34 @@
 	child = xmlnode_new_child(node, "name");
 	xmlnode_insert_data(child, purple_account_get_username(account), -1);
 
-	if (purple_account_get_remember_password(account) &&
-		((tmp = purple_account_get_password(account)) != NULL))
+	if (purple_account_get_remember_password(account))
 	{
-		child = xmlnode_new_child(node, "password");
-		xmlnode_insert_data(child, tmp, -1);
+		purple_debug_info("accounts", "Exporting password for account %s (%s).\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+
+		purple_keyring_export_password(account, &keyring_id, 
+			&mode, &data, &error, &destroy);
+
+		if (error != NULL) {
+
+			purple_debug_info("accounts",
+				"failed to export password for account %s : %s.\n",
+				purple_account_get_username(account),
+				error->message);
+
+		} else {
+			child = xmlnode_new_child(node, "password");
+			if (keyring_id != NULL)
+				xmlnode_set_attrib(child, "keyring_id", keyring_id);
+			if (mode != NULL)
+				xmlnode_set_attrib(child, "mode", mode);
+			if (data != NULL)
+				xmlnode_insert_data(child, data, -1);
+
+			if (destroy != NULL)
+				destroy(data);
+		}
 	}
 
 	if ((tmp = purple_account_get_alias(account)) != NULL)
@@ -455,6 +504,8 @@
 		return;
 	}
 
+	purple_debug_info("account", "Syncing accounts.\n");
+
 	node = accounts_to_xmlnode();
 	data = xmlnode_to_formatted_str(node, NULL);
 	purple_util_write_data_to_file("accounts.xml", data, -1);
@@ -803,7 +854,11 @@
 	xmlnode *child;
 	char *protocol_id = NULL;
 	char *name = NULL;
-	char *data;
+	const char *keyring_id = NULL;
+	const char *mode = NULL;
+	char *data = NULL;
+	gboolean result = FALSE;
+	GError * error = NULL;
 
 	child = xmlnode_get_child(node, "protocol");
 	if (child != NULL)
@@ -831,15 +886,6 @@
 	g_free(name);
 	g_free(protocol_id);
 
-	/* Read the password */
-	child = xmlnode_get_child(node, "password");
-	if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
-	{
-		purple_account_set_remember_password(ret, TRUE);
-		purple_account_set_password(ret, data);
-		g_free(data);
-	}
-
 	/* Read the alias */
 	child = xmlnode_get_child(node, "alias");
 	if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
@@ -912,6 +958,26 @@
 		parse_current_error(child, ret);
 	}
 
+	/* Read the password */
+
+	child = xmlnode_get_child(node, "password");
+	if (child != NULL)
+	{
+		keyring_id = xmlnode_get_attrib(child, "keyring_id");
+		mode = xmlnode_get_attrib(child, "mode");
+		data = xmlnode_get_data(child);
+
+		result = purple_keyring_import_password(ret, keyring_id, mode, data, &error);
+
+		if (result == TRUE) {
+			purple_debug_info("accounts", "password imported successfully.\n");
+			purple_account_set_remember_password(ret, TRUE);		
+		} else {
+			purple_debug_info("accounts", "failed to imported password.\n");
+		} 
+		g_free(data); 
+	}
+
 	return ret;
 }
 
@@ -1076,24 +1142,62 @@
 	purple_debug_info("account", "Registering account %s\n",
 					purple_account_get_username(account));
 
-	_purple_connection_new(account, TRUE, purple_account_get_password(account));
+	purple_keyring_get_password_async(account, purple_account_register_got_password_cb, NULL);
 }
 
+
+static void
+purple_account_register_got_password_cb(PurpleAccount * account, char * password, GError * error, gpointer data)
+{
+	g_return_if_fail(account != NULL);
+
+	_purple_connection_new(account, TRUE, password);
+}
+
+typedef struct
+{
+	PurpleAccountUnregistrationCb cb;
+	void *user_data;
+} UnregisterData;
+
 void
 purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data)
 {
+	UnregisterData * data;
+
 	g_return_if_fail(account != NULL);
 
 	purple_debug_info("account", "Unregistering account %s\n",
 					  purple_account_get_username(account));
 
-	_purple_connection_new_unregister(account, purple_account_get_password(account), cb, user_data);
+	data = g_new0(UnregisterData, 1);
+	data->cb = cb;
+	data->user_data = user_data;
+
+	purple_keyring_get_password_async(account, purple_account_unregister_got_password_cb, data);
+
+}
+
+static void
+purple_account_unregister_got_password_cb(PurpleAccount * account, char * password, GError * error, gpointer data)
+{
+	UnregisterData * unregdata;
+	
+	g_return_if_fail(account != NULL);
+
+	unregdata = data;
+	g_return_if_fail(unregdata != NULL);
+
+	_purple_connection_new_unregister(account, password, unregdata->cb, unregdata->user_data);
+
+	g_free(unregdata);
 }
 
 static void
 request_password_ok_cb(PurpleAccount *account, PurpleRequestFields *fields)
 {
 	const char *entry;
+	char * password;
 	gboolean remember;
 
 	entry = purple_request_fields_get_string(fields, "password");
@@ -1105,12 +1209,16 @@
 		return;
 	}
 
-	if(remember)
-		purple_account_set_remember_password(account, TRUE);
-
-	purple_account_set_password(account, entry);
-
+	if(!remember)
+		purple_keyring_set_password_async(account, NULL, NULL, NULL, NULL);
+
+	purple_account_set_remember_password(account, remember);
+
+	password = g_strdup(entry);
+	purple_account_set_password(account, password);
+	g_free(password);
 	_purple_connection_new(account, FALSE, entry);
+
 }
 
 static void
@@ -1193,7 +1301,20 @@
 	purple_debug_info("account", "Connecting to account %s.\n", username);
 
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-	password = purple_account_get_password(account);
+	purple_keyring_get_password_async(account, purple_account_connect_got_password_cb, prpl_info);
+
+}
+
+static void
+purple_account_connect_got_password_cb(PurpleAccount * account,
+				       gchar * password,
+				       GError * error,
+				       gpointer data)
+{
+	PurplePluginProtocolInfo *prpl_info;
+
+	prpl_info = data;
+
 	if ((password == NULL) &&
 		!(prpl_info->options & OPT_PROTO_NO_PASSWORD) &&
 		!(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL))
@@ -1219,10 +1340,8 @@
 
 	gc = purple_account_get_connection(account);
 	_purple_connection_destroy(gc);
-	if (!purple_account_get_remember_password(account))
-		purple_account_set_password(account, NULL);
+
 	purple_account_set_connection(account, NULL);
-
 	account->disconnecting = FALSE;
 }
 
@@ -1556,14 +1675,52 @@
 void
 purple_account_set_password(PurpleAccount *account, const char *password)
 {
+	schedule_accounts_save();
+
 	g_return_if_fail(account != NULL);
 
 	g_free(account->password);
+
 	account->password = g_strdup(password);
 
+	if (purple_account_get_remember_password(account) == TRUE)
+		purple_keyring_set_password_async(account, g_strdup(password), g_free, NULL, NULL);
+}
+
+void 
+purple_account_set_password_async(PurpleAccount * account, 
+				  gchar * password,
+				  GDestroyNotify destroypassword,
+				  PurpleKeyringSaveCallback cb,
+				  gpointer data)
+{
+	/**
+	 * This is so we can force an account sync by calling
+	 * it with account == NULL.
+	 */
 	schedule_accounts_save();
+
+	g_return_if_fail(account != NULL);
+
+	g_free(account->password);
+
+	account->password = g_strdup(password);
+
+	if (purple_account_get_remember_password(account) == FALSE) {
+
+		account->password = g_strdup(password);
+		purple_debug_info("account",
+			"Password for %s set, not sent to keyring.\n",
+			purple_account_get_username(account));
+
+		if (cb != NULL)
+			cb(account, NULL, data);
+
+	} else {
+		purple_keyring_set_password_async(account, password,
+			destroypassword, cb, data);
+	}
 }
-
 void
 purple_account_set_alias(PurpleAccount *account, const char *alias)
 {
@@ -1960,13 +2117,92 @@
 	return account->username;
 }
 
+#ifndef PURPLE_DISABLE_DEPRECATED
+/* XXX will be replaced by the async code in 3.0 */
 const char *
-purple_account_get_password(const PurpleAccount *account)
+purple_account_get_password(PurpleAccount *account)
 {
 	g_return_val_if_fail(account != NULL, NULL);
 
+	if (account->password == NULL) {
+		purple_debug_info("accounts",
+			"Reading password for account %s (%s) from sync keyring.\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+
+		account->password = g_strdup(purple_keyring_get_password_sync(account));
+
+	} else {
+		purple_debug_info("accounts",
+			"Reading password for account %s (%s) from cached.\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+	}
+
 	return account->password;
 }
+#endif
+
+void
+purple_account_get_password_async(PurpleAccount * account,
+				  PurpleKeyringReadCallback cb,
+				  gpointer data)
+{
+	CbInfo * info = g_new0(CbInfo,1);
+	info->cb = cb;
+	info->data = data;
+
+	if(account == NULL) {
+		cb(NULL, NULL, NULL, data);
+		return;
+	}
+
+	if (account->password != NULL) {
+		purple_debug_info("accounts",
+			"Reading password for account %s (%s) from cached (async).\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+		cb(account, account->password, NULL, data);
+
+	} else {
+		purple_debug_info("accounts",
+			"Reading password for account %s (%s) from async keyring.\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+		purple_keyring_get_password_async(account, 
+			purple_account_get_password_async_finish, info);
+	}
+}
+
+static void
+purple_account_get_password_async_finish(PurpleAccount * account,
+					 char * password,
+					 GError * error,
+					 gpointer data)
+{
+	CbInfo * info;
+	PurpleKeyringReadCallback cb;
+
+	
+
+	purple_debug_info("accounts",
+		"Read password for account %s (%s) from async keyring.\n",
+		purple_account_get_username(account),
+		purple_account_get_protocol_id(account));
+
+	info = data;
+
+	g_return_if_fail(account != NULL);
+	g_free(account->password);
+	account->password = g_strdup(password);
+	g_return_if_fail(info != NULL);
+
+	cb = info->cb;
+	if (cb != NULL)
+		cb(account, password, error, info->data);
+
+	g_free(data);
+}
 
 const char *
 purple_account_get_alias(const PurpleAccount *account)
@@ -2412,7 +2648,7 @@
 	PurpleConnection *gc = purple_account_get_connection(account);
 	PurplePlugin *prpl = NULL;
 
-	purple_account_set_password(account, new_pw);
+	purple_account_set_password_async(account, g_strdup(new_pw), g_free, NULL, NULL);
 
 	if (gc != NULL)
 	        prpl = purple_connection_get_prpl(gc);
@@ -2620,7 +2856,18 @@
 	/* This will cause the deletion of an old buddy icon. */
 	purple_buddy_icons_set_account_icon(account, NULL, 0);
 
-	purple_account_destroy(account);
+	/* this is async because we do not want the
+	 * account overwritten before we are done.
+	 */
+	purple_keyring_set_password_async(account, NULL, NULL, 
+		purple_accounts_delete_finish, NULL);
+
+}
+
+static void
+purple_accounts_delete_finish(PurpleAccount * account, GError * error, gpointer data)
+{
+		purple_account_destroy(account);
 }
 
 void
--- a/libpurple/account.h	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/account.h	Sat Aug 29 00:45:37 2009 +0000
@@ -46,6 +46,7 @@
 #include "proxy.h"
 #include "prpl.h"
 #include "status.h"
+#include "keyring.h"
 
 /**
  * Account request types.
@@ -334,10 +335,33 @@
  *
  * @param account  The account.
  * @param password The password.
+ *
+ * This functions is just a wrapper for the async code anyway.
  */
 void purple_account_set_password(PurpleAccount *account, const char *password);
 
 /**
+ * Set the account's password, and call the callback
+ * This should be renamed purple_account_set_password() when getting
+ * to 3.0. This calls the keyring function and syncs the accounts.xml
+ *
+ * The password in the keyring might not be immediatly updated, but the cache
+ * version will be, and it is therefore safe to read the password back before
+ * the callback has been triggered. One can also set a NULL calback.
+ *
+ * @param account The account for which the password is to be saved.
+ * @param password The password to save.
+ * @param destroypassword A function called to free the password. Can be NULL.
+ * @param cb A callback for once the password is saved.
+ * @param data A pointer to be passed to the callback.
+ */
+void purple_account_set_password_async(PurpleAccount * account, 
+				  gchar * password,
+				  GDestroyNotify destroypassword,
+				  PurpleKeyringSaveCallback cb,
+				  gpointer data);
+
+/**
  * Sets the account's alias.
  *
  * @param account The account.
@@ -566,14 +590,32 @@
  */
 const char *purple_account_get_username(const PurpleAccount *account);
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Returns the account's password.
  *
  * @param account The account.
  *
  * @return The password.
+ *
+ * @deprecated This might return NULL if the password has not been cached yet,
+ * and the keyring doesn't support sync access. It might also hang libpurple
+ * while the keyring is prompting for a password. Use purple_account_get_password_async()
+ * or purple_connection_get_password() instead (depending on the part of the code you are
+ * calling from.
  */
-const char *purple_account_get_password(const PurpleAccount *account);
+const char *purple_account_get_password(PurpleAccount *account);
+#endif
+
+/**
+ * Reads the password for the account and passes it to the callback
+ *
+ * @param account The account to read the password for.
+ * @param cb The callback to pass the password to.
+ * @param data A pointer passed to the callback.
+ */
+void purple_account_get_password_async(PurpleAccount * account, 
+	PurpleKeyringReadCallback cb, gpointer data);
 
 /**
  * Returns the account's alias.
--- a/libpurple/connection.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/connection.c	Sat Aug 29 00:45:37 2009 +0000
@@ -458,12 +458,16 @@
 	return gc->prpl;
 }
 
+
+/**
+ * FIXME : all the calling tree needs to be async.
+ */
 const char *
 purple_connection_get_password(const PurpleConnection *gc)
 {
 	g_return_val_if_fail(gc != NULL, NULL);
 
-	return gc->password ? gc->password : gc->account->password;
+	return gc->password;
 }
 
 const char *
@@ -481,6 +485,14 @@
 	return connection->proto_data;
 }
 
+gboolean
+purple_connection_had_error(const PurpleConnection *gc)
+{
+	g_return_val_if_fail(gc != NULL, FALSE);
+
+	return gc->disconnect_timeout != 0;
+}
+
 void
 purple_connection_update_progress(PurpleConnection *gc, const char *text,
 								size_t step, size_t count)
@@ -517,18 +529,13 @@
 {
 	PurpleAccount *account;
 	PurpleConnection *gc;
-	char *password;
 
 	account = data;
 	gc = purple_account_get_connection(account);
 
 	gc->disconnect_timeout = 0;
 
-	password = g_strdup(purple_account_get_password(account));
 	purple_account_disconnect(account);
-	purple_account_set_password(account, password);
-	g_free(password);
-
 	return FALSE;
 }
 
--- a/libpurple/connection.h	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/connection.h	Sat Aug 29 00:45:37 2009 +0000
@@ -146,6 +146,7 @@
 #include <time.h>
 
 #include "account.h"
+#include "keyring.h"
 #include "plugin.h"
 #include "status.h"
 #include "sslconn.h"
@@ -416,7 +417,8 @@
 PurplePlugin * purple_connection_get_prpl(const PurpleConnection *gc);
 
 /**
- * Returns the connection's password.
+ * Returns the connection's password. Deprecated, use
+ * purple_connection_get_password_async() instead.
  *
  * @param gc The connection.
  *
@@ -445,6 +447,15 @@
 void *purple_connection_get_protocol_data(const PurpleConnection *connection);
 
 /**
+ * Returns if the connection had an error
+ *
+ * @param gc The connection.
+ *
+ * @return TRUE if the connection had an error, FALSE otherwise
+ */
+gboolean purple_connection_had_error(const PurpleConnection *gc);
+
+/**
  * Updates the connection progress.
  *
  * @param gc    The connection.
--- a/libpurple/core.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/core.c	Sat Aug 29 00:45:37 2009 +0000
@@ -35,6 +35,7 @@
 #include "ft.h"
 #include "idle.h"
 #include "imgstore.h"
+#include "keyring.h"
 #include "network.h"
 #include "notify.h"
 #include "plugin.h"
@@ -134,6 +135,7 @@
 
 	purple_ciphers_init();
 	purple_cmds_init();
+	purple_keyring_init();
 
 	/* Since plugins get probed so early we should probably initialize their
 	 * subsystem right away too.
--- a/libpurple/example/nullclient.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/example/nullclient.c	Sat Aug 29 00:45:37 2009 +0000
@@ -295,7 +295,7 @@
 
 	/* Get the password for the account */
 	password = getpass("Password: ");
-	purple_account_set_password(account, password);
+	purple_account_set_password_async(account, g_strdup(password), g_free, NULL, NULL);
 
 	/* It's necessary to enable the account first. */
 	purple_account_set_enabled(account, UI_ID, TRUE);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/keyring.c	Sat Aug 29 00:45:37 2009 +0000
@@ -0,0 +1,1176 @@
+/**
+ * @file keyring.c Keyring plugin API
+ * @ingroup core
+ * @todo ? : add dummy callback to all calls to prevent poorly written
+ * plugins from segfaulting on NULL callback ?
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program ; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include <glib.h>
+#include <string.h>
+#include "account.h"
+#include "keyring.h"
+#include "signals.h"
+#include "core.h"
+#include "debug.h"
+#include "internal.h"
+
+typedef struct _PurpleKeyringCbInfo PurpleKeyringCbInfo;
+typedef struct _PurpleKeyringChangeTracker PurpleKeyringChangeTracker;
+
+static void purple_keyring_pref_cb(const char *, PurplePrefType, gconstpointer, gpointer);
+static PurpleKeyring * purple_keyring_find_keyring_by_id(char * id);
+static void purple_keyring_drop_passwords(const PurpleKeyring * keyring);
+static void purple_keyring_set_inuse_check_error_cb(const PurpleAccount *,GError *,gpointer);
+static void purple_keyring_set_inuse_got_pw_cb(PurpleAccount *, gchar *, GError *, gpointer);
+static void purple_keyring_set_password_async_cb(PurpleAccount * account, GError * error, gpointer data);
+
+
+/******************************************/
+/** @name PurpleKeyring                   */
+/******************************************/
+/*@{*/
+
+struct _PurpleKeyring
+{
+	char * name;		/* a user friendly name	*/
+	char * id;		/* same as plugin id	*/
+	PurpleKeyringRead read_password;
+	PurpleKeyringSave save_password;
+	PurpleKeyringClose close_keyring;
+	PurpleKeyringChangeMaster change_master;
+	PurpleKeyringImportPassword import_password;
+	PurpleKeyringExportPassword export_password;
+	PurpleKeyringReadSync read_sync;
+	PurpleKeyringSaveSync save_sync;
+	gpointer r1;	/* RESERVED */
+	gpointer r2;	/* RESERVED */
+	gpointer r3;	/* RESERVED */
+};
+
+struct _PurpleKeyringChangeTracker
+{
+	GError * error;			/* could probably be dropped */
+	PurpleKeyringSetInUseCallback cb;
+	gpointer data;
+	const PurpleKeyring * new;
+	const PurpleKeyring * old;	/* we are done when : finished == TRUE && read_outstanding == 0 */
+	int read_outstanding;
+	gboolean finished;
+	gboolean abort;
+	gboolean force;
+};
+
+struct _PurpleKeyringCbInfo
+{
+	gpointer cb;
+	gpointer data;
+};
+
+/* Constructor */
+PurpleKeyring * 
+purple_keyring_new()
+{
+	return g_malloc0(sizeof(PurpleKeyring));
+}
+
+/* Destructor */
+void 
+purple_keyring_free(PurpleKeyring * keyring)
+{
+	g_free(keyring);
+	return;
+}
+
+/* Accessors */
+const char * 
+purple_keyring_get_name(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->name;
+}
+
+const char * 
+purple_keyring_get_id(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->id;
+}
+
+PurpleKeyringRead 
+purple_keyring_get_read_password(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->read_password;
+}
+
+PurpleKeyringSave 
+purple_keyring_get_save_password(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->save_password;
+}
+
+PurpleKeyringReadSync
+purple_keyring_get_read_sync(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->read_sync;
+}
+
+PurpleKeyringSaveSync
+purple_keyring_get_save_sync(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->save_sync;
+}
+
+PurpleKeyringClose 
+purple_keyring_get_close_keyring(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->close_keyring;
+}
+
+PurpleKeyringChangeMaster 
+purple_keyring_get_change_master(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->change_master;
+}
+
+PurpleKeyringImportPassword 
+purple_keyring_get_import_password(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->import_password;
+}
+
+PurpleKeyringExportPassword 
+purple_keyring_get_export_password(const PurpleKeyring * keyring)
+{
+	g_return_val_if_fail(keyring != NULL, NULL);
+
+	return keyring->export_password;
+}
+
+void 
+purple_keyring_set_name(PurpleKeyring * keyring, char * name)
+{
+	g_return_if_fail(keyring != NULL);
+
+	g_free(keyring->name);
+	keyring->name = g_strdup(name);
+
+	return;
+}
+
+void 
+purple_keyring_set_id(PurpleKeyring * keyring, char * id)
+{
+	g_return_if_fail(keyring != NULL);
+
+	g_free(keyring->id);
+	keyring->id = g_strdup(id);
+
+	return;
+}
+
+void
+purple_keyring_set_read_password(PurpleKeyring * keyring, PurpleKeyringRead read)
+{
+	g_return_if_fail(keyring != NULL);
+
+	keyring->read_password = read;
+
+	return;
+}
+
+void
+purple_keyring_set_save_password(PurpleKeyring * keyring, PurpleKeyringSave save)
+{
+	g_return_if_fail(keyring != NULL);
+
+	keyring->save_password = save;
+
+	return;
+}
+
+void 
+purple_keyring_set_read_sync(PurpleKeyring * keyring, PurpleKeyringReadSync read)
+{
+	g_return_if_fail(keyring != NULL);
+
+	keyring->read_sync = read;
+
+	return;
+}
+
+void 
+purple_keyring_set_save_sync(PurpleKeyring * keyring, PurpleKeyringSaveSync save)
+{
+	g_return_if_fail(keyring != NULL);
+
+	keyring->save_sync = save;
+
+	return;
+}
+
+void
+purple_keyring_set_close_keyring(PurpleKeyring * keyring, PurpleKeyringClose close)
+{
+	g_return_if_fail(keyring != NULL);
+
+	keyring->close_keyring = close;
+
+	return;
+}
+
+void
+purple_keyring_set_change_master(PurpleKeyring * keyring, PurpleKeyringChangeMaster change)
+{
+	g_return_if_fail(keyring != NULL);
+
+	keyring->change_master = change;
+
+	return;
+}
+
+void
+purple_keyring_set_import_password(PurpleKeyring * keyring, PurpleKeyringImportPassword import)
+{
+	g_return_if_fail(keyring != NULL);
+
+	keyring->import_password = import;
+
+	return;
+}
+
+void
+purple_keyring_set_export_password(PurpleKeyring * keyring, PurpleKeyringExportPassword export)
+{
+	g_return_if_fail(keyring != NULL);
+
+	keyring->export_password = export;
+
+	return;
+}
+
+/*@}*/
+
+
+/***************************************/
+/** @name Keyring API                  */
+/***************************************/
+/*@{*/
+
+static GList * purple_keyring_keyrings;		/* list of available keyrings   */
+static const PurpleKeyring * purple_keyring_inuse;	/* keyring being used	        */
+static char * purple_keyring_to_use;
+static guint purple_keyring_pref_cb_id;
+
+void 
+purple_keyring_init()
+{
+	PurpleCore * core;
+	const char * touse;
+
+	/* Make sure we don't have junk */
+	purple_keyring_keyrings = NULL;
+	purple_keyring_inuse = NULL;
+
+	/* register signals */
+	core = purple_get_core();
+
+	purple_signal_register(core, "keyring-register",
+		purple_marshal_VOID__POINTER_POINTER, 
+		NULL, 2,
+		purple_value_new(PURPLE_TYPE_STRING),			/* keyring ID */
+		purple_value_new(PURPLE_TYPE_BOXED, "PurpleKeyring *")); /* a pointer to the keyring */
+
+	purple_signal_register(core, "keyring-unregister",
+		purple_marshal_VOID__POINTER_POINTER, 
+		NULL, 2,
+		purple_value_new(PURPLE_TYPE_STRING),			/* keyring ID */
+		purple_value_new(PURPLE_TYPE_BOXED, "PurpleKeyring *")); /* a pointer to the keyring */
+
+	/* see what keyring we want to use */
+	touse = purple_prefs_get_string("/purple/keyring/active");
+
+	if (touse == NULL) {
+		purple_prefs_add_none("/purple/keyring");
+		purple_prefs_add_string("/purple/keyring/active", FALLBACK_KEYRING);
+		purple_keyring_to_use = g_strdup(FALLBACK_KEYRING);
+
+	} else {
+
+		purple_keyring_to_use = g_strdup(touse);
+	}
+
+	purple_keyring_pref_cb_id = purple_prefs_connect_callback(NULL, 
+		"/purple/keyring/active", purple_keyring_pref_cb, NULL);
+
+	purple_debug_info("keyring", "purple_keyring_init() done, selected keyring is : %s.\n",
+		purple_keyring_to_use);
+
+	return;
+}
+
+void
+purple_keyring_uninit()
+{
+	g_free(purple_keyring_to_use);
+	purple_debug_info("keyring", "purple_keyring_uninit() done.\n");
+}
+
+static PurpleKeyring *
+purple_keyring_find_keyring_by_id(char * id)
+{
+	GList * l;
+	PurpleKeyring * keyring;
+	const char * curr_id;
+
+	for (l = purple_keyring_keyrings; l != NULL; l = l->next) {
+		keyring = l->data;
+		curr_id = purple_keyring_get_id(keyring);
+
+		if (g_strcmp0(id, curr_id) == 0)
+			return keyring;
+	}
+
+	return NULL;
+}
+
+const GList * 
+purple_keyring_get_keyrings()
+{
+	return purple_keyring_keyrings;
+}
+
+const PurpleKeyring * 
+purple_keyring_get_inuse()
+{
+	return purple_keyring_inuse;
+}
+
+
+/* fire and forget */
+static void
+purple_keyring_drop_passwords(const PurpleKeyring * keyring)
+{
+	GList * cur;
+	PurpleKeyringSave save;
+
+	save = purple_keyring_get_save_password(keyring);
+
+	g_return_if_fail(save != NULL);
+
+	for (cur = purple_accounts_get_all();
+	     cur != NULL;
+	     cur = cur->next)
+		save(cur->data, NULL, NULL, NULL, NULL);
+
+	return;
+}
+
+
+
+static void
+purple_keyring_set_inuse_check_error_cb(const PurpleAccount * account,
+					GError * error,
+					gpointer data)
+{
+
+	const char * name;
+	PurpleKeyringClose close;
+	struct _PurpleKeyringChangeTracker * tracker;
+
+	tracker = (PurpleKeyringChangeTracker *)data;
+
+	g_return_if_fail(tracker->abort == FALSE);
+
+	tracker->read_outstanding--;
+
+	name = purple_account_get_username(account);;
+
+
+	if ((error != NULL) && (error->domain == ERR_PIDGINKEYRING)) {
+
+		tracker->error = error;
+
+		switch(error->code) {
+
+			case ERR_NOCAP :
+				purple_debug_info("keyring",
+					"Keyring could not save password for account %s : %s\n",
+					name, error->message);
+				break;
+
+			case ERR_NOPASSWD :
+				purple_debug_info("keyring",
+					"No password found while changing keyring for account %s : %s\n",
+					name, error->message);
+				break;
+
+			case ERR_NOCHANNEL :
+				purple_debug_info("keyring",
+					"Failed to communicate with backend while changing keyring for account %s : %s Aborting changes.\n",
+					name, error->message);
+				tracker->abort = TRUE;
+				break;
+
+			default :
+				purple_debug_info("keyring",
+					"Unknown error while changing keyring for account %s : %s\n",
+					name, error->message);
+				break;
+		}
+	}
+	/* if this was the last one */
+	if (tracker->finished == TRUE && tracker->read_outstanding == 0) {
+	
+		if (tracker->abort == TRUE && tracker->force == FALSE) {
+
+			if (tracker->cb != NULL)
+				tracker->cb(tracker->old, FALSE, tracker->error, tracker->data);
+
+			purple_keyring_drop_passwords(tracker->new);
+
+			close = purple_keyring_get_close_keyring(tracker->new);
+			if (close != NULL)
+				close(NULL);
+
+			purple_debug_info("keyring",
+				"Failed to change keyring, aborting");
+
+			purple_notify_error(NULL, _("Keyrings"), _("Failed to change the keyring."),
+				_("Aborting changes."));
+			purple_keyring_inuse = tracker->old;
+			purple_prefs_disconnect_callback(purple_keyring_pref_cb_id);
+			purple_prefs_set_string("/purple/keyring/active",
+				purple_keyring_get_id(tracker->old));
+			purple_keyring_pref_cb_id = purple_prefs_connect_callback(NULL, 
+				"/purple/keyring/active", purple_keyring_pref_cb, NULL);
+
+
+		} else {
+			close = purple_keyring_get_close_keyring(tracker->old);
+			if (close != NULL)
+				close(&error);
+
+			purple_keyring_drop_passwords(tracker->old);
+
+			purple_debug_info("keyring",
+				"Successfully changed keyring.\n");
+
+			if (tracker->cb != NULL)
+				tracker->cb(tracker->new, TRUE, error, tracker->data);
+		}
+
+		g_free(tracker);
+	}
+	/**
+	 * This is kind of hackish. It will schedule an account save,
+	 * and then return because account == NULL.
+	 * Another way to do this would be to expose the
+	 * schedule_accounts_save() function, but other such functions
+	 * are not exposed. So these was done for consistency.
+	 */
+	purple_account_set_password_async(NULL, NULL, NULL, NULL, NULL);
+
+	return;
+}
+
+
+static void
+purple_keyring_set_inuse_got_pw_cb(PurpleAccount * account, 
+				  gchar * password, 
+				  GError * error, 
+				  gpointer data)
+{
+	const PurpleKeyring * new;
+	PurpleKeyringSave save;
+	PurpleKeyringChangeTracker * tracker;
+
+	tracker = (PurpleKeyringChangeTracker *)data;
+	new = tracker->new;
+
+	g_return_if_fail(tracker->abort == FALSE);
+
+	if (error != NULL) {
+
+		if (error->code == ERR_NOPASSWD ||
+		    error->code == ERR_NOACCOUNT ||
+		    tracker->force == TRUE) {
+
+			/* don't save password, and directly trigger callback */
+			purple_keyring_set_inuse_check_error_cb(account, error, data);
+
+		} else {
+
+			/* fatal error, abort all */
+			tracker->abort = TRUE;
+		}
+
+	} else {
+
+
+		save = purple_keyring_get_save_password(new);
+
+		if (save != NULL) {
+			/* this test is probably totally useless, since there's no point
+			 * in having a keyring that can't store passwords, but it
+			 * will prevent crash with invalid keyrings
+			 */
+			save(account, password, NULL, 
+			     purple_keyring_set_inuse_check_error_cb, tracker);
+
+		} else {
+			error = g_error_new(ERR_PIDGINKEYRING , ERR_NOCAP,
+				"cannot store passwords in new keyring");
+			purple_keyring_set_inuse_check_error_cb(account, error, data);
+			g_error_free(error);
+		}
+	}
+
+	return;
+}
+
+
+
+
+void
+purple_keyring_set_inuse(const PurpleKeyring * newkeyring,
+			 gboolean force,
+			 PurpleKeyringSetInUseCallback cb,
+			 gpointer data)
+{
+	GList * cur;
+	const PurpleKeyring * oldkeyring;
+	PurpleKeyringRead read = NULL;
+	PurpleKeyringClose close;
+	PurpleKeyringChangeTracker * tracker;
+
+	if (newkeyring != NULL)
+		purple_debug_info("keyring", "Attempting to set new keyring : %s.\n",
+			newkeyring->id);
+	else
+		purple_debug_info("keyring", "Attempting to set new keyring : NULL.\n");
+
+	oldkeyring = purple_keyring_get_inuse();
+
+	if (oldkeyring != NULL) {
+		read = purple_keyring_get_read_password(oldkeyring);
+
+		if (read == NULL) {
+			/*
+			error = g_error_new(ERR_PIDGINKEYRING , ERR_NOCAP,
+				"Existing keyring cannot read passwords");
+			*/
+			purple_debug_info("keyring", "Existing keyring cannot read passwords");
+
+			/* at this point, we know the keyring won't let us
+			 * read passwords, so there no point in copying them.
+			 * therefore we just cleanup the old and setup the new 
+			 * one later.
+			 */
+
+			purple_keyring_drop_passwords(oldkeyring);
+
+			close = purple_keyring_get_close_keyring(oldkeyring);
+
+			if (close != NULL)
+				close(NULL);	/* we can't do much about errors at this point*/
+
+		} else {
+			tracker = g_malloc(sizeof(PurpleKeyringChangeTracker));
+			oldkeyring = purple_keyring_get_inuse();
+
+			purple_keyring_inuse = newkeyring;
+
+			tracker->cb = cb;
+			tracker->data = data;
+			tracker->new = newkeyring;
+			tracker->old = oldkeyring;
+			tracker->read_outstanding = 0;
+			tracker->finished = FALSE;
+			tracker->abort = FALSE;
+			tracker->force = force;
+			tracker->error = NULL;
+
+			for (cur = purple_accounts_get_all(); 
+			    (cur != NULL) && (tracker->abort == FALSE);
+			    cur = cur->next) {
+
+				tracker->read_outstanding++;
+
+				if (cur->next == NULL)
+					tracker->finished = TRUE;
+
+				read(cur->data, purple_keyring_set_inuse_got_pw_cb, tracker);
+			}
+		}
+
+	} else { /* no keyring was set before. */
+		purple_debug_info("keyring", "Setting keyring for the first time : %s.\n",
+			newkeyring->id);
+		purple_keyring_inuse = newkeyring;
+
+		if (cb != NULL)
+			cb(newkeyring, TRUE, NULL, data);
+
+		return;
+	}
+}
+
+
+
+static void 
+purple_keyring_pref_cb(const char *pref,
+		       PurplePrefType type,
+		       gconstpointer id,
+		       gpointer data)
+{
+	PurpleKeyring * new;
+
+	g_return_if_fail(g_strcmp0(pref, "/purple/keyring/active") == 0);
+	g_return_if_fail(type == PURPLE_PREF_STRING);
+	g_return_if_fail(id != NULL);
+
+	new = purple_keyring_get_keyring_by_id(id);
+	g_return_if_fail(new != NULL);
+
+	purple_keyring_set_inuse(new, FALSE, NULL, data);
+
+	return;
+}
+
+GList *
+purple_keyring_get_options()
+{
+	const GList * keyrings;
+	PurpleKeyring * keyring;
+	GList * list = NULL;
+
+	for (keyrings = purple_keyring_get_keyrings();
+	     keyrings != NULL;
+	     keyrings = keyrings->next) {
+
+		keyring = keyrings->data;
+		list = g_list_append(list, keyring->name);
+		list = g_list_append(list, keyring->id);
+		purple_debug_info("keyring", "adding pair : %s, %s.\n",
+			keyring->name, keyring->id);
+	}
+
+	return list;
+}
+
+PurpleKeyring *
+purple_keyring_get_keyring_by_id(const char * id)
+{
+	const GList * keyrings;
+	PurpleKeyring * keyring;
+
+	for (keyrings = purple_keyring_get_keyrings();
+	     keyrings != NULL;
+	     keyrings = keyrings->next) {
+
+		keyring = keyrings->data;
+		if (g_strcmp0(id, keyring->id) == 0)
+			return keyring;
+
+	}
+	return NULL;
+}
+
+
+
+void 
+purple_keyring_register(PurpleKeyring * keyring)
+{
+	const char * keyring_id;
+	PurpleCore * core;
+
+	g_return_if_fail(keyring != NULL);
+	
+	keyring_id = purple_keyring_get_id(keyring);
+
+	/* keyring with no ID. Add error handling ? */
+	g_return_if_fail(keyring_id != NULL);
+
+
+	purple_debug_info("keyring", "Registering keyring : %s\n",
+		keyring->id);
+
+	/* If this is the configured keyring, use it. */
+	if (purple_keyring_inuse == NULL &&
+	    g_strcmp0(keyring_id, purple_keyring_to_use) == 0) {
+
+		purple_debug_info("keyring", "Keyring %s matches keyring to use, using it.\n",
+			keyring->id);
+		purple_keyring_set_inuse(keyring, TRUE, NULL, NULL);
+
+	}
+
+	core = purple_get_core();
+
+	purple_signal_emit(core, "keyring-register", keyring_id, keyring);
+	purple_debug_info("keyring", "registered keyring : %s.\n", keyring_id);
+
+	purple_keyring_keyrings = g_list_prepend(purple_keyring_keyrings,
+		keyring);
+}
+
+
+void 
+purple_keyring_unregister(PurpleKeyring * keyring)
+{
+	PurpleCore * core;
+	const PurpleKeyring * inuse;
+	PurpleKeyring * fallback;
+	const char * keyring_id;
+
+	g_return_if_fail(keyring != NULL);
+	
+	purple_debug_info("keyring", 
+		"Unregistering keyring %s",
+		purple_keyring_get_id(keyring));
+
+	core = purple_get_core();
+	keyring_id = purple_keyring_get_id(keyring);
+	purple_signal_emit(core, "keyring-unregister", keyring_id, keyring);
+
+	inuse = purple_keyring_get_inuse();
+	fallback = purple_keyring_find_keyring_by_id(FALLBACK_KEYRING);
+
+	if (inuse == keyring) {
+		if (inuse != fallback) {
+			purple_keyring_set_inuse(fallback, TRUE, NULL, NULL);
+
+		} else {
+			fallback = NULL;
+			purple_keyring_set_inuse(NULL, TRUE, NULL, NULL);
+		}
+	}
+
+	purple_keyring_keyrings = g_list_remove(purple_keyring_keyrings,
+		keyring);
+
+	purple_debug_info("keyring", "Keyring %s unregistered", keyring->id);
+}
+
+
+/*@}*/
+
+
+/***************************************/
+/** @name Keyring plugin wrappers      */
+/***************************************/
+/*@{*/
+
+
+gboolean
+purple_keyring_import_password(PurpleAccount * account, 
+			       const char * keyringid,
+			       const char * mode,
+			       const char * data,
+			       GError ** error)
+{
+	const PurpleKeyring * inuse;
+	PurpleKeyringImportPassword import;
+	const char * realid;
+
+	purple_debug_info("keyring", "Importing password for account %s (%s) to keyring %s.\n",
+		purple_account_get_username(account),
+		purple_account_get_protocol_id(account),
+		keyringid);
+
+	inuse = purple_keyring_get_inuse();
+
+	if (inuse == NULL) {
+		*error = g_error_new(ERR_PIDGINKEYRING , ERR_NOKEYRING,
+			"No keyring configured, cannot import password info");
+		purple_debug_info("Keyring", 
+			"No keyring configured, cannot import password info for account %s (%s).\n",
+			purple_account_get_username(account), purple_account_get_protocol_id(account));
+		return FALSE;
+	}
+
+	realid = purple_keyring_get_id(inuse);
+	/*
+	 * we want to be sure that either :
+	 *  - there is a keyringid specified and it matches the one configured
+	 *  - or the configured keyring is the fallback, compatible one.
+	 */
+	if ((keyringid != NULL && g_strcmp0(realid, keyringid) != 0) ||
+	    (keyringid == NULL && g_strcmp0(FALLBACK_KEYRING, realid))) {
+
+		*error = g_error_new(ERR_PIDGINKEYRING , ERR_INVALID,
+			"Specified keyring id does not match the configured one.");
+		purple_debug_info("keyring",
+			"Specified keyring id does not match the configured one (%s vs. %s). Data will be lost.\n",
+			keyringid, realid);
+		return FALSE;
+	}
+
+	import = purple_keyring_get_import_password(inuse);
+	if (import == NULL) {
+		*error = g_error_new(ERR_PIDGINKEYRING , ERR_NOCAP,
+			"Keyring cannot import password info.");
+		purple_debug_info("Keyring", "Configured keyring cannot import password info. This might be normal.");
+		return FALSE;
+	}
+	
+	return import(account, mode, data, error);
+}
+
+gboolean
+purple_keyring_export_password(PurpleAccount * account,
+			       const char ** keyringid,
+			       const char ** mode,
+			       char ** data,
+			       GError ** error,
+			       GDestroyNotify * destroy)
+{
+	const PurpleKeyring * inuse;
+	PurpleKeyringExportPassword export;
+
+	inuse = purple_keyring_get_inuse();
+
+	if (inuse == NULL) {
+		*error = g_error_new(ERR_PIDGINKEYRING , ERR_NOKEYRING,
+			"No keyring configured, cannot export password info");
+		purple_debug_info("keyring",
+			"No keyring configured, cannot export password info");
+		return FALSE;
+	}
+
+	*keyringid = purple_keyring_get_id(inuse);
+
+	purple_debug_info("keyring",
+		"Exporting password for account %s (%s) from keyring %s.\n",
+		purple_account_get_username(account),
+		purple_account_get_protocol_id(account),
+		*keyringid);
+
+	if (*keyringid == NULL) {
+		*error = g_error_new(ERR_PIDGINKEYRING , ERR_INVALID,
+			"Plugin does not have a keyring id");
+		purple_debug_info("keyring",
+			"Configured keyring does not have a keyring id, cannot export password");
+		return FALSE;
+	}
+
+	export = purple_keyring_get_export_password(inuse);
+
+	if (export == NULL) {
+		*error = g_error_new(ERR_PIDGINKEYRING , ERR_NOCAP,
+			"Keyring cannot export password info.");
+		purple_debug_info("keyring",
+			"Keyring cannot export password info. This might be normal");
+		return FALSE;
+	}
+
+	return export(account, mode, data, error, destroy);
+}
+
+
+/**
+ * This should be renamed purple_keyring_get_password() when getting
+ * to 3.0, while dropping purple_keyring_get_password_sync().
+ */
+void 
+purple_keyring_get_password_async(PurpleAccount * account,
+				  PurpleKeyringReadCallback cb,
+				  gpointer data)
+{
+	GError * error = NULL;
+	const PurpleKeyring * inuse;
+	PurpleKeyringRead read;
+
+	if (account == NULL) {
+		error = g_error_new(ERR_PIDGINKEYRING, ERR_INVALID,
+			"No account passed to the function.");
+
+		if (cb != NULL)
+			cb(account, NULL, error, data);
+
+		g_error_free(error);
+
+	} else {
+		inuse = purple_keyring_get_inuse();
+
+		if (inuse == NULL) {
+			error = g_error_new(ERR_PIDGINKEYRING, ERR_NOKEYRING,
+				"No keyring configured.");
+
+			if (cb != NULL)
+				cb(account, NULL, error, data);
+
+			g_error_free(error);
+
+		} else {
+			read = purple_keyring_get_read_password(inuse);
+
+			if (read == NULL) {
+				error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP,
+					"Keyring cannot read password.");
+
+				if (cb != NULL)
+					cb(account, NULL, error, data);
+
+				g_error_free(error);
+
+			} else {
+				read(account, cb, data);
+			}
+		}
+	}
+
+	return;
+}
+
+/**
+ * This should be renamed purple_keyring_set_password() when getting
+ * to 3.0, while dropping purple_keyring_set_password_sync().
+ */
+void 
+purple_keyring_set_password_async(PurpleAccount * account, 
+				  gchar * password,
+				  GDestroyNotify destroypassword,
+				  PurpleKeyringSaveCallback cb,
+				  gpointer data)
+{
+	GError * error = NULL;
+	const PurpleKeyring * inuse;
+	PurpleKeyringSave save;
+	PurpleKeyringCbInfo * cbinfo;
+
+	g_return_if_fail(account != NULL);
+
+	inuse = purple_keyring_get_inuse();
+	if (inuse == NULL) {
+		error = g_error_new(ERR_PIDGINKEYRING, ERR_NOKEYRING,
+			"No keyring configured.");
+		if (cb != NULL)
+			cb(account, error, data);
+		g_error_free(error);
+
+	} else {
+		save = purple_keyring_get_save_password(inuse);
+		if (save == NULL) {
+			error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP,
+				"Keyring cannot save password.");
+			if (cb != NULL)
+				cb(account, error, data);
+			g_error_free(error);
+
+		} else {
+			cbinfo = g_malloc(sizeof(PurpleKeyringCbInfo));
+			cbinfo->cb = cb;
+			cbinfo->data = data;
+			save(account, password, destroypassword,
+				purple_keyring_set_password_async_cb, data);
+		}
+	}
+	return;
+}
+
+static void 
+purple_keyring_set_password_async_cb(PurpleAccount * account, 
+				     GError * error,
+				     gpointer data)
+{
+	PurpleKeyringCbInfo * cbinfo;
+	PurpleKeyringSaveCallback cb;
+
+	g_return_if_fail(data != NULL);
+	g_return_if_fail(account != NULL);
+
+	cbinfo = data;
+	cb = cbinfo->cb;
+
+	if (error != NULL) {
+		purple_notify_error(NULL, _("Keyrings"),
+			_("Failed to save password in keyring."),
+			error->message);
+	}
+
+	if (cb != NULL)
+		cb(account, error, cbinfo->data);
+	g_free(data);
+}
+
+/**
+ * This should be dropped at 3.0 (it's here for compatibility)
+ */
+const char * 
+purple_keyring_get_password_sync(const PurpleAccount * account)
+{
+	PurpleKeyringReadSync read;
+	const PurpleKeyring * inuse;
+
+	g_return_val_if_fail(account != NULL, NULL);
+
+	purple_debug_info("keyring (_sync_)",
+		"Reading password for account %s (%s)",
+		purple_account_get_username(account),
+		purple_account_get_protocol_id(account));
+
+	inuse = purple_keyring_get_inuse();
+
+	if (inuse == NULL) {
+		return NULL;
+
+	} else {
+		read = purple_keyring_get_read_sync(inuse);
+
+		if (read == NULL)
+			return NULL;
+		else
+			return read(account);
+	}
+}
+
+/**
+ * This should be dropped at 3.0 (it's here for compatibility)
+ */
+void 
+purple_keyring_set_password_sync(PurpleAccount * account,
+				 const char *password)
+{
+	PurpleKeyringSaveSync save;
+	const PurpleKeyring * inuse;
+
+	purple_debug_info("keyring (_sync_)",
+		"Setting password for account %s (%s)",
+		purple_account_get_username(account),
+		purple_account_get_protocol_id(account));
+
+	g_return_if_fail(account != NULL);
+
+	inuse = purple_keyring_get_inuse();
+	if (inuse != NULL) {
+		save = purple_keyring_get_save_sync(inuse);
+
+		if (save != NULL)
+			return save(account, password);
+	}
+
+	/* schedule account save */
+	purple_account_set_password(NULL, NULL);
+
+	return;
+}
+
+void
+purple_keyring_close(PurpleKeyring * keyring,
+		     GError ** error)
+{
+	PurpleKeyringClose close;
+
+	if (keyring == NULL) {
+		*error = g_error_new(ERR_PIDGINKEYRING, ERR_INVALID,
+			"No keyring passed to the function.");
+
+		return;
+
+	} else {
+		close = purple_keyring_get_close_keyring(keyring);
+
+		if (close == NULL) {
+			*error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP,
+				"Keyring doesn't support being closed.");
+
+		} else {
+			close(error);
+
+		}
+	}
+
+	return;
+}
+
+
+void 
+purple_keyring_change_master(PurpleKeyringChangeMasterCallback cb,
+			     gpointer data)
+{
+	GError * error = NULL;
+	PurpleKeyringChangeMaster change;
+	const PurpleKeyring * inuse;
+
+	inuse = purple_keyring_get_inuse();
+	
+	if (inuse == NULL) {
+		error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP,
+			"Keyring doesn't support master passwords.");
+		if (cb)
+			cb(FALSE, error, data);
+		g_error_free(error);
+
+	} else {
+
+		change = purple_keyring_get_change_master(inuse);
+
+		if (change == NULL) {
+			error = g_error_new(ERR_PIDGINKEYRING, ERR_NOCAP,
+				"Keyring doesn't support master passwords.");
+			if (cb)
+				cb(FALSE, error, data);
+
+			g_error_free(error);
+
+		} else {
+			change(cb, data);
+
+		}
+	}
+	return;
+}
+
+/*@}*/
+
+
+/***************************************/
+/** @name Error Codes                  */
+/***************************************/
+/*@{*/
+
+GQuark purple_keyring_error_domain(void)
+{
+	return g_quark_from_static_string("libpurple keyring");
+}
+
+/*}@*/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/keyring.h	Sat Aug 29 00:45:37 2009 +0000
@@ -0,0 +1,476 @@
+/**
+ * @file keyring.h Keyring plugin API
+ * @ingroup core
+ *
+ * @todo 
+ *  - Offer a way to prompt the user for a password or for a password change.
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program ; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef _PURPLE_KEYRING_H_
+#define _PURPLE_KEYRING_H_
+
+#include <glib.h>
+#include "account.h"
+
+/**
+ * Default keyring
+ */
+#define FALLBACK_KEYRING	"core-scrouaf-internalkeyring"
+
+/*******************************************************/
+/** @name data structures and types                    */
+/*******************************************************/
+/*@{*/
+typedef struct _PurpleKeyring PurpleKeyring;				/* public (for now) */
+
+/*@}*/
+
+/**
+ * XXX maybe strip a couple GError* if they're not used,
+ * since they should only be interresting for the callback
+ *  --> ability to forward errors ?
+ *
+ */
+
+/********************************************************/
+/** @name Callbacks for basic keyring operation         */
+/********************************************************/
+/*@{*/
+
+/**
+ * Callback for once a password is read. If there was a problem, the password should
+ * be NULL, and the error set.
+ *
+ * @param account The account of which the password was asked.
+ * @param password The password that was read. This should be freed when the callback returns.
+ * @param error Error that could have occured. Must be freed by the calling function.
+ * @param data Data passed to the callback.
+ */
+typedef void (*PurpleKeyringReadCallback)(PurpleAccount * account,
+					  gchar * password,
+					  GError * error,
+					  gpointer data);
+
+/**
+ * Callback for once a password has been stored. If there was a problem, the error will be set.
+ *
+ * @param error Error that could have occured. Must be freed by the calling function.
+ * @param account The account of which the password was saved.
+ * @param data Data passed to the callback.
+ */
+typedef void (*PurpleKeyringSaveCallback)(PurpleAccount * account, 
+					  GError * error,
+					  gpointer data);
+
+/**
+ * Callback for once the master password for a keyring has been changed.
+ *
+ * @param result Will be TRUE if the password has been changed, false otherwise.
+ * @param error Error that has occured. Must be freed if non NULL.
+ * @param data Data passed to the callback.
+ */
+typedef void (*PurpleKeyringChangeMasterCallback)(gboolean result,
+						  GError * error,
+						  gpointer data);
+
+/**
+ * Callback for when we change the keyring.
+ *
+ * @param keyring The keyring that is in use.
+ * @param result TRUE if the keyring was changed, FALSE otherwise.
+ * @param error An error that might have occured.
+ * @param data A pointer to user supplied data.
+ */
+typedef void (*PurpleKeyringSetInUseCallback)(const PurpleKeyring * keyring,
+					      gboolean result,
+					      GError * error,
+					      gpointer data);
+
+/*@}*/
+
+/********************************************************/
+/** @name pointers to the keyring's functions           */
+/********************************************************/
+/*@{*/
+
+/**
+ * Read the password for an account
+ * @param account The account which's password we want.
+ * @param cb A callback to be used once the password is found.
+ * @param data Data to be passed to the callback.
+ */
+typedef void (*PurpleKeyringRead)(PurpleAccount * account,
+				  PurpleKeyringReadCallback cb,
+				  gpointer data);
+
+/**
+ * Store a password in the keyring.
+ * @param account The account for which the password is to be stored.
+ * @param password The password to be stored. This password will be freed once 
+ * the function returns, so one must take care to make a copy if they call other 
+ * async functions later. If the password is NULL, this means that the keyring
+ * should forget about that password.
+ * @param destroypassword A function that will be called to free the password.
+ * @param cb A callback to be called once the password is saved.
+ * @param data A pointer to be passed to the callback
+ */
+typedef void (*PurpleKeyringSave)(PurpleAccount * account,
+				  gchar * password,
+				  GDestroyNotify destroypassword,
+				  PurpleKeyringSaveCallback cb,
+				  gpointer data);
+
+/**
+ * Read a password in an sync way. This is only used for API
+ * compatibility, and plugins are not expected to support this.
+ * Also, this should be dropped in 3.0.
+ * @param account The account.
+ * @return The password for the account.
+ */
+typedef const char * (*PurpleKeyringReadSync)(const PurpleAccount * account);
+
+/**
+ * Read a password in an sync way. This is only used for API
+ * compatibility, and plugins are not expected to support this.
+ * Also, this should be dropped in 3.0.
+ * @param account The account.
+ * @param password The password to save.
+ */
+typedef void (*PurpleKeyringSaveSync)(PurpleAccount * account,
+				      const char * password);
+
+/**
+ * Close the keyring.
+ * This will be called so the keyring can do any cleanup it wants.
+ * @param error An error that may occur.
+ */
+typedef void (*PurpleKeyringClose)(GError ** error);
+
+/**
+ * Change the master password for the keyring. If NULL, it means the Keyring
+ * has no master password capabilities.
+ * @param error An error that may occur.
+ * @param cb A callback to call once the master password has been changed.
+ * @param data A pointer to pass to the callback.
+ */
+typedef void (*PurpleKeyringChangeMaster)(PurpleKeyringChangeMasterCallback cb,
+					  gpointer data);
+
+/**
+ * Import info on an XML node.
+ * This is not async because it is not meant to prompt for a master password and decrypt passwords.
+ * @param account The account.
+ * @param mode A keyring specific option that was stored. Can be NULL, will be freed when function returns.
+ * @param data Data that was stored, Can be NULL, will be freed when function returns (so copy it if you need it).
+ * @return TRUE on success, FALSE on failure.
+ */
+typedef gboolean (*PurpleKeyringImportPassword)(PurpleAccount * account, 
+					    const char * mode,
+					    const char * data,
+					    GError ** error);
+
+/**
+ * Export information that will be stored in an XML node.
+ * This is not async because it is not meant to prompt for a master password or encrypt passwords.
+ * @param account The account for which we want the info.
+ * @param mode An option field that can be used by the plugin. This is expected to be a static string.
+ * @param data The data to be stored in the XML node. This string will be freed using destroy() once not needed anymore.
+ * @param error Will be set if a problem occured.
+ * @param destroy A function to be called, if non NULL, to free data.
+ * @return TRUE on success, FALSE on failure.
+ */
+typedef gboolean (*PurpleKeyringExportPassword)(PurpleAccount * account,
+						const char ** mode,
+						char ** data,
+						GError ** error,
+						GDestroyNotify * destroy);
+
+
+/*@}*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/***************************************/
+/** @name Keyring API                  */
+/***************************************/
+/*@{*/
+
+/**
+ * Find a keyring from it's keyring id.
+ * @param id The id for the keyring.
+ * @return The keyring, or NULL if not found.
+ */
+PurpleKeyring * purple_keyring_get_keyring_by_id(const char * id);
+
+/**
+ * Get a list of id/name pairs (for preferences)
+ * @return The list.
+ */
+GList * purple_keyring_get_options(void);
+
+/**
+ * Prepare stuff at startup.
+ */
+void purple_keyring_init(void);
+
+/**
+ * Do some cleanup.
+ */
+void purple_keyring_uninit(void);
+
+/**
+ * Get the keyring list. Used by the UI.
+ */
+const GList * 
+purple_keyring_get_keyrings(void);
+
+/**
+ * Get the keyring being used.
+ */
+const PurpleKeyring * 
+purple_keyring_get_inuse(void);
+
+/**
+ * Set the keyring to use. This function will move all passwords from
+ * the old keyring to the new one. If it fails, it will cancel all changes,
+ * close the new keyring, and notify the callback. If it succeeds, it will
+ * remove all passwords from the old safe and close that safe.
+ * @param newkeyring The new keyring to use.
+ * @param error Error that may occur.
+ * @param cb The callback to call once the change is done.
+ * @param data A pointer that will be passed to the callback.
+ * @param force FALSE if the change can be cancelled. If this is TRUE and an 
+ * error occurs, data might be lost.
+ */
+void
+purple_keyring_set_inuse(const PurpleKeyring * newkeyring,
+			 gboolean force,
+			 PurpleKeyringSetInUseCallback cb,
+			 gpointer data);
+
+
+/**
+ * Register a keyring plugin.
+ * @param keyrint The keyring to register.
+ */
+void 
+purple_keyring_register(PurpleKeyring * keyring);
+
+/**
+ * Unregister a keyring plugin. In case the keyring is in use,
+ * passwords will be moved to a fallback safe, and the keyring
+ * to unregister will be properly closed.
+ * @param keyrint The keyring to unregister.
+ */
+void 
+purple_keyring_unregister(PurpleKeyring * keyring);
+
+/*@}*/
+
+
+/***************************************/
+/** @name Keyring plugin wrappers      */
+/***************************************/
+/*@{*/
+
+/**
+ * used by account.c while reading a password from xml.
+ * @param account The account.
+ * @param keyringid The plugin ID that was stored in the xml file. Can be NULL.
+ * @param mode A keyring specific option that was stored. Can be NULL.
+ * @param data Data that was stored, can be NULL.
+ * @return TRUE if the input was accepted, FALSE otherwise.
+ */
+gboolean purple_keyring_import_password(PurpleAccount * account, 
+				    const char * keyringid,
+				    const char * mode,
+				    const char * data,
+				    GError ** error);
+
+/**
+ * used by account.c while syncing accounts
+ * @param account The account for which we want the info.
+ * @param keyringid The plugin id to be stored in the XML node. This will be NULL or a string that can be considered static.
+ * @param mode An option field that can be used by the plugin. This will be NULL or a string that can be considered static.
+ * @param data The data to be stored in the XML node. This string must be freed using destroy() once not needed anymore if it is not NULL.
+ * @param error Will be set if a problem occured.
+ * @param destroy A function to be called, if non NULL, to free data.
+ * @return TRUE if the info was exported successfully, FALSE otherwise.
+ */
+gboolean
+purple_keyring_export_password(PurpleAccount * account,
+			       const char ** keyringid,
+			       const char ** mode,
+			       char ** data,
+			       GError ** error,
+			       GDestroyNotify * destroy);
+
+/**
+ * Read a password from the active safe.
+ * This should be renamed purple_keyring_get_password() when getting
+ * to 3.0, while dropping purple_keyring_get_password_sync().
+ * @param account The account for which we want the password.
+ * @param cb The callback to be called.
+ * @param data A pointer passed to the callback.
+ */
+void 
+purple_keyring_get_password_async(PurpleAccount * account,
+				  PurpleKeyringReadCallback cb,
+				  gpointer data);
+
+/**
+ * Set a password to be remembered.
+ * This should be renamed purple_keyring_set_password() when getting
+ * to 3.0, while dropping purple_keyring_set_password_sync().keyring
+ * @param account The account for which the password is to be saved.
+ * @param password The password to save.
+ * @param destroypassword A function called to free the password. Can be NULL.
+ * @param cb A callback for once the password is saved.
+ * @param data A pointer to be passed to the callback.
+ */
+void 
+purple_keyring_set_password_async(PurpleAccount * account, 
+				  gchar * password,
+				  GDestroyNotify destroypassword,
+				  PurpleKeyringSaveCallback cb,
+				  gpointer data);
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Read a password in a synchronous way.
+ *
+ * @param account The account for which we want the password.
+ *
+ * @return A pointer to the the password
+ *
+ * @deprecated This is here only for compatibility reasons. Keyrings
+ * are not expected to support this, and you should use 
+ * purple_keyring_get_password_async() instead.
+ */
+const char * 
+purple_keyring_get_password_sync(const PurpleAccount * account);
+#endif
+
+#ifndef PURPLE_DISABLE_DEPRECATED
+/**
+ * Save a password in a synchronous way.
+ *
+ * @param account The account for which we want the password.
+ * @param password The password to save.
+ *
+ * @deprecated This is here only for compatibility reasons. Keyrings are not
+ * expected to support this, and you should use
+ * purple_keyring_set_password_async() instead.
+ */
+void 
+purple_keyring_set_password_sync(PurpleAccount * account,
+				 const char *password);
+#endif
+
+/**
+ * Close a safe.
+ * @param keyring The safe to close.
+ * @param error Error that might occur.
+ */
+void
+purple_keyring_close(PurpleKeyring * keyring,
+		     GError ** error);
+
+/**
+ * Change the master password for a safe (if the safe supports it).
+ * @param cb A callback to notify once we're done.
+ * @param data A pointer to be passed to the callback.
+ */
+void 
+purple_keyring_change_master(PurpleKeyringChangeMasterCallback cb,
+			     gpointer data);
+
+/*@}*/
+
+/***************************************/
+/** @name PurpleKeyring Accessors      */
+/***************************************/
+/*@{*/
+
+PurpleKeyring * purple_keyring_new(void);
+void purple_keyring_free(PurpleKeyring * keyring);
+
+const char * purple_keyring_get_name(const PurpleKeyring * info);
+const char * purple_keyring_get_id(const PurpleKeyring * info);
+PurpleKeyringRead purple_keyring_get_read_password(const PurpleKeyring * info);
+PurpleKeyringSave purple_keyring_get_save_password(const PurpleKeyring * info);
+PurpleKeyringReadSync purple_keyring_get_read_sync(const PurpleKeyring * info);
+PurpleKeyringSaveSync purple_keyring_get_save_sync(const PurpleKeyring * info);
+PurpleKeyringClose purple_keyring_get_close_keyring(const PurpleKeyring * info);
+PurpleKeyringChangeMaster purple_keyring_get_change_master(const PurpleKeyring * info);
+PurpleKeyringImportPassword purple_keyring_get_import_password(const PurpleKeyring * info);
+PurpleKeyringExportPassword purple_keyring_get_export_password(const PurpleKeyring * info);
+
+void purple_keyring_set_name(PurpleKeyring * info, char * name);
+void purple_keyring_set_id(PurpleKeyring * info, char * id);
+void purple_keyring_set_read_sync(PurpleKeyring * info, PurpleKeyringReadSync read);
+void purple_keyring_set_save_sync(PurpleKeyring * info, PurpleKeyringSaveSync save);
+void purple_keyring_set_read_password(PurpleKeyring * info, PurpleKeyringRead read);
+void purple_keyring_set_save_password(PurpleKeyring * info, PurpleKeyringSave save);
+void purple_keyring_set_close_keyring(PurpleKeyring * info, PurpleKeyringClose close);
+void purple_keyring_set_change_master(PurpleKeyring * info, PurpleKeyringChangeMaster change_master);
+void purple_keyring_set_import_password(PurpleKeyring * info, PurpleKeyringImportPassword import_password);
+void purple_keyring_set_export_password(PurpleKeyring * info, PurpleKeyringExportPassword export_password);
+
+/*@}*/
+
+/***************************************/
+/** @name Error Codes                  */
+/***************************************/
+/*@{*/
+
+/**
+ * Error domain GQuark. 
+ * See @ref purple_keyring_error_domain .
+ */
+#define ERR_PIDGINKEYRING 	purple_keyring_error_domain()
+/** stuff here too */
+GQuark purple_keyring_error_domain(void);
+
+/** error codes for keyrings. */
+enum PurpleKeyringError
+{
+	ERR_OK = 0,		/**< no error. */
+	ERR_NOPASSWD = 1,	/**< no stored password. */
+	ERR_NOACCOUNT,		/**< account not found. */
+	ERR_WRONGPASS,		/**< user submitted wrong password when prompted. */
+	ERR_WRONGFORMAT,	/**< data passed is not in suitable format. */
+	ERR_NOKEYRING,		/**< no keyring configured. */
+	ERR_NOCHANNEL,		/**< failed to communicate with the backend */
+	ERR_INVALID,		/**< invalid input */
+	ERR_NOCAP,		/**< keyring doesn't support this */
+	ERR_UNKNOWN		/**< unknown error */
+};
+
+#ifdef __cplusplus
+}
+#endif
+/*}@*/
+#endif /* _PURPLE_KEYRING_H_ */
--- a/libpurple/plugin.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/plugin.c	Sat Aug 29 00:45:37 2009 +0000
@@ -492,6 +492,12 @@
 		}
 	}
 
+	if (plugin->info->flags & PURPLE_PLUGIN_FLAG_AUTOLOAD) {
+		purple_debug_info("plugins", "Loading autoload plugin %s\n",
+						plugin->path);
+		purple_plugin_load(plugin);
+	}
+
 	return plugin;
 #else
 	return NULL;
@@ -1444,7 +1450,7 @@
 			}
 
 			protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin,
-													(GCompareFunc)compare_prpl);
+								(GCompareFunc)compare_prpl);
 		}
 	}
 
--- a/libpurple/plugin.h	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/plugin.h	Sat Aug 29 00:45:37 2009 +0000
@@ -67,6 +67,7 @@
 #define PURPLE_PRIORITY_LOWEST  -9999
 
 #define PURPLE_PLUGIN_FLAG_INVISIBLE 0x01
+#define PURPLE_PLUGIN_FLAG_AUTOLOAD  0x02
 
 #define PURPLE_PLUGIN_MAGIC 5 /* once we hit 6.0.0 I think we can remove this */
 
--- a/libpurple/plugins/Makefile.am	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/plugins/Makefile.am	Sat Aug 29 00:45:37 2009 +0000
@@ -1,4 +1,4 @@
-DIST_SUBDIRS = mono perl ssl tcl
+DIST_SUBDIRS = mono perl ssl tcl keyrings
 
 if USE_PERL
 PERL_DIR = perl
@@ -20,7 +20,8 @@
 	$(MONO_DIR) \
 	$(PERL_DIR) \
 	ssl \
-	$(TCL_DIR)
+	$(TCL_DIR) \
+	keyrings
 
 plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/keyrings/Makefile.am	Sat Aug 29 00:45:37 2009 +0000
@@ -0,0 +1,54 @@
+EXTRA_DIST = \
+		Makefile.mingw
+
+plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+
+
+if ENABLE_GNOMEKEYRING
+
+gnomekeyring_la_LDFLAGS     = -module -avoid-version
+GKRSOURCES = gnomekeyring.c
+#GNOME_KEYRING_CFLAGS = -I/usr/include/gnome-keyring-1
+#GNOME_KEYRING_LIBS   = -lgnome-keyring
+GNOME_KEYRING_LA = gnomekeyring.la
+
+endif #ENABLE_GNOMEKEYRING
+
+internalkeyring_la_LDFLAGS  = -module -avoid-version
+IKSOURCES = internalkeyring.c
+
+if PLUGINS
+
+plugin_LTLIBRARIES = \
+	$(GNOME_KEYRING_LA)\
+	internalkeyring.la
+
+
+if ENABLE_GNOMEKEYRING
+
+gnomekeyring_la_SOURCES     = $(GKRSOURCES)
+gnomekeyring_la_LIBADD  = $(GLIB_LIBS) $(GNOMEKEYRING_LIBS)
+
+endif #ENABLE_GNOMEKEYRING
+
+internalkeyring_la_SOURCES  = $(IKSOURCES)
+internalkeyring_la_LIBADD   = $(GLIB_LIBS)
+
+endif #PLUGINS
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/libpurple \
+	-I$(top_builddir)/libpurple \
+	$(GLIB_CFLAGS) \
+	$(DEBUG_CFLAGS) \
+	$(PLUGIN_CFLAGS) \
+	$(GNOME_KEYRING_CFLAGS)
+
+if ENABLE_GNOMEKEYRING
+gnomekeyring_la_CFLAGS = $(AM_CPPFLAGS) $(GNOMEKEYRING_CFLAGS)
+endif #ENABLE_GNOMEKEYRING
+
+internalkeyring_la_CFLAGS = $(AM_CPPFLAGS)
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/keyrings/gnomekeyring.c	Sat Aug 29 00:45:37 2009 +0000
@@ -0,0 +1,525 @@
+/**
+ * @file gnomekeyring.c Gnome keyring password storage
+ * @ingroup plugins
+ *
+ * @todo 
+ *   cleanup error handling and reporting
+ *   refuse unloading when active (in internal keyring too)
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program ; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef PURPLE_PLUGINS
+# define PURPLE_PLUGINS
+#endif
+
+#include <glib.h>
+#include <gnome-keyring.h>
+
+#ifndef G_GNUC_NULL_TERMINATED
+# if __GNUC__ >= 4
+#  define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+# else
+#  define G_GNUC_NULL_TERMINATED
+# endif
+#endif
+
+#include "account.h"
+#include "version.h"
+#include "keyring.h"
+#include "debug.h"
+#include "plugin.h"
+#include "internal.h"
+
+#define GNOMEKEYRING_NAME		N_("Gnome-Keyring")
+#define GNOMEKEYRING_VERSION		"0.3b"
+#define GNOMEKEYRING_DESCRIPTION	N_("This plugin will store passwords in Gnome-Keyring.")
+#define	GNOMEKEYRING_AUTHOR		"Scrouaf (scrouaf[at]soc.pidgin.im)"
+#define GNOMEKEYRING_ID			"core-scrouaf-gnomekeyring"
+
+#define ERR_GNOMEKEYRINGPLUGIN 	gkp_error_domain()
+
+static PurpleKeyring * keyring_handler = NULL;
+
+typedef struct _InfoStorage InfoStorage;
+
+struct _InfoStorage
+{
+	gpointer cb;
+	gpointer user_data;
+	PurpleAccount * account;
+	char * name;
+};
+
+/* a few prototypes : */
+static GQuark 		gkp_error_domain(void);
+static void 		gkp_read(PurpleAccount *, PurpleKeyringReadCallback, gpointer);
+static void		gkp_read_continue(GnomeKeyringResult, const char *, gpointer);
+static void 		gkp_save(PurpleAccount *, gchar *, GDestroyNotify, PurpleKeyringSaveCallback, gpointer);
+static void		gkp_save_continue(GnomeKeyringResult, gpointer);
+static const char * 	gkp_read_sync(const PurpleAccount *);
+static void 		gkp_save_sync(PurpleAccount *, const gchar *);
+static void		gkp_close(GError **);
+static gboolean		gkp_import_password(PurpleAccount *, const char *, const char *, GError **);
+static gboolean 	gkp_export_password(PurpleAccount *, const char **, char **, GError **, GDestroyNotify *);
+static gboolean		gkp_init(void);
+static void		gkp_uninit(void);
+static gboolean		gkp_load(PurplePlugin *);
+static gboolean		gkp_unload(PurplePlugin *);
+static void		gkp_destroy(PurplePlugin *);
+
+static void		gkp_change_master(PurpleKeyringChangeMasterCallback cb, gpointer data);
+
+GQuark gkp_error_domain(void)
+{
+	return g_quark_from_static_string("Gnome-Keyring plugin");
+}
+
+
+/***********************************************/
+/*     Keyring interface                       */
+/***********************************************/
+static void 
+gkp_read(PurpleAccount * account,
+	 PurpleKeyringReadCallback cb,
+	 gpointer data)
+{
+	InfoStorage * storage = g_malloc(sizeof(InfoStorage));
+
+	storage->cb = cb;
+	storage->user_data = data;
+	storage->account = account;
+
+	gnome_keyring_find_password(GNOME_KEYRING_NETWORK_PASSWORD,
+				    gkp_read_continue,
+				    storage,
+				    g_free,
+				    "user", purple_account_get_username(account),
+				    "protocol", purple_account_get_protocol_id(account),
+				    NULL);
+}
+
+static void gkp_read_continue(GnomeKeyringResult result,
+                       const char *password,
+                       gpointer data)
+/* XXX : make sure list is freed on return */
+{
+	InfoStorage * storage = data;
+	PurpleAccount * account =storage->account;
+	PurpleKeyringReadCallback cb = storage->cb;
+	GError * error;
+	char * copy;
+
+	if (result != GNOME_KEYRING_RESULT_OK) {
+
+		switch(result)
+		{
+			case GNOME_KEYRING_RESULT_NO_MATCH :
+				error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, 
+					ERR_NOPASSWD, "no password found for account : %s",
+					purple_account_get_username(account));
+				if(cb != NULL)
+					cb(account, NULL, error, storage->user_data);
+				g_error_free(error);
+				return;
+
+			case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON :
+			case GNOME_KEYRING_RESULT_IO_ERROR :
+				error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, 
+					ERR_NOCHANNEL, "Failed to communicate with gnome keyring (account : %s).",
+					purple_account_get_username(account));
+				if(cb != NULL)
+					cb(account, NULL, error, storage->user_data);
+				g_error_free(error);
+				return;
+
+			default :
+				error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, 
+					ERR_NOCHANNEL, "Unknown error (account : %s).",
+					purple_account_get_username(account));
+				if(cb != NULL)
+					cb(account, NULL, error, storage->user_data);
+				g_error_free(error);
+				return;
+		}
+
+	} else {
+
+		if(cb != NULL) {
+			copy = g_strdup(password);
+			cb(account, copy, NULL, storage->user_data);
+			g_free(copy);
+		}
+		return;
+	}
+}
+
+
+static void
+gkp_save(PurpleAccount * account,
+	 gchar * password,
+	 GDestroyNotify destroy,
+	 PurpleKeyringSaveCallback cb,
+	 gpointer data)
+{
+	InfoStorage * storage = g_new0(InfoStorage,1);
+
+	storage->account = account;
+	storage->cb = cb;
+	storage->user_data = data;
+	storage->name = g_strdup_printf("pidgin-%s",
+		purple_account_get_username(account));
+
+	if(password != NULL && *password != '\0') {
+
+		purple_debug_info("Gnome keyring plugin",
+			"Updating password for account %s (%s).\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+
+		gnome_keyring_store_password(GNOME_KEYRING_NETWORK_PASSWORD,
+					     NULL, 	/*default keyring */
+					     storage->name,
+					     password,
+		                             gkp_save_continue,
+					     storage,
+					     g_free,		/* function to free storage */
+					     "user", purple_account_get_username(account),
+					     "protocol", purple_account_get_protocol_id(account),
+					     NULL);
+	
+		if (destroy)
+			destroy(password);
+
+	} else {	/* password == NULL, delete password. */
+
+		purple_debug_info("Gnome keyring plugin",
+			"Forgetting password for account %s (%s).\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+
+		gnome_keyring_delete_password(GNOME_KEYRING_NETWORK_PASSWORD,
+					      gkp_save_continue,
+					      storage,
+					      g_free,
+					      "user", purple_account_get_username(account),
+					      "protocol", purple_account_get_protocol_id(account),
+					      NULL);
+
+	}
+	return;
+}
+
+static void
+gkp_save_continue(GnomeKeyringResult result,
+            gpointer data)
+{
+	InfoStorage * storage;
+	PurpleKeyringSaveCallback cb;
+	GError * error;
+	PurpleAccount * account;
+
+	storage = data;
+	g_return_if_fail(storage != NULL);
+
+	cb = storage->cb;
+	account = storage->account;
+	
+	g_free(storage->name);
+
+	if (result != GNOME_KEYRING_RESULT_OK) {
+		switch(result)
+		{
+			case GNOME_KEYRING_RESULT_NO_MATCH :
+				purple_debug_info("Gnome keyring plugin",
+					"Could not update password for %s (%s) : not found.\n",
+					purple_account_get_username(account),
+					purple_account_get_protocol_id(account));
+				error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, 
+					ERR_NOPASSWD, "Could not update password for %s : not found",
+					purple_account_get_username(account));
+				if(cb != NULL)
+					cb(account, error, storage->user_data);
+				g_error_free(error);
+				return;
+
+			case GNOME_KEYRING_RESULT_NO_KEYRING_DAEMON :
+			case GNOME_KEYRING_RESULT_IO_ERROR :
+				purple_debug_info("Gnome keyring plugin",
+					"Failed to communicate with gnome keyring (account : %s (%s)).\n",
+					purple_account_get_username(account),
+					purple_account_get_protocol_id(account));
+				error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, 
+					ERR_NOCHANNEL, "Failed to communicate with gnome keyring (account : %s).",
+					purple_account_get_username(account));
+				if(cb != NULL)
+					cb(account, error, storage->user_data);
+				g_error_free(error);
+				return;
+
+			default :
+				purple_debug_info("Gnome keyring plugin",
+					"Unknown error (account : %s (%s)).\n",
+					purple_account_get_username(account),
+					purple_account_get_protocol_id(account));
+				error = g_error_new(ERR_GNOMEKEYRINGPLUGIN, 
+					ERR_NOCHANNEL, "Unknown error (account : %s).",
+					purple_account_get_username(account));
+				if(cb != NULL)
+					cb(account, error, storage->user_data);
+				g_error_free(error);
+				return;
+		}
+
+	} else {
+
+		purple_debug_info("gnome-keyring-plugin", "password for %s updated.\n",
+			purple_account_get_username(account));
+
+		if(cb != NULL)
+			cb(account, NULL, storage->user_data);
+
+		return;
+	
+	}
+}
+
+static const char * 
+gkp_read_sync(const PurpleAccount * account)
+/**
+ * This is tricky, since the calling function will not free the password. 
+ * Since we free the password on the next request, the last request will leak.
+ * But that part of the code shouldn't be used anyway. It might however be an issue
+ * if another call is made and the old pointer still used.
+ * Elegant solution would include a hashtable or a linked list, but would be
+ * complex. Also, this might just be dropped at 1.0 of the plugin
+ */
+{
+	GnomeKeyringResult result;
+	static char * password = NULL;
+
+	purple_debug_info("Gnome-Keyring plugin (_sync_)", "password for %s was read.\n",
+			purple_account_get_username(account));
+
+	gnome_keyring_free_password(password);
+
+	result = gnome_keyring_find_password_sync(GNOME_KEYRING_NETWORK_PASSWORD,
+		&password,
+		"user", purple_account_get_username(account),
+		"protocol", purple_account_get_protocol_id(account),
+		NULL);
+
+	return password;
+}
+
+static void
+gkp_save_sync(PurpleAccount * account, 
+	const char * password)
+{
+	const char * name;
+
+	if(password != NULL && *password != '\0') {
+
+		name =g_strdup_printf("pidgin-%s", purple_account_get_username(account)),
+
+		gnome_keyring_store_password_sync(GNOME_KEYRING_NETWORK_PASSWORD,
+			NULL, name, password, 
+			"user", purple_account_get_username(account),
+			"protocol", purple_account_get_protocol_id(account),
+			NULL);
+		purple_debug_info("GnomeKeyring plugin (_sync_)", 
+			"Updated password for account %s (%s).\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+
+	} else {
+		gnome_keyring_delete_password_sync(GNOME_KEYRING_NETWORK_PASSWORD,
+			"user", purple_account_get_username(account),
+			"protocol", purple_account_get_protocol_id(account),
+			NULL);
+		purple_debug_info("GnomeKeyring plugin (_sync_)", 
+			"Deleted password for account %s (%s).\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+	
+	}
+}
+
+static void
+gkp_close(GError ** error)
+{
+	return;
+}
+
+static gboolean
+gkp_import_password(PurpleAccount * account, 
+		    const char * mode,
+		    const char * data,
+		    GError ** error)
+{
+	purple_debug_info("Gnome Keyring plugin",
+		"Importing password.\n");
+	return TRUE;
+}
+
+static gboolean 
+gkp_export_password(PurpleAccount * account,
+				 const char ** mode,
+				 char ** data,
+				 GError ** error,
+				 GDestroyNotify * destroy)
+{
+	purple_debug_info("Gnome Keyring plugin",
+		"Exporting password.\n");
+	*data = NULL;
+	*mode = NULL;
+	*destroy = NULL;
+
+	return TRUE;
+}
+
+/* this was written just to test the pref change */
+static void
+gkp_change_master(PurpleKeyringChangeMasterCallback cb, gpointer data)
+{
+	purple_debug_info("Gnome-Keyring plugin",
+		"This keyring does not support master passwords.\n");
+
+	purple_notify_info(NULL, _("Gnome-Keyring plugin"), 
+			_("Failed to change master password."),
+			_("This plugin does not really support master passwords, it just pretends to."));
+	if(cb)
+		cb(FALSE, NULL, data);
+}
+
+static gboolean
+gkp_init()
+{
+	purple_debug_info("gnome-keyring-plugin", "init.\n");
+
+	if (gnome_keyring_is_available()) {
+
+		keyring_handler = purple_keyring_new();
+
+		purple_keyring_set_name(keyring_handler, GNOMEKEYRING_NAME);
+		purple_keyring_set_id(keyring_handler, GNOMEKEYRING_ID);
+		purple_keyring_set_read_sync(keyring_handler, gkp_read_sync);
+		purple_keyring_set_save_sync(keyring_handler, gkp_save_sync);
+		purple_keyring_set_read_password(keyring_handler, gkp_read);
+		purple_keyring_set_save_password(keyring_handler, gkp_save);
+		purple_keyring_set_close_keyring(keyring_handler, gkp_close);
+		purple_keyring_set_change_master(keyring_handler, gkp_change_master);
+		purple_keyring_set_import_password(keyring_handler, gkp_import_password);
+		purple_keyring_set_export_password(keyring_handler, gkp_export_password);
+
+		purple_keyring_register(keyring_handler);
+
+		return TRUE;
+
+	} else {
+
+		purple_debug_info("gnome-keyring-plugin",
+			"failed to communicate with daemon, not loading.");
+		return FALSE;
+	}
+}
+
+static void
+gkp_uninit()
+{
+	purple_debug_info("gnome-keyring-plugin", "uninit.\n");
+	gkp_close(NULL);
+	purple_keyring_unregister(keyring_handler);
+	purple_keyring_free(keyring_handler);
+	keyring_handler = NULL;
+}
+
+
+
+/***********************************************/
+/*     Plugin interface                        */
+/***********************************************/
+
+static gboolean
+gkp_load(PurplePlugin *plugin)
+{
+	return gkp_init();
+}
+
+static gboolean
+gkp_unload(PurplePlugin *plugin)
+{
+	gkp_uninit();
+	return TRUE;
+}
+
+static void
+gkp_destroy(PurplePlugin *plugin)
+{
+	gkp_uninit();
+	return;
+}
+
+PurplePluginInfo plugininfo =
+{
+	PURPLE_PLUGIN_MAGIC,						/* magic */
+	PURPLE_MAJOR_VERSION,						/* major_version */
+	PURPLE_MINOR_VERSION,						/* minor_version */
+	PURPLE_PLUGIN_STANDARD,						/* type */
+	NULL,								/* ui_requirement */
+	PURPLE_PLUGIN_FLAG_INVISIBLE|PURPLE_PLUGIN_FLAG_AUTOLOAD,	/* flags */
+	NULL,								/* dependencies */
+	PURPLE_PRIORITY_DEFAULT,					/* priority */
+	GNOMEKEYRING_ID,						/* id */
+	GNOMEKEYRING_NAME,						/* name */
+	GNOMEKEYRING_VERSION,					/* version */
+	"Internal Keyring Plugin",					/* summary */
+	GNOMEKEYRING_DESCRIPTION,					/* description */
+	GNOMEKEYRING_AUTHOR,						/* author */
+	"N/A",								/* homepage */
+	gkp_load,						/* load */
+	gkp_unload,					/* unload */
+	gkp_destroy,					/* destroy */
+	NULL,								/* ui_info */
+	NULL,								/* extra_info */
+	NULL,								/* prefs_info */
+	NULL,								/* actions */
+	NULL,								/* padding... */
+	NULL,
+	NULL,
+	NULL,
+};
+
+static void                        
+init_plugin(PurplePlugin *plugin)
+{                                  
+	purple_debug_info("Gnome Keyring plugin", "init plugin called.\n");
+}
+
+PURPLE_INIT_PLUGIN(gnome_keyring, init_plugin, plugininfo)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/keyrings/internalkeyring.c	Sat Aug 29 00:45:37 2009 +0000
@@ -0,0 +1,370 @@
+/**
+ * @file internalkeyring.c internal keyring
+ * @ingroup plugins
+ *
+ * @todo 
+ *   cleanup error handling and reporting
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program ; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef PURPLE_PLUGINS
+# define PURPLE_PLUGINS
+#endif
+
+#include <glib.h>
+
+#ifndef G_GNUC_NULL_TERMINATED
+# if __GNUC__ >= 4
+#  define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+# else
+#  define G_GNUC_NULL_TERMINATED
+# endif
+#endif
+
+#include "account.h"
+#include "version.h"
+#include "keyring.h"
+#include "debug.h"
+#include "plugin.h"
+#include "internal.h"
+
+#define INTERNALKEYRING_NAME		N_("Internal keyring")
+#define INTERNALKEYRING_VERSION		"0.8b"
+#define INTERNALKEYRING_DESCRIPTION	N_("This plugin provides the default password storage behaviour for libpurple. Password will be stored unencrypted.")
+#define	INTERNALKEYRING_AUTHOR		"Scrouaf (scrouaf[at]soc.pidgin.im)"
+#define INTERNALKEYRING_ID		FALLBACK_KEYRING
+
+
+#define GET_PASSWORD(account) \
+	g_hash_table_lookup (internal_keyring_passwords, account)
+#define SET_PASSWORD(account, password) \
+	g_hash_table_replace(internal_keyring_passwords, account, password)
+#define ACTIVATE()\
+	if (internal_keyring_is_active == FALSE)\
+		internal_keyring_open();	
+	
+
+static GHashTable * internal_keyring_passwords = NULL;
+static PurpleKeyring * keyring_handler = NULL;
+static gboolean internal_keyring_is_active = FALSE;
+
+/* a few prototypes : */
+static void 		internal_keyring_read(PurpleAccount *, PurpleKeyringReadCallback, gpointer);
+static void 		internal_keyring_save(PurpleAccount *, gchar *, GDestroyNotify, PurpleKeyringSaveCallback, gpointer);
+static const char * 	internal_keyring_read_sync(const PurpleAccount *);
+static void 		internal_keyring_save_sync(PurpleAccount *, const gchar *);
+static void		internal_keyring_close(GError **);
+static void		internal_keyring_open(void);
+static gboolean		internal_keyring_import_password(PurpleAccount *, const char *, const char *, GError **);
+static gboolean 	internal_keyring_export_password(PurpleAccount *, const char **, char **, GError **, GDestroyNotify *);
+static void		internal_keyring_init(void);
+static void		internal_keyring_uninit(void);
+static gboolean		internal_keyring_load(PurplePlugin *);
+static gboolean		internal_keyring_unload(PurplePlugin *);
+static void		internal_keyring_destroy(PurplePlugin *);
+
+/***********************************************/
+/*     Keyring interface                       */
+/***********************************************/
+static void 
+internal_keyring_read(PurpleAccount * account,
+		      PurpleKeyringReadCallback cb,
+		      gpointer data)
+{
+	char * password;
+	GError * error;
+
+	ACTIVATE();
+
+	purple_debug_info("Internal Keyring",
+		"Reading password for account %s (%s).\n",
+		purple_account_get_username(account),
+		purple_account_get_protocol_id(account));
+
+	password = GET_PASSWORD(account);
+
+	if (password != NULL) {
+		if(cb != NULL)
+			cb(account, password, NULL, data);
+	} else {
+		error = g_error_new(ERR_PIDGINKEYRING, 
+			ERR_NOPASSWD, "password not found");
+		if(cb != NULL)
+			cb(account, NULL, error, data);
+		g_error_free(error);
+	}
+	return;
+}
+
+static void
+internal_keyring_save(PurpleAccount * account,
+		      gchar * password,
+		      GDestroyNotify destroy,
+		      PurpleKeyringSaveCallback cb,
+		      gpointer data)
+{
+	gchar * copy;
+
+	ACTIVATE();
+
+	if (password == NULL || *password == '\0') {
+		g_hash_table_remove(internal_keyring_passwords, account);
+		purple_debug_info("Internal Keyring",
+			"Deleted password for account %s (%s).\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+	} else {
+		copy = g_strdup(password);
+		SET_PASSWORD((void *)account, copy);	/* cast prevents warning because account is const */
+		purple_debug_info("Internal Keyring",
+			"Updated password for account %s (%s).\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+
+	}
+
+	if(destroy && password)
+		destroy(password);
+
+	if (cb != NULL)
+		cb(account, NULL, data);
+	return;
+}
+
+
+static const char * 
+internal_keyring_read_sync(const PurpleAccount * account)
+{
+	ACTIVATE();
+
+	purple_debug_info("Internal Keyring (_sync_)", 
+		"Password for %s (%s) was read.\n",
+		purple_account_get_username(account),
+		purple_account_get_protocol_id(account));
+
+	return GET_PASSWORD(account);
+}
+
+static void
+internal_keyring_save_sync(PurpleAccount * account,
+			   const char * password)
+{
+	gchar * copy;
+
+	ACTIVATE();
+
+	if (password == NULL || *password == '\0') {
+		g_hash_table_remove(internal_keyring_passwords, account);
+		purple_debug_info("Internal Keyring (_sync_)", 
+			"Password for %s (%s) was deleted.\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+	} else {
+		copy = g_strdup(password);
+		SET_PASSWORD(account, copy);
+		purple_debug_info("Internal Keyring (_sync_)", 
+			"Password for %s (%s) was set.\n",
+			purple_account_get_username(account),
+			purple_account_get_protocol_id(account));
+	}
+
+	return;
+}
+
+static void
+internal_keyring_close(GError ** error)
+{
+	internal_keyring_is_active = FALSE;
+
+	g_hash_table_destroy(internal_keyring_passwords);
+	internal_keyring_passwords = NULL;
+}
+
+static void
+internal_keyring_open()
+{
+	internal_keyring_passwords = g_hash_table_new_full(g_direct_hash,
+		g_direct_equal, NULL, g_free);
+	internal_keyring_is_active = TRUE;
+}
+
+static gboolean
+internal_keyring_import_password(PurpleAccount * account, 
+				 const char * mode,
+				 const char * data,
+				 GError ** error)
+{
+	gchar * copy;
+
+	ACTIVATE();
+
+	purple_debug_info("Internal keyring",
+		"Importing password");
+
+	if (account != NULL && 
+	    data != NULL &&
+	    (mode == NULL || g_strcmp0(mode, "cleartext") == 0)) {
+
+		copy = g_strdup(data);
+		SET_PASSWORD(account, copy);
+		return TRUE;
+
+	} else {
+
+		*error = g_error_new(ERR_PIDGINKEYRING, ERR_NOPASSWD, "no password for account");
+		return FALSE;
+
+	}
+
+	return TRUE;
+}
+
+static gboolean 
+internal_keyring_export_password(PurpleAccount * account,
+				 const char ** mode,
+				 char ** data,
+				 GError ** error,
+				 GDestroyNotify * destroy)
+{
+	gchar * password;
+
+	ACTIVATE();
+
+	purple_debug_info("Internal keyring",
+		"Exporting password");
+
+	password = GET_PASSWORD(account);
+
+	if (password == NULL) {
+		return FALSE;
+	} else {
+		*mode = "cleartext";
+		*data = g_strdup(password);
+		*destroy = g_free;
+		return TRUE;
+	}
+}
+
+
+
+
+static void
+internal_keyring_init()
+{
+	keyring_handler = purple_keyring_new();
+
+	purple_keyring_set_name(keyring_handler, INTERNALKEYRING_NAME);
+	purple_keyring_set_id(keyring_handler, INTERNALKEYRING_ID);
+	purple_keyring_set_read_sync(keyring_handler, internal_keyring_read_sync);
+	purple_keyring_set_save_sync(keyring_handler, internal_keyring_save_sync);
+	purple_keyring_set_read_password(keyring_handler, internal_keyring_read);
+	purple_keyring_set_save_password(keyring_handler, internal_keyring_save);
+	purple_keyring_set_close_keyring(keyring_handler, internal_keyring_close);
+	purple_keyring_set_change_master(keyring_handler, NULL);
+	purple_keyring_set_import_password(keyring_handler, internal_keyring_import_password);
+	purple_keyring_set_export_password(keyring_handler, internal_keyring_export_password);
+
+	purple_keyring_register(keyring_handler);
+}
+
+static void
+internal_keyring_uninit()
+{
+	internal_keyring_close(NULL);
+	purple_keyring_unregister(keyring_handler);
+
+}
+
+
+
+/***********************************************/
+/*     Plugin interface                        */
+/***********************************************/
+
+static gboolean
+internal_keyring_load(PurplePlugin *plugin)
+{
+	internal_keyring_init();
+	return TRUE;
+}
+
+static gboolean
+internal_keyring_unload(PurplePlugin *plugin)
+{
+	internal_keyring_uninit();
+
+	purple_keyring_free(keyring_handler);
+	keyring_handler = NULL;
+
+	return TRUE;
+}
+
+static void
+internal_keyring_destroy(PurplePlugin *plugin)
+{
+	internal_keyring_uninit();
+	return;
+}
+
+PurplePluginInfo plugininfo =
+{
+	PURPLE_PLUGIN_MAGIC,						/* magic */
+	PURPLE_MAJOR_VERSION,						/* major_version */
+	PURPLE_MINOR_VERSION,						/* minor_version */
+	PURPLE_PLUGIN_STANDARD,						/* type */
+	NULL,								/* ui_requirement */
+	PURPLE_PLUGIN_FLAG_INVISIBLE|PURPLE_PLUGIN_FLAG_AUTOLOAD,	/* flags */
+	NULL,								/* dependencies */
+	PURPLE_PRIORITY_DEFAULT,					/* priority */
+	INTERNALKEYRING_ID,						/* id */
+	INTERNALKEYRING_NAME,						/* name */
+	INTERNALKEYRING_VERSION,					/* version */
+	"Internal Keyring Plugin",					/* summary */
+	INTERNALKEYRING_DESCRIPTION,					/* description */
+	INTERNALKEYRING_AUTHOR,						/* author */
+	"N/A",								/* homepage */
+	internal_keyring_load,						/* load */
+	internal_keyring_unload,					/* unload */
+	internal_keyring_destroy,					/* destroy */
+	NULL,								/* ui_info */
+	NULL,								/* extra_info */
+	NULL,								/* prefs_info */
+	NULL,								/* actions */
+	NULL,								/* padding... */
+	NULL,
+	NULL,
+	NULL,
+};
+
+static void                        
+init_plugin(PurplePlugin *plugin)
+{                                  
+	purple_debug_info("internalkeyring", "init plugin called.\n");
+}
+
+PURPLE_INIT_PLUGIN(internal_keyring, init_plugin, plugininfo)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/keyrings/kwallet.cpp	Sat Aug 29 00:45:37 2009 +0000
@@ -0,0 +1,422 @@
+/**
+ * @file kwallet.cpp KWallet password storage
+ * @ingroup plugins
+ *
+ * @todo 
+ *   cleanup error handling and reporting
+ *   refuse unloading when active (in internal keyring too)
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program ; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include <config.h>
+#endif
+
+#ifndef PURPLE_PLUGINS
+ #define PURPLE_PLUGINS
+#endif
+
+#include <glib.h>
+
+#ifndef G_GNUC_NULL_TERMINATED
+ #if __GNUC__ >= 4
+  #define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
+ #else
+  #define G_GNUC_NULL_TERMINATED
+ #endif
+#endif
+
+#include "account.h"
+#include "version.h"
+#include "keyring.h"
+#include "debug.h"
+#include "plugin.h"
+#include "internal.h"
+
+
+static GQuark kwallet_plugin_error_domain(void);
+
+static void kwallet_read(PurpleAccount * account, PurpleKeyringReadCallback cb, gpointer data);
+static void kwallet_save(PurpleAccount * account, gchar * password, GDestroyNotify destroypassword, PurpleKeyringSaveCallback cb, gpointer data);
+static void kwallet_close(GError ** error);
+static void kwallet_import(PurpleAccount * account, const char * mode, const char * data, GError ** error);
+static void kwallet_export(PurpleAccount * account, const char ** mode, char ** data, GError ** error, GDestroyNotify * destroy);
+
+static gboolean kwallet_load(PurplePlugin *plugin);
+static gboolean kwallet_unload(PurplePlugin *plugin);
+static void kwallet_destroy(PurplePlugin *plugin);
+static void init_plugin(PurplePlugin *plugin)
+
+PurpleKeyring * keyring_handler;
+
+PurplePluginInfo plugininfo =
+{
+	PURPLE_PLUGIN_MAGIC,						/* magic */
+	PURPLE_MAJOR_VERSION,						/* major_version */
+	PURPLE_MINOR_VERSION,						/* minor_version */
+	PURPLE_PLUGIN_STANDARD,						/* type */
+	NULL,								/* ui_requirement */
+	PURPLE_PLUGIN_FLAG_INVISIBLE|PURPLE_PLUGIN_FLAG_AUTOLOAD,	/* flags */
+	NULL,								/* dependencies */
+	PURPLE_PRIORITY_DEFAULT,					/* priority */
+	GNOMEKEYRING_ID,						/* id */
+	GNOMEKEYRING_NAME,						/* name */
+	GNOMEKEYRING_VERSION,						/* version */
+	"Internal Keyring Plugin",					/* summary */
+	GNOMEKEYRING_DESCRIPTION,					/* description */
+	GNOMEKEYRING_AUTHOR,						/* author */
+	"N/A",								/* homepage */
+	kwallet_load,							/* load */
+	kwallet_unload,							/* unload */
+	kwallet_destroy,						/* destroy */
+	NULL,								/* ui_info */
+	NULL,								/* extra_info */
+	NULL,								/* prefs_info */
+	NULL,								/* actions */
+	NULL,								/* padding... */
+	NULL,
+	NULL,
+	NULL,
+};
+
+
+extern "C"
+{
+PURPLE_INIT_PLUGIN(kwallet_keyring, init_plugin, plugininfo)
+}
+
+
+
+
+#define ERR_KWALLETPLUGIN 	kwallet_plugin_error_domain()
+
+
+
+class KWalletPlugin::engine
+{
+	public :
+		engine();
+		~engine();
+		void queue(request req);
+		static engine * Instance();
+
+	signal :
+		void walletopened(bool opened);
+
+	private :
+		bool connected;
+		KWallet::wallet * wallet;
+		std::list<request> requests;
+		static engine * pinstance;
+
+		KApplication *app;
+		void ExecuteRequests();
+};
+
+KWalletPlugin::engine::engine()
+{
+	KAboutData aboutData("libpurple_plugin", N_("LibPurple KWallet Plugin"), "", "", KAboutData::License_GPL, "");
+	KCmdLineArgs::init( &aboutData );
+	app = new KApplication(false, false);
+
+	connected = FALSE;
+	wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), 0, Asynchronous);
+	QObject::connect(wallet, SIGNAL(KWallet::Wallet::walletOpened(bool)), SLOT(walletopened(bool)));
+}
+
+KWalletPlugin::engine::~engine()
+{
+	<list<request>>::iterator it;
+
+	for(it = requests.begin(); it != requests.end ; it++) {
+		it->abort();
+		requests.pop_front();
+	}
+
+	KWallet::closeWallet(KWallet::Wallet::NetworkWallet(), TRUE)
+	delete wallet;
+	pinstance = NULL;
+}
+
+engine * 
+KWalletPlugin::engine::Instance()
+{
+	if (pinstance == NULL)
+		pinstance = new engine;
+	return pinstance;
+}
+
+void
+KWalletPlugin::engine::walletopened(bool opened)
+{
+	connected = opened;
+
+	if(opened) {
+		ExecuteRequests();
+	} else {
+		for(it = requests.begin(); it != requests.end ; it++) {
+			it->abort();
+			requests.pop_front();
+		}
+		delete this;
+	}
+}
+
+void
+KWalletPlugin::engine::queue(request req)
+{
+	requests.push_back(req);
+	ExecuteRequests();
+}
+
+void
+KWalletPlugin::engine::ExecuteRequests()
+{
+	if(connected)
+		for(it = requests.begin(); it != requests.end() ; it++) {
+			it->execute();
+			requests.pop_front();
+			delete it;
+		}
+}
+
+class KWalletPlugin::request
+{
+	public :		
+		virtual void abort();
+		virtual void execute(KWallet::wallet * wallet);
+
+	private :
+		gpointer data;
+		PurpleAccount * account
+		QString password;
+}
+
+class KWalletPlugin::save_request : public request
+{
+	public :
+		request(PurpleAccount * account, char * password, void * cb, void * data);
+		void abort();
+		void execute(KWallet::wallet * wallet);
+
+	private :
+		PurpleKeyringReadCallback callback;
+}
+
+class KWalletPlugin::read_request : public request
+{
+	public :
+		request(PurpleAccount * account, void * cb, void * data);
+		void abort();
+		void execute(KWallet::wallet * wallet);
+
+	private :
+		PurpleKeyringSaveCallback callback;
+}
+
+
+
+KWalletPlugin::save_request::save_request(PurpleAccount * acc, char * pw, void * cb, void * userdata)
+{
+	account  = acc;
+	data     = userdata;
+	callback = cb;
+	password = pw;
+}
+
+KWalletPlugin::read_request::read_request(PurpleAccount * acc, void * cb, void * userdata)
+{
+	account  = acc;
+	data     = userdata;
+	callback = cb;
+	password = NULL;
+}
+
+void
+KWalletPlugin::save_request::abort()
+{
+	GError * error;
+	if (cb != NULL) {
+		error = g_error_new(ERR_KWALLETPLUGIN, 
+		                    ERR_UNKNOWN,
+		                    "Failed to save password");
+		cb(account, error, data);
+		g_error_free(error);
+	}
+}
+
+void
+KWalletPlugin::read_request::abort()
+{
+	GError * error;
+	if (callback != NULL) {
+		error = g_error_new(ERR_KWALLETPLUGIN, 
+		                    ERR_UNKNOWN,
+		                    "Failed to save password");
+		cb(account, NULL, error, data);
+		g_error_free(error);
+	}
+}
+
+void
+KWalletPlugin::read_request::execute(KWallet::wallet * wallet)
+{
+	int result;
+	GString key;
+
+	key = "purple-" + purple_account_get_username(account) + " " + purple_account_get_protocol_id(account);
+	result = wallet.readPassword(key, password);
+	
+	if(result != 0)
+		abort();
+	else
+		if (callback != NULL)
+			callback(account, (const char *)password, NULL, data);
+}
+
+void
+KWalletPlugin::save_request::execute(KWallet::wallet * wallet)
+{
+	int result;
+	GString key;
+
+	key = "purple-" + purple_account_get_username(account) + " " + purple_account_get_protocol_id(account);
+	result = wallet.writePassword(key, password);
+	
+	if(result != 0)
+		abort();
+	else
+		if (callback != NULL)
+			callback(account, (const char *)password, NULL, data);
+}
+
+
+
+
+
+
+
+
+
+
+void 
+kwallet_read(PurpleAccount * account,
+	     PurpleKeyringReadCallback cb,
+	     gpointer data)
+{
+	KWalletPlugin::read_request req(account, cb, data);
+	KWalletPlugin::engine::instance()->queue(req);
+}
+
+
+void 
+kwallet_save(PurpleAccount * account,
+	     char * password,
+	     GDestroyNotify destroypassword,
+	     PurpleKeyringSaveCallback cb,
+	     gpointer data)
+{
+	KWalletPlugin::read_request req(account, password, cb, data);
+	KWalletPlugin::engine::instance()->queue(req);
+	if (destroypassword)
+		destroypassword(password);
+}
+
+
+void 
+kwallet_close(GError ** error)
+{
+	delete KWalletPlugin::engine::instance();
+}
+
+void 
+kwallet_import(PurpleAccount * account, 
+	       const char * mode,
+	       const char * data,
+	       GError ** error)
+{
+	return TRUE;
+}
+
+void 
+kwallet_export(PurpleAccount * account,
+	       const char ** mode,
+	       char ** data,
+	       GError ** error,
+	       GDestroyNotify * destroy)
+{
+	*mode = NULL;
+	*data = NULL;
+	destroy = NULL;
+}
+
+gboolean
+kwallet_load(PurplePlugin *plugin)
+{
+	keyring_handler = purple_keyring_new();
+
+	purple_keyring_set_name(keyring_handler, GNOMEKEYRING_NAME);
+	purple_keyring_set_id(keyring_handler, GNOMEKEYRING_ID);
+	purple_keyring_set_read_sync(keyring_handler, gkp_read_sync);
+	purple_keyring_set_save_sync(keyring_handler, gkp_save_sync);
+	purple_keyring_set_read_password(keyring_handler, gkp_read);
+	purple_keyring_set_save_password(keyring_handler, gkp_save);
+	purple_keyring_set_close_keyring(keyring_handler, gkp_close);
+	purple_keyring_set_change_master(keyring_handler, gkp_change_master);
+	purple_keyring_set_import_password(keyring_handler, gkp_import_password);
+	purple_keyring_set_export_password(keyring_handler, gkp_export_password);
+
+	purple_keyring_register(keyring_handler);
+
+	return TRUE;
+}
+
+gboolean
+kwallet_unload(PurplePlugin *plugin)
+{
+	kwallet_close();
+	return TRUE;
+}
+
+void
+gkp_destroy(PurplePlugin *plugin)
+{
+	kwallet_close();
+	return;
+}
+
+void                        
+init_plugin(PurplePlugin *plugin)
+{                                  
+	purple_debug_info("KWallet plugin", "init plugin called.\n");
+}
+
+GQuark kwallet_plugin_error_domain(void)
+{
+	return g_quark_from_static_string("KWallet keyring");
+}
+
+
+
+
+
--- a/libpurple/protocols/oscar/flap_connection.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/protocols/oscar/flap_connection.c	Sat Aug 29 00:45:37 2009 +0000
@@ -20,6 +20,7 @@
 
 #include "oscar.h"
 
+#include "account.h"
 #include "eventloop.h"
 #include "proxy.h"
 
--- a/libpurple/protocols/qq/qq_network.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/protocols/qq/qq_network.c	Sat Aug 29 00:45:37 2009 +0000
@@ -732,7 +732,7 @@
 #endif
 
 	/* now generate md5 processed passwd */
-	passwd = purple_account_get_password(purple_connection_get_account(gc));
+	passwd = purple_connection_get_password(gc);
 
 	/* use twice-md5 of user password as session key since QQ 2003iii */
 	dest = qd->ld.pwd_md5;
--- a/libpurple/prpl.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/prpl.c	Sat Aug 29 00:45:37 2009 +0000
@@ -343,11 +343,17 @@
 
 	if (!purple_status_is_online(new_status))
 	{
+		/* Clear out the unsaved password if the disconnect was initiated
+		   by the user */
+		if (!purple_account_get_remember_password(account)) {
+			PurpleConnection *gc = purple_account_get_connection(account);
+			if (gc && purple_connection_had_error(gc))
+				purple_account_set_password_async(account, NULL, NULL, NULL, NULL);
+		}
+
 		if (!purple_account_is_disconnected(account))
 			purple_account_disconnect(account);
-		/* Clear out the unsaved password if we're already disconnected and we switch to offline status */
-		else if (!purple_account_get_remember_password(account))
-			purple_account_set_password(account, NULL);
+
 		return;
 	}
 
--- a/libpurple/xmlnode.h	Fri Aug 28 23:27:10 2009 +0000
+++ b/libpurple/xmlnode.h	Sat Aug 29 00:45:37 2009 +0000
@@ -163,7 +163,7 @@
  *
  * @param node   The node to set an attribute for.
  * @param attr   The name of the attribute to set
- * @param prefix The prefix of the attribute to ste
+ * @param prefix The prefix of the attribute to set
  * @param value  The value of the attribute
  *
  * @deprecated Use xmlnode_set_attrib_full instead.
--- a/pidgin/gtkaccount.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/pidgin/gtkaccount.c	Sat Aug 29 00:45:37 2009 +0000
@@ -115,6 +115,7 @@
 	GtkWidget *login_frame;
 	GtkWidget *protocol_menu;
 	GtkWidget *password_box;
+	char *password;
 	GtkWidget *username_entry;
 	GtkWidget *password_entry;
 	GtkWidget *alias_entry;
@@ -145,6 +146,7 @@
 
 } AccountPrefsDialog;
 
+
 static AccountsWindow *accounts_window = NULL;
 static GHashTable *account_pref_wins;
 
@@ -155,6 +157,7 @@
 /**************************************************************************
  * Add/Modify Account dialog
  **************************************************************************/
+static void pidgin_account_dialog_show_continue(PurpleAccount * account, char * password, GError * error, gpointer data);
 static void add_login_options(AccountPrefsDialog *dialog, GtkWidget *parent);
 static void add_user_options(AccountPrefsDialog *dialog, GtkWidget *parent);
 static void add_protocol_options(AccountPrefsDialog *dialog);
@@ -584,10 +587,10 @@
 
 	/* Set the fields. */
 	if (dialog->account != NULL) {
-		if (purple_account_get_password(dialog->account) &&
+		if (dialog->password &&
 		    purple_account_get_remember_password(dialog->account))
 			gtk_entry_set_text(GTK_ENTRY(dialog->password_entry),
-							   purple_account_get_password(dialog->account));
+				dialog->password);
 
 		gtk_toggle_button_set_active(
 				GTK_TOGGLE_BUTTON(dialog->remember_pass_check),
@@ -1187,6 +1190,7 @@
 	char *tmp;
 	gboolean new_acct = FALSE, icon_change = FALSE;
 	PurpleAccount *account;
+	gboolean remember;
 
 	/* Build the username string. */
 	username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->username_entry)));
@@ -1292,9 +1296,11 @@
 
 
 	/* Remember Password */
-	purple_account_set_remember_password(account,
-			gtk_toggle_button_get_active(
-					GTK_TOGGLE_BUTTON(dialog->remember_pass_check)));
+	remember = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dialog->remember_pass_check));
+	if(!remember)
+		purple_keyring_set_password_async(account, NULL, NULL, NULL, NULL);
+
+	purple_account_set_remember_password(account, remember);
 
 	/* Check Mail */
 	if (dialog->prpl_info && dialog->prpl_info->options & OPT_PROTO_MAIL_CHECK)
@@ -1302,6 +1308,7 @@
 			gtk_toggle_button_get_active(
 					GTK_TOGGLE_BUTTON(dialog->new_mail_check)));
 
+
 	/* Password */
 	value = gtk_entry_get_text(GTK_ENTRY(dialog->password_entry));
 
@@ -1311,10 +1318,11 @@
 	 * the account editor (but has not checked the 'save' box), then we
 	 * don't want to prompt them.
 	 */
-	if ((purple_account_get_remember_password(account) || new_acct) && (*value != '\0'))
+	if ((purple_account_get_remember_password(account) || new_acct) && (*value != '\0')) {
 		purple_account_set_password(account, value);
-	else
+	} else {
 		purple_account_set_password(account, NULL);
+	}
 
 	purple_account_set_username(account, username);
 	g_free(username);
@@ -1446,10 +1454,23 @@
 	{"STRING", 0, 2}
 };
 
+
 void
 pidgin_account_dialog_show(PidginAccountDialogType type,
-							 PurpleAccount *account)
+			   PurpleAccount *account)
 {
+	/* this is to make sure the password will be cached */
+	purple_account_get_password_async(account,
+		pidgin_account_dialog_show_continue, (void*)type);
+}
+
+static void
+pidgin_account_dialog_show_continue(PurpleAccount * account,
+			   char * password,
+			   GError * error,
+			   gpointer data)
+{
+	PidginAccountDialogType type = (PidginAccountDialogType)data;
 	AccountPrefsDialog *dialog;
 	GtkWidget *win;
 	GtkWidget *main_vbox;
@@ -1473,6 +1494,7 @@
 	}
 
 	dialog->account = account;
+	dialog->password = g_strdup(password);
 	dialog->type    = type;
 	dialog->sg      = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
 
@@ -1513,6 +1535,7 @@
 
 	/* Setup the top frames. */
 	add_login_options(dialog, vbox);
+
 	add_user_options(dialog, vbox);
 
 	button = gtk_check_button_new_with_mnemonic(
@@ -2066,7 +2089,7 @@
 	if (global_buddyicon != NULL)
 		g_object_unref(G_OBJECT(global_buddyicon));
 
-	return ret;
+	return ret; 
 }
 
 #if !GTK_CHECK_VERSION(2,2,0)
--- a/pidgin/gtkconn.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/pidgin/gtkconn.c	Sat Aug 29 00:45:37 2009 +0000
@@ -208,10 +208,7 @@
 	while (l) {
 		PurpleAccount *a = (PurpleAccount*)l->data;
 		if (!purple_account_is_disconnected(a)) {
-			char *password = g_strdup(purple_account_get_password(a));
 			purple_account_disconnect(a);
-			purple_account_set_password(a, password);
-			g_free(password);
 		}
 		l = l->next;
 	}
--- a/pidgin/gtkprefs.c	Fri Aug 28 23:27:10 2009 +0000
+++ b/pidgin/gtkprefs.c	Sat Aug 29 00:45:37 2009 +0000
@@ -42,6 +42,7 @@
 #include "upnp.h"
 #include "util.h"
 #include "network.h"
+#include "keyring.h"
 
 #include "gtkblist.h"
 #include "gtkconv.h"
@@ -2242,6 +2243,80 @@
 	return ret;
 }
 
+static void
+change_master_password_cb(GtkWidget * button, gpointer ptr)
+{
+	purple_keyring_change_master(NULL, NULL);
+}
+
+static void
+keyring_page_pref_changed(const char *name, PurplePrefType type, gconstpointer val, gpointer data)
+{
+	GtkWidget * button = data;
+	PurpleKeyring * keyring;
+
+	g_return_if_fail(type == PURPLE_PREF_STRING);
+	g_return_if_fail(g_strcmp0(name,"/purple/keyring/active") == 0);
+
+	/**
+	 * This part is annoying.
+	 * Since we do not know if purple_keyring_inuse was changed yet,
+	 * as we do not know the order the callbacks are called in, we
+	 * have to rely on the prefs system, and find the keyring that
+	 * is (or will be) used, from there.
+	 */
+
+	keyring = purple_keyring_get_keyring_by_id(val);
+
+	if (purple_keyring_get_change_master(keyring))
+		gtk_widget_set_sensitive(button,TRUE);
+	else
+		gtk_widget_set_sensitive(button,FALSE);
+}
+
+
+static GtkWidget *
+keyring_page(void)
+{
+	GtkWidget *ret;
+	GtkWidget *vbox;
+	GtkWidget * button;
+	GList *names;
+	void * prefs;
+	const char * keyring_id;
+	PurpleKeyring * keyring;
+
+	keyring_id = purple_prefs_get_string("/purple/keyring/active");
+	keyring = purple_keyring_get_keyring_by_id(keyring_id);
+
+	prefs = purple_prefs_get_handle();
+
+	ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+	gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
+
+	/*  Keyring selection */
+	vbox = pidgin_make_frame (ret, _("Keyring"));
+	names = purple_keyring_get_options();
+	pidgin_prefs_dropdown_from_list(vbox, _("Keyring :"), PURPLE_PREF_STRING,
+				 "/purple/keyring/active", names);
+	g_list_free(names);
+
+	/* Change master password */
+	button = gtk_button_new_with_mnemonic(_("_Change master password."));
+	
+	g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(change_master_password_cb), NULL);
+	purple_prefs_connect_callback (prefs, "/purple/keyring/active", keyring_page_pref_changed, button);
+
+	if (purple_keyring_get_change_master(keyring))
+		gtk_widget_set_sensitive(button,TRUE);
+	else
+		gtk_widget_set_sensitive(button,FALSE);
+
+	gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 1);
+	gtk_widget_show_all(ret);
+	return ret;
+}
+
 #ifndef _WIN32
 static gint
 sound_cmd_yeah(GtkEntry *entry, gpointer d)
@@ -2806,6 +2881,7 @@
 	}
 #endif
 	prefs_notebook_add_page(_("Logging"), logging_page(), notebook_page++);
+	prefs_notebook_add_page(_("Password Storage"), keyring_page(), notebook_page++);
 	prefs_notebook_add_page(_("Status / Idle"), away_page(), notebook_page++);
 }
 

mercurial