propagate from branch 'im.pidgin.pidgin' (head 94f8b49d1192a4470f2af0eab91c15442e9ea1a4) soc.2008.masterpassword

Mon, 11 Aug 2008 00:49:06 +0000

author
Vivien Bernet-Rollande <scrouaf@soc.pidgin.im>
date
Mon, 11 Aug 2008 00:49:06 +0000
branch
soc.2008.masterpassword
changeset 33985
d8ddac5ecfa1
parent 24024
94f8b49d1192 (current diff)
parent 33983
317cd0a252c2 (diff)
child 33986
9b1127b96fe8

propagate from branch 'im.pidgin.pidgin' (head 94f8b49d1192a4470f2af0eab91c15442e9ea1a4)
to branch 'im.pidgin.soc.2008.masterpassword' (head 317cd0a252c217ae4851872482e2d3acc1db51c9)

COPYRIGHT file | annotate | diff | comparison | revisions
libpurple/protocols/qq/crypt.c file | annotate | diff | comparison | revisions
libpurple/protocols/qq/crypt.h file | annotate | diff | comparison | revisions
libpurple/protocols/qq/group_network.c file | annotate | diff | comparison | revisions
libpurple/protocols/qq/group_network.h file | annotate | diff | comparison | revisions
--- a/COPYRIGHT	Sun Aug 10 21:49:23 2008 +0000
+++ b/COPYRIGHT	Mon Aug 11 00:49:06 2008 +0000
@@ -30,6 +30,7 @@
 Dave Bell
 Igor Belyi
 Brian Bernas
+Vivien Bernet-Rollande
 Paul Betts
 Jonas Birmé
 George-Cristian Bîrzan
--- a/libpurple/Makefile.am	Sun Aug 10 21:49:23 2008 +0000
+++ b/libpurple/Makefile.am	Mon Aug 11 00:49:06 2008 +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	Sun Aug 10 21:49:23 2008 +0000
+++ b/libpurple/account.c	Mon Aug 11 00:49:06 2008 +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,12 @@
 #include "util.h"
 #include "xmlnode.h"
 
+/**
+ * TODO :
+ *  - grab Trannie's code for asynch connection
+ */
+
+
 typedef struct
 {
 	PurpleConnectionErrorInfo *current_error;
@@ -88,6 +95,15 @@
 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(const PurpleAccount * account,
+       gchar * password, GError * error, gpointer data);
 /*********************************************************************
  * Writing to disk                                                   *
  *********************************************************************/
@@ -360,8 +376,13 @@
 
 	xmlnode *node, *child;
 	const char *tmp;
+	const char *keyring_id;
+	const char *mode;
+	char *data;
 	PurplePresence *presence;
 	PurpleProxyInfo *proxy_info;
+	GError * error;
+	GDestroyNotify destroy;
 
 	node = xmlnode_new("account");
 
@@ -371,11 +392,26 @@
 	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.\n");
+		purple_keyring_export_password(account, &keyring_id, 
+			&mode, &data, &error, &destroy);
+
+		if (error != NULL) {
+
+			/* Output debug info */
+
+		} else {
+
+			child = xmlnode_new_child(node, "password");
+			xmlnode_set_attrib(child, "keyringid", keyring_id);
+			xmlnode_set_attrib(child, "mode", mode);
+			xmlnode_insert_data(child, data, -1);
+
+			if (destroy != NULL)
+				destroy(data);
+		}
 	}
 
 	if ((tmp = purple_account_get_alias(account)) != NULL)
@@ -765,7 +801,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;
+	GError * error = NULL;
 
 	child = xmlnode_get_child(node, "protocol");
 	if (child != NULL)
@@ -793,15 +833,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 +905,28 @@
 		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);
+			g_free(keyring_id);
+			g_free(mode);
+			g_free(data);
+		} else {
+			purple_debug_info("accounts", "failed to imported password.\n");
+			// FIXME handle error
+		}
+	}
+
 	return ret;
 }
 
@@ -1035,21 +1088,59 @@
 	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);
