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

Wed, 13 Aug 2008 21:46:17 +0000

author
Vivien Bernet-Rollande <scrouaf@soc.pidgin.im>
date
Wed, 13 Aug 2008 21:46:17 +0000
branch
soc.2008.masterpassword
changeset 33989
3d229403dd41
parent 24049
a9f4343f9aad (current diff)
parent 33987
9beebdbf44d6 (diff)
child 33990
8cef8a764ab9

propagate from branch 'im.pidgin.pidgin' (head a9f4343f9aad8830875ced7e6469f1fec95a3542)
to branch 'im.pidgin.soc.2008.masterpassword' (head 9beebdbf44d6fa37e0fb6b25511cbe755bd79b58)

COPYRIGHT file | annotate | diff | comparison | revisions
--- a/COPYRIGHT	Wed Aug 13 19:38:25 2008 +0000
+++ b/COPYRIGHT	Wed Aug 13 21:46:17 2008 +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	Wed Aug 13 19:38:25 2008 +0000
+++ b/configure.ac	Wed Aug 13 21:46:17 2008 +0000
@@ -2391,6 +2391,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
--- a/libpurple/Makefile.am	Wed Aug 13 19:38:25 2008 +0000
+++ b/libpurple/Makefile.am	Wed Aug 13 21:46:17 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	Wed Aug 13 19:38:25 2008 +0000
+++ b/libpurple/account.c	Wed Aug 13 21:46:17 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 async 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 = NULL;
+	GDestroyNotify destroy;
 
 	node = xmlnode_new("account");
 
@@ -371,11 +392,30 @@
 	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.\n",
+			purple_account_get_username(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");
+			xmlnode_set_attrib(child, "keyring_id", 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)
@@ -451,6 +491,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 +807,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 +839,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 +911,31 @@
 		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);	TODO :
+			g_free(mode);		This was commented becaus eof a double free.
+			g_free(data); 		I should figure out which one causes the bug to avoid leaks
+			*/
+		} else {
+			purple_debug_info("accounts", "failed to imported password.\n");
+			/* TODO handle error */
+		}
+	}
+
 	return ret;
 }
 
@@ -1035,21 +1097,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 +1249,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,12 +1607,50 @@
 {
 	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();
 }
 
+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.
+	 */
+	if(account != NULL) {
+
+		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);
+
+		}
+	}
+	schedule_accounts_save();
+}
 void
 purple_account_set_alias(PurpleAccount *account, const char *alias)
 {
@@ -1567,6 +1718,10 @@
 	account->gc = gc;
 }
 
