explicit merge of '32e25b8171117846e0db113ab05d2987504617bc' soc.2008.masterpassword

Sat, 03 Jan 2009 21:38:25 +0000

author
Ka-Hing Cheung <khc@pidgin.im>
date
Sat, 03 Jan 2009 21:38:25 +0000
branch
soc.2008.masterpassword
changeset 34017
937477b11fa0
parent 24215
8dd21651408c (current diff)
parent 34016
32e25b817111 (diff)
child 34018
303ae52b5882

explicit merge of '32e25b8171117846e0db113ab05d2987504617bc'
and '8dd21651408c86f59d16d6c658e209300717bd00'

COPYRIGHT file | annotate | diff | comparison | revisions
configure.ac file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/Makefile.am file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/Makefile.mingw file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_1.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_10.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_100.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_11.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_12.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_13.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_14.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_15.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_16.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_17.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_18.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_19.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_2.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_20.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_21.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_22.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_23.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_24.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_25.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_26.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_27.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_28.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_29.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_3.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_30.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_31.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_32.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_33.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_34.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_35.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_36.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_37.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_38.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_39.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_4.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_40.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_41.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_42.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_43.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_44.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_45.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_46.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_47.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_48.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_49.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_5.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_50.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_51.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_52.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_53.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_54.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_55.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_56.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_57.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_58.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_59.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_6.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_60.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_61.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_62.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_63.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_64.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_65.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_66.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_67.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_68.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_69.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_7.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_70.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_71.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_72.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_73.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_74.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_75.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_76.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_77.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_78.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_79.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_8.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_80.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_81.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_82.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_83.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_84.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_85.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_86.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_87.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_88.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_89.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_9.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_90.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_91.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_92.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_93.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_94.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_95.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_96.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_97.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_98.png file | annotate | diff | comparison | revisions
pidgin/pixmaps/buddy_icons/qq/qq_99.png file | annotate | diff | comparison | revisions
--- a/COPYRIGHT	Sat Aug 30 02:30:04 2008 +0000
+++ b/COPYRIGHT	Sat Jan 03 21:38:25 2009 +0000
@@ -30,6 +30,7 @@
 Dave Bell
 Igor Belyi
 Brian Bernas
+Vivien Bernet-Rollande
 Paul Betts
 Jonas Birmé
 George-Cristian Bîrzan
--- a/configure.ac	Sat Aug 30 02:30:04 2008 +0000
+++ b/configure.ac	Sat Jan 03 21:38:25 2009 +0000
@@ -1229,6 +1229,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)
@@ -1261,10 +1262,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 #######################################################################
@@ -2392,6 +2416,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
@@ -2452,6 +2477,7 @@
 	eval eval echo D-Bus services directory...... : $DBUS_SERVICES_DIR
 fi
 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	Sat Aug 30 02:30:04 2008 +0000
+++ b/finch/gntaccount.c	Sat Jan 03 21:38:25 2009 +0000
@@ -83,6 +83,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)
 {
@@ -173,9 +177,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,
@@ -484,6 +488,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;
@@ -563,7 +574,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	Sat Aug 30 02:30:04 2008 +0000
+++ b/finch/gntprefs.c	Sat Jan 03 21:38:25 2009 +0000
@@ -1,6 +1,7 @@
 /**
  * @file gntprefs.c GNT Preferences API
  * @ingroup finch
+ * @todo : add support for master password changing.
  */
 
 /* finch
@@ -80,6 +81,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();
@@ -202,6 +210,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},
@@ -257,6 +270,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	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/Makefile.am	Sat Jan 03 21:38:25 2009 +0000
@@ -50,6 +50,7 @@
 	ft.c \
 	idle.c \
 	imgstore.c \
+	keyring.c \
 	log.c \
 	mime.c \
 	nat-pmp.c \
@@ -103,6 +104,7 @@
 	gaim-compat.h \
 	idle.h \
 	imgstore.h \
+	keyring.h \
 	log.h \
 	mime.h \
 	nat-pmp.h \
--- a/libpurple/account.c	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/account.c	Sat Jan 03 21:38:25 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;
@@ -77,6 +79,12 @@
 	PurpleAccountRequestAuthorizationCb deny_cb;
 } PurpleAccountRequestInfo;
 
+typedef struct
+{
+	gpointer cb;
+	gpointer data;
+} CbInfo;
+
 static PurpleAccountUiOps *account_ui_ops = NULL;
 
 static GList   *accounts = NULL;
@@ -88,6 +96,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                                                   *
  *********************************************************************/
@@ -195,8 +217,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
 	{
@@ -360,8 +381,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");
 
@@ -371,11 +397,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)
@@ -451,6 +500,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);
@@ -765,7 +816,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)
@@ -793,15 +848,6 @@
 	g_free(name);
 	g_free(protocol_id);
 