+
+	/* FIXME : handle error properly */
+
+	purple_connection_new(account, TRUE, password);
+}
+
+struct _unregister_data
+{
+	PurpleAccountUnregistrationCb cb;
+	void *user_data;
+};
+
 void
 purple_account_unregister(PurpleAccount *account, PurpleAccountUnregistrationCb cb, void *user_data)
 {
+	struct _unregister_data * 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_malloc(sizeof(struct _unregister_data));
+	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)
+{
+	struct _unregister_data * unregdata;
+
+	g_return_if_fail(account != NULL);
+
+	/* FIXME : handle error properly */
+
+	unregdata = data;
+	purple_connection_new_unregister(account, password, unregdata->cb, unregdata->user_data);
+}
+
+
+static void
 request_password_ok_cb(PurpleAccount *account, PurpleRequestFields *fields)
 {
 	const char *entry;
@@ -1149,7 +1240,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(const 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))
@@ -1494,8 +1598,14 @@
 {
 	g_return_if_fail(account != NULL);
 
-	g_free(account->password);
-	account->password = g_strdup(password);
+	if (account->password != NULL)
+		g_free(account->password);
+
+	if (purple_account_get_remember_password(account) == FALSE)
+		account->password = g_strdup(password);
+
+	else
+		purple_keyring_set_password_sync(account, password);
 
 	schedule_accounts_save();
 }
@@ -1887,14 +1997,34 @@
 	return account->username;
 }
 
+/* XXX will be replaced by the async code in 3.0 */
 const char *
 purple_account_get_password(const PurpleAccount *account)
 {
 	g_return_val_if_fail(account != NULL, NULL);
 
-	return account->password;
+	if (account->password != NULL) {
+	
+		purple_debug_info("keyring", "password was read from stored\n");
+		return account->password;
+	
+
+	} else {
+		purple_debug_info("keyring", "reading password from keyring\n");	
+		return purple_keyring_get_password_sync(account);
+	}
 }
 
+void
+purple_account_get_password_async(PurpleAccount * account,
+				  PurpleKeyringReadCallback cb,
+				  gpointer data)
+{
+	purple_keyring_get_password_async(account, cb, data);
+}
+
+
+
 const char *
 purple_account_get_alias(const PurpleAccount *account)
 {
--- a/libpurple/account.h	Sun Aug 10 21:49:23 2008 +0000
+++ b/libpurple/account.h	Mon Aug 11 00:49:06 2008 +0000
@@ -46,6 +46,7 @@
 #include "proxy.h"
 #include "prpl.h"
 #include "status.h"
+#include "keyring.h"
 
 /**
  * Account request types.
@@ -549,7 +550,7 @@
 const char *purple_account_get_username(const PurpleAccount *account);
 
 /**
- * Returns the account's password.
+ * Returns the account's password (deprecated, use async code instead).
  *
  * @param account The account.
  *
@@ -557,6 +558,17 @@
  */
 const char *purple_account_get_password(const PurpleAccount *account);
 
+
+/**
+ * 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	Sun Aug 10 21:49:23 2008 +0000
+++ b/libpurple/connection.c	Mon Aug 11 00:49:06 2008 +0000
@@ -39,6 +39,8 @@
 #include "util.h"
 
 #define KEEPALIVE_INTERVAL 30
+static void purple_connection_disconnect_got_pw_cb(PurpleAccount * account,
+       gchar * password, GError * error, gpointer data);
 
 static GList *connections = NULL;
 static GList *connections_connecting = NULL;
@@ -483,11 +485,23 @@
 purple_connection_disconnect_cb(gpointer data)
 {
 	PurpleAccount *account = data;
-	char *password = g_strdup(purple_account_get_password(account));
+	purple_account_get_password_async(account, 
+		purple_connection_disconnect_got_pw_cb, NULL);
+
+	return FALSE;
+}
+
+static void
+purple_connection_disconnect_got_pw_cb(PurpleAccount * account,
+				       gchar * password,
+				       GError * error,
+				       gpointer data)
+{
+	/* FIXME : handle error */
+	char * pw = g_strdup(password);
+
 	purple_account_disconnect(account);
-	purple_account_set_password(account, password);
-	g_free(password);
-	return FALSE;
+	purple_account_set_password(account, pw);
 }
 
 void