+/**
+ * FIXME :
+ *  This should add/remove the password to/from the keyring
+ */
 void
 purple_account_set_remember_password(PurpleAccount *account, gboolean value)
 {
@@ -1887,14 +2042,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	Wed Aug 13 19:38:25 2008 +0000
+++ b/libpurple/account.h	Wed Aug 13 21:46:17 2008 +0000
@@ -46,6 +46,7 @@
 #include "proxy.h"
 #include "prpl.h"
 #include "status.h"
+#include "keyring.h"
 
 /**
  * Account request types.
@@ -330,6 +331,22 @@
 void purple_account_set_password(PurpleAccount *account, const char *password);
 
 /**
+ * Set a password to be remembered.
+ * This should be renamed purple_account_set_password() when getting
+ * to 3.0. This calls the keyring function and syncs the accounts.xml
+ * @param account The account for which the password is to be saved.
+ * @param password The password to save.
+ * @param destroypassword A function called to free the password. Can be NULL.
+ * @param cb A callback for once the password is saved.
+ * @param data A pointer to be passed to the callback.
+ */
+void purple_account_set_password_async(PurpleAccount * account, 
+				  gchar * password,
+				  GDestroyNotify destroypassword,
+				  PurpleKeyringSaveCallback cb,
+				  gpointer data);
+
+/**
  * Sets the account's alias.
  *
  * @param account The account.
@@ -549,7 +566,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 +574,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	Wed Aug 13 19:38:25 2008 +0000
+++ b/libpurple/connection.c	Wed Aug 13 21:46:17 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	Wed Aug 13 19:38:25 2008 +0000
+++ b/libpurple/core.c	Wed Aug 13 21:46:17 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	Wed Aug 13 21:46:17 2008 +0000
@@ -0,0 +1,1165 @@
+/**
+ * @file keyring.c Keyring plugin API
+ * @ingroup core
+ */
+
+/* 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"
+
+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);
+
+
+/******************************************/
+/** @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;
+};
+
+/* 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;
+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 */
+/** @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, 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) {
+			/**
+			 * TODO :
+			 *  - create faulty keyring to test this code.
+			 */
+			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");
+
+			/* FIXME : this should maybe be in a callback
+			 *  cancel the prefs change 
+			 */
+			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_inuse = tracker->new;
+			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;
+	GError * error = NULL; 
+
+	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();
+
+			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);	/* FIXME Maybe this should have a callback that can cancel the action */
+
+	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);
+		/** @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;
+
+	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) {
+			/* TODO : check result ? */
+			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, 
+			       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) 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(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	Wed Aug 13 21:46:17 2008 +0000
@@ -0,0 +1,474 @@
+/**
+ * @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)(const PurpleAccount * account,
+					  const gchar * password,
+					  GError * error,
+					  gpointer data);
+
+/**
+ * Callback for once a password has been stored. If there was a problem, the error will be set.
+ *
+ * @param 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)(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, 
+					    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                  */
+/***************************************/
+/*@{*/
+
+/**
+ * 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, 
+				    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	Wed Aug 13 19:38:25 2008 +0000
+++ b/libpurple/plugin.c	Wed Aug 13 21:46:17 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	Wed Aug 13 19:38:25 2008 +0000
+++ b/libpurple/plugin.h	Wed Aug 13 21:46:17 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	Wed Aug 13 19:38:25 2008 +0000
+++ b/libpurple/plugins/Makefile.am	Wed Aug 13 21:46:17 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)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/internalkeyring.c	Wed Aug 13 21:46:17 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)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/keyrings/Makefile.am	Wed Aug 13 21:46:17 2008 +0000
@@ -0,0 +1,41 @@
+EXTRA_DIST = \
+		Makefile.mingw
+
+plugindir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
+
+gnomekeyring_la_LDFLAGS     = -module -avoid-version
+internalkeyring_la_LDFLAGS  = -module -avoid-version
+
+GNOME_KEYRING_CFLAGS = -I/usr/include/gnome-keyring-1
+GNOME_KEYRING_LIBS   = -lgnome-keyring
+
+GKRSOURCES = gnomekeyring.c
+IKSOURCES = internalkeyring.c
+
+if PLUGINS
+
+plugin_LTLIBRARIES = \
+	gnomekeyring.la       \
+	internalkeyring.la
+
+gnomekeyring_la_SOURCES     = gnomekeyring.c
+internalkeyring_la_SOURCES  = internalkeyring.c
+
+gnomekeyring_la_LIBADD  = $(GLIB_LIBS) $(GNOME_KEYRING_LIBS)
+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)
+
+gnomekeyring_la_CFLAGS = $(AM_CPPFLAGS) $(GNOME_KEYRING_CFLAGS)
+internalkeyring_la_CFLAGS = $(AM_CPPFLAGS)
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/plugins/keyrings/gnomekeyring.c	Wed Aug 13 21:46:17 2008 +0000
@@ -0,0 +1,473 @@
+/**
+ * @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"
+
+#define GNOMEKEYRING_NAME		"Gnome-Keyring"
+#define GNOMEKEYRING_VERSION		"0.2"
+#define GNOMEKEYRING_DESCRIPTION	"This plugin provides the default password storage behaviour for libpurple."
+#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;
+};
+
+
+
+
+/* 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 *, char *, 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 *);
+
+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;
+
+	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)
+			cb(account, password, NULL, storage->user_data);
+		return;
+
+	}
+
+}
+
+
+static void
+gkp_save(PurpleAccount * account,
+	 gchar * password,
+	 GDestroyNotify destroy,
+	 PurpleKeyringSaveCallback cb,
+	 gpointer data)
+{
+	InfoStorage * storage = g_malloc(sizeof(InfoStorage));
+
+	storage->account = account;
+	storage->cb = cb;
+	storage->user_data = data;
+
+	if(password != NULL) {
+
+		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 */
+					     g_strdup_printf("pidgin-%s", purple_account_get_username(account)),
+					     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 = data;
+	PurpleKeyringSaveCallback cb = storage->cb;
+	GError * error;
+	PurpleAccount * account = storage->account;
+
+	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;
+
+	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)
+{
+	char * copy = g_strdup(password);
+	gkp_save(account, copy, NULL, NULL, NULL);
+	g_free(copy);
+}
+
+static void
+gkp_close(GError ** error)
+{
+	return;
+}
+
+static gboolean
+gkp_import_password(PurpleAccount * account, 
+		    char * mode,
+		    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;
+}
+
+
+
+
+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, NULL);
+		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("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/internalkeyring.c	Wed Aug 13 21:46:17 2008 +0000
@@ -0,0 +1,369 @@
+/**
+ * @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)
+#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 *, 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(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) {
+		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 != NULL)
+		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) {
+		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, 
+				 char * mode,
+				 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)
+
--- a/libpurple/xmlnode.h	Wed Aug 13 19:38:25 2008 +0000
+++ b/libpurple/xmlnode.h	Wed Aug 13 21:46:17 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	Wed Aug 13 19:38:25 2008 +0000
+++ b/pidgin/gtkaccount.c	Wed Aug 13 21:46:17 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	Wed Aug 13 19:38:25 2008 +0000
+++ b/pidgin/gtkprefs.c	Wed Aug 13 21:46:17 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.\n");
+	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