-	/* Read the password */
-	child = xmlnode_get_child(node, "password");
-	if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
-	{
-		purple_account_set_remember_password(ret, TRUE);
-		purple_account_set_password(ret, data);
-		g_free(data);
-	}
-
 	/* Read the alias */
 	child = xmlnode_get_child(node, "alias");
 	if ((child != NULL) && ((data = xmlnode_get_data(child)) != NULL))
@@ -874,6 +920,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;
 }
 
@@ -1035,24 +1101,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");
@@ -1064,12 +1168,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
@@ -1126,7 +1234,6 @@
 {
 	PurplePlugin *prpl;
 	PurplePluginProtocolInfo *prpl_info;
-	const char *password;
 
 	g_return_if_fail(account != NULL);
 
@@ -1149,7 +1256,20 @@
 	}
 
 	prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
-	password = purple_account_get_password(account);
+	purple_keyring_get_password_async(account, purple_account_connect_got_password_cb, prpl_info);
+
+}
+
+static void
+purple_account_connect_got_password_cb(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))
@@ -1172,10 +1292,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;
 }
 
@@ -1492,14 +1610,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)
 {
@@ -1887,14 +2043,91 @@
 	return account->username;
 }
 
+/* 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;
 }
 
+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)
 {
@@ -2339,7 +2572,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);      
@@ -2544,7 +2777,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	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/account.h	Sat Jan 03 21:38:25 2009 +0000
@@ -46,6 +46,7 @@
 #include "proxy.h"
 #include "prpl.h"
 #include "status.h"
+#include "keyring.h"
 
 /**
  * Account request types.
@@ -321,13 +322,39 @@
  */
 void purple_account_set_username(PurpleAccount *account, const char *username);
 
+#ifndef PURPLE_DISABLE_DEPRECATED
 /**
  * Sets the account's password.
  *
  * @param account  The account.
  * @param password The password.
+ *
+ * @deprecated You should use either purple_account_set_password_async() instead.
+ * This functions is just a wrapper for the async code anyway.
  */
 void purple_account_set_password(PurpleAccount *account, const char *password);
+#endif
+
+/**
+ * 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.
@@ -548,14 +575,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	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/connection.c	Sat Jan 03 21:38:25 2009 +0000
@@ -432,12 +432,22 @@
 	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;
+	if (gc->password) {
+		return gc->password;
+	} else {
+		purple_debug_info("connection",
+			"Password was unknown, getting password from account");
+		return purple_account_get_password(gc->account);
+	}
 }
 
 const char *
@@ -483,10 +493,7 @@
 purple_connection_disconnect_cb(gpointer data)
 {
 	PurpleAccount *account = data;
-	char *password = g_strdup(purple_account_get_password(account));
 	purple_account_disconnect(account);
-	purple_account_set_password(account, password);
-	g_free(password);
 	return FALSE;
 }
 
@@ -505,6 +512,7 @@
 	purple_connection_error_reason (gc, reason, text);
 }
 
+
 void
 purple_connection_error_reason (PurpleConnection *gc,
                                 PurpleConnectionError reason,
--- a/libpurple/connection.h	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/connection.h	Sat Jan 03 21:38:25 2009 +0000
@@ -144,6 +144,7 @@
 #include <time.h>
 
 #include "account.h"
+#include "keyring.h"
 #include "plugin.h"
 #include "status.h"
 #include "sslconn.h"
@@ -390,7 +391,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.
  *
--- a/libpurple/core.c	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/core.c	Sat Jan 03 21:38:25 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"
@@ -132,6 +133,7 @@
 
 	purple_ciphers_init();
 	purple_cmds_init();
+	purple_keyring_init();
 
 	/* Since plugins get probed so early we should probably initialize their
 	 * subsystem right away too.
--- a/libpurple/example/nullclient.c	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/example/nullclient.c	Sat Jan 03 21:38:25 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 Jan 03 21:38:25 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 Jan 03 21:38:25 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	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/plugin.c	Sat Jan 03 21:38:25 2009 +0000
@@ -489,6 +489,12 @@
 		}
 	}
 
+	if (plugin->info->flags & PURPLE_PLUGIN_FLAG_AUTOLOAD) {
+		purple_debug_info("plugins", "Loading autoload plugin %s\n",
+						plugin->path);
+		purple_plugin_load(plugin);
+	}
+
 	return plugin;
 #else
 	return NULL;
@@ -1406,7 +1412,7 @@
 			}
 
 			protocol_plugins = g_list_insert_sorted(protocol_plugins, plugin,
-													(GCompareFunc)compare_prpl);
+								(GCompareFunc)compare_prpl);
 		}
 	}
 
--- a/libpurple/plugin.h	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/plugin.h	Sat Jan 03 21:38:25 2009 +0000
@@ -62,6 +62,7 @@
 #define PURPLE_PRIORITY_LOWEST  -9999
 
 #define PURPLE_PLUGIN_FLAG_INVISIBLE 0x01
+#define PURPLE_PLUGIN_FLAG_AUTOLOAD  0x02
 
 #define PURPLE_PLUGIN_MAGIC 5 /* once we hit 6.0.0 I think we can remove this */
 