--- a/libpurple/core.c	Sun Aug 10 21:49:23 2008 +0000
+++ b/libpurple/core.c	Mon Aug 11 00:49:06 2008 +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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/keyring.c	Mon Aug 11 00:49:06 2008 +0000
@@ -0,0 +1,1094 @@
+/**
+ * @file keyring.c Keyring plugin API
+ * @todo
+ *  - purple_keyring_()
+ *  - loading : find a way to fallback
+ */
+
+/* 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"
+
+/******************************************/
+/** @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 */
+};
+
+
+/* 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                  */
+/** @todo (maybe)                      */
+/**  - rename as purple_keyrings       */
+/***************************************/
+/*@{*/
+
+static GList * purple_keyring_keyrings;		/* list of available keyrings   */
+static const PurpleKeyring * purple_keyring_inuse;	/* keyring being used	        */
+static char * purple_keyring_to_use;
+
+
+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 */
+
+	purple_prefs_add_string("/purple/keyring/active", "txt");
+	purple_prefs_connect_callback(NULL, "/purple/keyring/active",
+				purple_keyring_pref_cb, NULL);
+
+
+	touse = purple_prefs_get_string("/purple/keyring/active");
+
+	if (touse == NULL) {
+		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_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 */
+/** @todo ? : add dummy callback ? */
+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);
+
+	return;
+}
+
+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;
+};
+
+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 = (struct _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 :
+				g_debug("Keyring could not save password for account %s : %s", name, error->message);
+				break;
+
+			case ERR_NOPASSWD :
+				g_debug("No password found while changing keyring for account %s : %s",
+					 name, error->message);
+				break;
+
+			case ERR_NOCHANNEL :
+				g_debug("Failed to communicate with backend while changing keyring for account %s : %s Aborting changes.",
+					 name, error->message);
+				tracker->abort = TRUE;
+				break;
+
+			default :
+				g_debug("Unknown error while changing keyring for account %s : %s", 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);
+
+		} else {
+			close = purple_keyring_get_close_keyring(tracker->old);
+			close(&error);
+
+			tracker->cb(tracker->new, TRUE, error, tracker->data);
+		}
+
+		g_free(tracker);
+	}
+	return;
+}
+
+
+static void
+purple_keyring_set_inuse_got_pw_cb(const PurpleAccount * account, 
+				  gchar * password, 
+				  GError * error, 
+				  gpointer data)
+{
+	const PurpleKeyring * new;
+	PurpleKeyringSave save;
+	struct _PurpleKeyringChangeTracker * tracker;
+
+	tracker = (struct _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;
+	struct _PurpleKeyringChangeTracker * tracker;
+	GError * error = NULL; 
+
+	purple_debug_info("keyring", "Attempting to set new keyring : %s.\n",
+		newkeyring->id);
+
+	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");
+			g_debug("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(struct _PurpleKeyringChangeTracker));
+			oldkeyring = purple_keyring_get_inuse();
+
+			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;
+	}
+}
+
+
+
+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, /* XXX */NULL, data);	/* FIXME This should have a callback that can cancel the action */
+
+	return;
+}
+
+GList *
+purple_keyring_get_options()
+{
+	const GList * keyrings;
+	PurpleKeyring * keyring;
+	static GList * list = NULL;
+
+	for (keyrings = purple_keyring_get_keyrings();
+	     keyrings != NULL;
+	     keyrings = keyrings->next) {
+
+		keyring = keyrings->data;
+		list = g_list_append(list, keyring->name);
+		list = g_list_append(list, keyring->id);
+	}
+
+	return list;
+}
+
+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);
+		/** @todo add callback to make sure all is ok */
+		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;
+
+	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();
+
+	if (inuse == keyring) {
+		fallback = purple_keyring_find_keyring_by_id(FALLBACK_KEYRING);
+
+		/* this is problematic. If it fails, we won't detect it */
+		purple_keyring_set_inuse(fallback, 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, 
+			       char * keyringid,
+			       char * mode,
+			       char * data,
+			       GError ** error)
+{
+	const PurpleKeyring * inuse;
+	PurpleKeyringImportPassword import;
+	const char * realid;
+
+	purple_debug_info("keyring", "Importing password for account %s (%s).\n",
+		purple_account_get_username(account), purple_account_get_protocol_id(account));
+
+	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) ||
+	    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. Data will be lost.");
+		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;
+
+	purple_debug_info("keyring", "exporting password.\n");
+
+	inuse = purple_keyring_get_inuse();
+
+	if (inuse == NULL) {
+		*error = g_error_new(ERR_PIDGINKEYRING , ERR_NOKEYRING,
+			"No keyring configured, cannot import password info");
+		g_debug("No keyring configured, cannot import password info");
+		return FALSE;
+	}
+
+	*keyringid = purple_keyring_get_id(inuse);
+
+	if (*keyringid == NULL) {
+		*error = g_error_new(ERR_PIDGINKEYRING , ERR_INVALID,
+			"Plugin does not have a keyring id");
+		g_debug("Plugin does not have a keyring id");
+		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.");
+		g_debug("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(const PurpleAccount * account, 
+				  gchar * password,
+				  GDestroyNotify destroypassword,
+				  PurpleKeyringSaveCallback cb,
+				  gpointer data)
+{
+	GError * error = NULL;
+	const PurpleKeyring * inuse;
+	PurpleKeyringSave save;
+
+	if (account == NULL) {
+		error = g_error_new(ERR_PIDGINKEYRING, ERR_INVALID,
+			"No account passed to the function.");
+
+		if (cb != NULL)
+			cb(account, 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, 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 {
+				save(account, password, destroypassword, cb, data);
+			}
+		}
+	}
+
+	return;
+}
+
+
+/**
+ * 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;
+
+	if (account == NULL) {
+		return NULL;
+
+	} else {
+
+		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;
+
+	if (account != NULL) {
+
+		inuse = purple_keyring_get_inuse();
+
+		if (inuse != NULL) {
+
+			save = purple_keyring_get_save_sync(inuse);
+
+			if (save != NULL){
+
+				return save(account, password);
+
+			}
+		}
+	}
+
+	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.");
+
+		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.");
+
+			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	Mon Aug 11 00:49:06 2008 +0000
@@ -0,0 +1,473 @@
+/**
+ * @file keyring.h Keyring plugin API
+ * @ingroup core
+ *
+ * @todo 
+ *  - Offer a way to prompt the user for a password or for a password change.
+ *  - write accessors and types for sync access.
+ */
+
+/* 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)(const 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)(const 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)(const 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)(const 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, 
+					    char * mode,
+					    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);
+
+
+/*@}*/
+
+
+/***************************************/
+/** @name Keyring API                  */
+/***************************************/
+/*@{*/
+
+PurpleKeyring *
+purple_keyring_get_keyring_by_id(const char * id);
+GList *
+purple_keyring_get_options(void);
+void 
+purple_keyring_pref_cb(const char *pref,
+		       PurplePrefType type,
+		       gconstpointer name,
+		       gpointer data);
+/**
+ * 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, 
+				    char * keyringid,
+				    char * mode,
+				    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);
+
+
+
+/** 
+ * functions called from the code to access passwords (account.h):
+ *	purple_account_get_password()
+ *	purple_account_set_password()
+ * @todo :
+ *	- rewrite these functions to use the sync functions for compatibility,
+ *	- build an async way to access the async functions, and patch all libpurple 
+ *	code that calls the accessors to use new ones.
+ */
+
+/**
+ * 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(const PurpleAccount * account, 
+				  gchar * password,
+				  GDestroyNotify destroypassword,
+				  PurpleKeyringSaveCallback cb,
+				  gpointer data);
+
+/**
+ * Read a password in a synchronous way.
+ * This is here only for compatibility reasons. Keyrings are not
+ * expected to support this (and shouldn't), and new code should
+ * use purple_keyring_get_password_async() instead.
+ * @param account The account for which we want the password.
+ * @return A pointer to the the password.
+ */
+const char * 
+purple_keyring_get_password_sync(const PurpleAccount * account);
+
+/**
+ * Save a password in a synchronous way.
+ * This is here only for compatibility reasons. Keyrings are not
+ * expected to support this (and shouldn't), and new code should
+ * use purple_keyring_set_password_async() instead.
+ * @param account The account for which we want the password.
+ * @param password The password to save.
+ */
+void 
+purple_keyring_set_password_sync(PurpleAccount * account,
+				 const char *password);
+
+/**
+ * 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 */
+};
+
+/*}@*/
+#endif /* _PURPLE_KEYRING_H_ */
--- a/libpurple/plugin.c	Sun Aug 10 21:49:23 2008 +0000
+++ b/libpurple/plugin.c	Mon Aug 11 00:49:06 2008 +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	Sun Aug 10 21:49:23 2008 +0000
+++ b/libpurple/plugin.h	Mon Aug 11 00:49:06 2008 +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	Sun Aug 10 21:49:23 2008 +0000
+++ b/libpurple/plugins/Makefile.am	Mon Aug 11 00:49:06 2008 +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)
 
@@ -41,6 +42,7 @@
 signals_test_la_LDFLAGS		= -module -avoid-version
 simple_la_LDFLAGS			= -module -avoid-version
 statenotify_la_LDFLAGS      = -module -avoid-version
+internalkeyring_la_LDFLAGS  = -module -avoid-version
 
 # this can't be in a conditional otherwise automake 1.4 yells
 dbus_example_la_LDFLAGS     = -module -avoid-version
@@ -57,7 +59,8 @@
 	offlinemsg.la       \
 	psychic.la          \
 	statenotify.la      \
-	$(DBUS_LTLIB)
+	$(DBUS_LTLIB)       \
+	internalkeyring.la
 
 noinst_LTLIBRARIES = \
 	ciphertest.la \
@@ -86,6 +89,7 @@
 signals_test_la_SOURCES		= signals-test.c
 simple_la_SOURCES			= simple.c
 statenotify_la_SOURCES      = statenotify.c
+internalkeyring_la_SOURCES  = internalkeyring.c
 
 autoaccept_la_LIBADD        = $(GLIB_LIBS)
 buddynote_la_LIBADD         = $(GLIB_LIBS)
@@ -102,6 +106,7 @@
 signals_test_la_LIBADD		= $(GLIB_LIBS)
 simple_la_LIBADD			= $(GLIB_LIBS)
 statenotify_la_LIBADD       = $(GLIB_LIBS)