--- a/libpurple/plugins/Makefile.am	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/plugins/Makefile.am	Sat Jan 03 21:38:25 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 Jan 03 21:38:25 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 Jan 03 21:38:25 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 Jan 03 21:38:25 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 Jan 03 21:38:25 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/prpl.c	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/prpl.c	Sat Jan 03 21:38:25 2009 +0000
@@ -329,7 +329,7 @@
 			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);
+			purple_account_set_password_async(account, NULL, NULL, NULL, NULL);
 		return;
 	}
 
--- a/libpurple/xmlnode.h	Sat Aug 30 02:30:04 2008 +0000
+++ b/libpurple/xmlnode.h	Sat Jan 03 21:38:25 2009 +0000
@@ -160,7 +160,7 @@
  *
  * @param node   The node to set an attribute for.
  * @param attr   The name of the attribute to set
- * @param prefix The prefix of the attribute to ste
+ * @param prefix The prefix of the attribute to set
  * @param value  The value of the attribute
  */
 void xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value);
--- a/pidgin/gtkaccount.c	Sat Aug 30 02:30:04 2008 +0000
+++ b/pidgin/gtkaccount.c	Sat Jan 03 21:38:25 2009 +0000
@@ -138,6 +138,7 @@
 
 } AccountPrefsDialog;
 
+
 static AccountsWindow *accounts_window = NULL;
 static GHashTable *account_pref_wins;
 
@@ -148,6 +149,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,
@@ -561,9 +563,9 @@
 
 	/* Set the fields. */
 	if (dialog->account != NULL) {
-		if (purple_account_get_password(dialog->account))
-			gtk_entry_set_text(GTK_ENTRY(dialog->password_entry),
-							   purple_account_get_password(dialog->account));
+		if (purple_account_get_password(dialog->account) != NULL)
+			gtk_entry_set_text(GTK_ENTRY(dialog->password_entry), 
+				purple_account_get_password(dialog->account));
 
 		gtk_toggle_button_set_active(
 				GTK_TOGGLE_BUTTON(dialog->remember_pass_check),
@@ -1186,6 +1188,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->screenname_entry)));
@@ -1291,9 +1294,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)
@@ -1301,6 +1306,7 @@
 			gtk_toggle_button_get_active(
 					GTK_TOGGLE_BUTTON(dialog->new_mail_check)));
 
+
 	/* Password */
 	value = gtk_entry_get_text(GTK_ENTRY(dialog->password_entry));
 
@@ -1310,10 +1316,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);
@@ -1453,10 +1460,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;
@@ -1520,6 +1540,7 @@
 
 	/* Setup the top frames. */
 	add_login_options(dialog, vbox);
+
 	add_user_options(dialog, vbox);
 
 	button = gtk_check_button_new_with_mnemonic(
@@ -2065,7 +2086,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/gtkprefs.c	Sat Aug 30 02:30:04 2008 +0000
+++ b/pidgin/gtkprefs.c	Sat Jan 03 21:38:25 2009 +0000
@@ -37,6 +37,7 @@
 #include "sound.h"
 #include "util.h"
 #include "network.h"
+#include "keyring.h"
 
 #include "gtkblist.h"
 #include "gtkconv.h"
@@ -1631,6 +1632,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)
 {
@@ -2153,6 +2228,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