+internalkeyring_la_LIBADD   = $(GLIB_LIBS)
 
 if ENABLE_DBUS
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/internalkeyring.c	Mon Aug 11 00:49:06 2008 +0000
@@ -0,0 +1,304 @@
+/**
+ * @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"
+
+#define INTERNALKEYRING_NAME		"Internal keyring"
+#define INTERNALKEYRING_VERSION		"0.7"
+#define INTERNALKEYRING_DESCRIPTION	"This plugin provides the default password storage behaviour for libpurple."
+#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)
+
+
+GHashTable * internal_keyring_passwords = NULL;
+static PurpleKeyring * keyring_handler = NULL;
+
+/* a few prototypes : */
+static void 		internal_keyring_read(const PurpleAccount *, PurpleKeyringReadCallback, gpointer);
+static void 		internal_keyring_save(const 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 gboolean		internal_keyring_import_password(PurpleAccount *, char *, 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(const PurpleAccount * account,
+		      PurpleKeyringReadCallback cb,
+		      gpointer data)
+{
+	char * password;
+	GError * error;
+
+	password = GET_PASSWORD(account);
+
+	if (password != NULL) {
+		cb(account, password, NULL, data);
+	} else {
+		error = g_error_new(ERR_PIDGINKEYRING, 
+			ERR_NOPASSWD, "password not found");
+		cb(account, NULL, error, data);
+		g_error_free(error);
+	}
+	return;
+}
+
+static void
+internal_keyring_save(const PurpleAccount * account,
+		      gchar * password,
+		      GDestroyNotify destroy,
+		      PurpleKeyringSaveCallback cb,
+		      gpointer data)
+{
+	gchar * copy;
+
+	if (password == NULL) {
+		g_hash_table_remove(internal_keyring_passwords, account);
+	} else {
+		copy = g_strdup(password);
+		SET_PASSWORD((void *)account, copy);	/* cast prevents warning because account is const */
+	}
+
+	if(destroy != NULL)
+		destroy(password);
+
+	cb(account, NULL, data);
+	return;
+}
+
+
+static const char * 
+internal_keyring_read_sync(const PurpleAccount * account)
+{
+	purple_debug_info("keyring", "password was read\n");
+	return GET_PASSWORD(account);
+}
+
+static void
+internal_keyring_save_sync(PurpleAccount * account,
+			   const char * password)
+{
+	gchar * copy;
+
+	if (password == NULL) {
+		g_hash_table_remove(internal_keyring_passwords, account);
+	} else {
+		copy = g_strdup(password);
+		SET_PASSWORD(account, copy);
+	}
+	purple_debug_info("keyring", "password was set\n");
+	return;
+}
+
+static void
+internal_keyring_close(GError ** error)
+{
+	internal_keyring_uninit();
+}
+
+static gboolean
+internal_keyring_import_password(PurpleAccount * account, 
+				 char * mode,
+				 char * data,
+				 GError ** error)
+{
+	gchar * copy;
+
+	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;
+
+	}
+}
+
+static gboolean 
+internal_keyring_export_password(PurpleAccount * account,
+				 const char ** mode,
+				 char ** data,
+				 GError ** error,
+				 GDestroyNotify * destroy)
+{
+	gchar * 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);
+
+	internal_keyring_passwords = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free);
+}
+
+static void
+internal_keyring_uninit()
+{
+	purple_keyring_free(keyring_handler);
+	keyring_handler = NULL;
+
+	g_hash_table_destroy(internal_keyring_passwords);
+	internal_keyring_passwords = NULL;
+}
+
+
+
+/***********************************************/
+/*     Plugin interface                        */
+/***********************************************/
+
+static gboolean
+internal_keyring_load(PurplePlugin *plugin)
+{
+	internal_keyring_init();
+	return TRUE;
+}
+
+static gboolean
+internal_keyring_unload(PurplePlugin *plugin)
+{
+	internal_keyring_uninit();
+	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)
+
--- a/libpurple/xmlnode.h	Sun Aug 10 21:49:23 2008 +0000
+++ b/libpurple/xmlnode.h	Mon Aug 11 00:49:06 2008 +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	Sun Aug 10 21:49:23 2008 +0000
+++ b/pidgin/gtkaccount.c	Mon Aug 11 00:49:06 2008 +0000
@@ -402,6 +402,7 @@
 	GList *user_splits;
 	GList *l, *l2;
 	char *username = NULL;
+	char *password = NULL;
 
 	if (dialog->protocol_menu != NULL)
 	{
@@ -561,9 +562,10 @@
 
 	/* 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));
+		password = purple_account_get_password(dialog->account);
+
+		if (password)
+			gtk_entry_set_text(GTK_ENTRY(dialog->password_entry), password);
 
 		gtk_toggle_button_set_active(
 				GTK_TOGGLE_BUTTON(dialog->remember_pass_check),
--- a/pidgin/gtkprefs.c	Sun Aug 10 21:49:23 2008 +0000
+++ b/pidgin/gtkprefs.c	Mon Aug 11 00:49:06 2008 +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,37 @@
 	return ret;
 }
 
+
+static GtkWidget *
+keyring_page(void)
+{
+	GtkWidget *ret;
+	GtkWidget *vbox;
+	GtkWidget *menu;
+	GtkWidget *changemaster;
+	GList *names;
+
+
+	purple_debug_info("prefs", "drawing keyring prefs page");
+	ret = gtk_vbox_new(FALSE, PIDGIN_HIG_CAT_SPACE);
+	gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
+
+
+	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);
+
+
+	gtk_widget_show_all(ret);
+
+	return ret;
+}
+
 #ifndef _WIN32
 static gint sound_cmd_yeah(GtkEntry *entry, gpointer d)
 {
@@ -2153,6 +2185,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