libpurple/keyring.c

Sat, 16 Aug 2008 02:14:04 +0000

author
Vivien Bernet-Rollande <scrouaf@soc.pidgin.im>
date
Sat, 16 Aug 2008 02:14:04 +0000
branch
soc.2008.masterpassword
changeset 33995
53b3189d7c84
parent 33992
a4299c59c1b0
child 33996
ca5e901a5311
permissions
-rw-r--r--

Broke something. Again. Commiting so i get back here if need be.
segfault in malloc() seems to point to a heap overflow...

/**
 * @file keyring.c Keyring plugin API
 * @ingroup core
 * @todo ? : add dummy callback to all calls to prevent poorly written
 * plugins from segfaulting on NULL callback ?
 */

/* purple
 *
 * Purple is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program ; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 */

#include <glib.h>
#include <string.h>
#include "account.h"
#include "keyring.h"
#include "signals.h"
#include "core.h"
#include "debug.h"

typedef struct _PurpleKeyringCbInfo PurpleKeyringCbInfo;
typedef struct _PurpleKeyringChangeTracker PurpleKeyringChangeTracker;

static void purple_keyring_pref_cb(const char *, PurplePrefType, gconstpointer, gpointer);
static PurpleKeyring * purple_keyring_find_keyring_by_id(char * id);
static void purple_keyring_drop_passwords(const PurpleKeyring * keyring);
static void purple_keyring_set_inuse_check_error_cb(const PurpleAccount *,GError *,gpointer);
static void purple_keyring_set_inuse_got_pw_cb(PurpleAccount *, gchar *, GError *, gpointer);


/******************************************/
/** @name PurpleKeyring                   */
/******************************************/
/*@{*/

struct _PurpleKeyring
{
	char * name;		// a user friendly name
	char * id;		// same as plugin id
	PurpleKeyringRead read_password;
	PurpleKeyringSave save_password;
	PurpleKeyringClose close_keyring;
	PurpleKeyringChangeMaster change_master;
	PurpleKeyringImportPassword import_password;
	PurpleKeyringExportPassword export_password;
	PurpleKeyringReadSync read_sync;
	PurpleKeyringSaveSync save_sync;
	gpointer r1;	/* RESERVED */
	gpointer r2;	/* RESERVED */
	gpointer r3;	/* RESERVED */
};

struct _PurpleKeyringChangeTracker
{
	GError * error;			/* could probably be dropped */
	PurpleKeyringSetInUseCallback cb;
	gpointer data;
	const PurpleKeyring * new;
	const PurpleKeyring * old;	/* we are done when : finished == TRUE && read_outstanding == 0 */
	int read_outstanding;
	gboolean finished;
	gboolean abort;
	gboolean force;
};

struct _PurpleKeyringCbInfo
{
	gpointer cb;
	gpointer data;
};

/* Constructor */
PurpleKeyring * 
purple_keyring_new()
{
	return g_malloc0(sizeof(PurpleKeyring));
}

/* Destructor */
void 
purple_keyring_free(PurpleKeyring * keyring)
{
	g_free(keyring);
	return;
}

/* Accessors */
const char * 
purple_keyring_get_name(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->name;
}

const char * 
purple_keyring_get_id(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->id;
}

PurpleKeyringRead 
purple_keyring_get_read_password(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->read_password;
}

PurpleKeyringSave 
purple_keyring_get_save_password(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->save_password;
}

PurpleKeyringReadSync
purple_keyring_get_read_sync(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->read_sync;
}

PurpleKeyringSaveSync
purple_keyring_get_save_sync(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->save_sync;
}

PurpleKeyringClose 
purple_keyring_get_close_keyring(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->close_keyring;
}

PurpleKeyringChangeMaster 
purple_keyring_get_change_master(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->change_master;
}

PurpleKeyringImportPassword 
purple_keyring_get_import_password(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->import_password;
}

PurpleKeyringExportPassword 
purple_keyring_get_export_password(const PurpleKeyring * keyring)
{
	g_return_val_if_fail(keyring != NULL, NULL);

	return keyring->export_password;
}

void 
purple_keyring_set_name(PurpleKeyring * keyring, char * name)
{
	g_return_if_fail(keyring != NULL);

	g_free(keyring->name);
	keyring->name = g_strdup(name);

	return;
}

void 
purple_keyring_set_id(PurpleKeyring * keyring, char * id)
{
	g_return_if_fail(keyring != NULL);

	g_free(keyring->id);
	keyring->id = g_strdup(id);

	return;
}

void
purple_keyring_set_read_password(PurpleKeyring * keyring, PurpleKeyringRead read)
{
	g_return_if_fail(keyring != NULL);

	keyring->read_password = read;

	return;
}

void
purple_keyring_set_save_password(PurpleKeyring * keyring, PurpleKeyringSave save)
{
	g_return_if_fail(keyring != NULL);

	keyring->save_password = save;

	return;
}

void 
purple_keyring_set_read_sync(PurpleKeyring * keyring, PurpleKeyringReadSync read)
{
	g_return_if_fail(keyring != NULL);

	keyring->read_sync = read;

	return;
}

void 
purple_keyring_set_save_sync(PurpleKeyring * keyring, PurpleKeyringSaveSync save)
{
	g_return_if_fail(keyring != NULL);

	keyring->save_sync = save;

	return;
}

void
purple_keyring_set_close_keyring(PurpleKeyring * keyring, PurpleKeyringClose close)
{
	g_return_if_fail(keyring != NULL);

	keyring->close_keyring = close;

	return;
}

void
purple_keyring_set_change_master(PurpleKeyring * keyring, PurpleKeyringChangeMaster change)
{
	g_return_if_fail(keyring != NULL);

	keyring->change_master = change;

	return;
}

void
purple_keyring_set_import_password(PurpleKeyring * keyring, PurpleKeyringImportPassword import)
{
	g_return_if_fail(keyring != NULL);

	keyring->import_password = import;

	return;
}

void
purple_keyring_set_export_password(PurpleKeyring * keyring, PurpleKeyringExportPassword export)
{
	g_return_if_fail(keyring != NULL);

	keyring->export_password = export;

	return;
}

/*@}*/


/***************************************/
/** @name Keyring API                  */
/***************************************/
/*@{*/

static GList * purple_keyring_keyrings;		/* list of available keyrings   */
static const PurpleKeyring * purple_keyring_inuse;	/* keyring being used	        */
static char * purple_keyring_to_use;
static guint purple_keyring_pref_cb_id;

void 
purple_keyring_init()
{
	PurpleCore * core;
	const char * touse;

	/* Make sure we don't have junk */
	purple_keyring_keyrings = NULL;
	purple_keyring_inuse = NULL;

	/* register signals */
	core = purple_get_core();

	purple_signal_register(core, "keyring-register",
		purple_marshal_VOID__POINTER_POINTER, 
		NULL, 2,
		purple_value_new(PURPLE_TYPE_STRING),			/* keyring ID */
		purple_value_new(PURPLE_TYPE_BOXED, "PurpleKeyring *")); /* a pointer to the keyring */

	purple_signal_register(core, "keyring-unregister",
		purple_marshal_VOID__POINTER_POINTER, 
		NULL, 2,
		purple_value_new(PURPLE_TYPE_STRING),			/* keyring ID */
		purple_value_new(PURPLE_TYPE_BOXED, "PurpleKeyring *")); /* a pointer to the keyring */

	/* see what keyring we want to use */
	touse = purple_prefs_get_string("/purple/keyring/active");

	if (touse == NULL) {
		purple_prefs_add_none("/purple/keyring");
		purple_prefs_add_string("/purple/keyring/active", FALLBACK_KEYRING);
		purple_keyring_to_use = g_strdup(FALLBACK_KEYRING);

	} else {

		purple_keyring_to_use = g_strdup(touse);
	}

	purple_keyring_pref_cb_id = purple_prefs_connect_callback(NULL, 
		"/purple/keyring/active", purple_keyring_pref_cb, NULL);

	purple_debug_info("keyring", "purple_keyring_init() done, selected keyring is : %s.\n",
		purple_keyring_to_use);

	return;
}

void
purple_keyring_uninit()
{
	g_free(purple_keyring_to_use);
	purple_debug_info("keyring", "purple_keyring_uninit() done.\n");
}

static PurpleKeyring *
purple_keyring_find_keyring_by_id(char * id)
{
	GList * l;
	PurpleKeyring * keyring;
	const char * curr_id;

	for (l = purple_keyring_keyrings; l != NULL; l = l->next) {
		keyring = l->data;
		curr_id = purple_keyring_get_id(keyring);

		if (g_strcmp0(id, curr_id) == 0)
			return keyring;
	}

	return NULL;
}

const GList * 
purple_keyring_get_keyrings()
{
	return purple_keyring_keyrings;
}

const PurpleKeyring * 
purple_keyring_get_inuse()
{
	return purple_keyring_inuse;
}


/* fire and forget */
static void
purple_keyring_drop_passwords(const PurpleKeyring * keyring)
{
	GList * cur;
	PurpleKeyringSave save;

	save = purple_keyring_get_save_password(keyring);

	g_return_if_fail(save != NULL);

	for (cur = purple_accounts_get_all();
	     cur != NULL;
	     cur = cur->next)
		save(cur->data, NULL, NULL, NULL, NULL);

	return;
}



static void
purple_keyring_set_inuse_check_error_cb(const PurpleAccount * account,
					GError * error,
					gpointer data)
{

	const char * name;
	PurpleKeyringClose close;
	struct _PurpleKeyringChangeTracker * tracker;

	tracker = (PurpleKeyringChangeTracker *)data;

	g_return_if_fail(tracker->abort == FALSE);

	tracker->read_outstanding--;

	name = purple_account_get_username(account);;


	if ((error != NULL) && (error->domain == ERR_PIDGINKEYRING)) {

		tracker->error = error;

		switch(error->code) {

			case ERR_NOCAP :
				purple_debug_info("keyring",
					"Keyring could not save password for account %s : %s\n",
					name, error->message);
				break;

			case ERR_NOPASSWD :
				purple_debug_info("keyring",
					"No password found while changing keyring for account %s : %s\n",
					name, error->message);
				break;

			case ERR_NOCHANNEL :
				purple_debug_info("keyring",
					"Failed to communicate with backend while changing keyring for account %s : %s Aborting changes.\n",
					name, error->message);
				tracker->abort = TRUE;
				break;

			default :
				purple_debug_info("keyring",
					"Unknown error while changing keyring for account %s : %s\n",
					name, error->message);
				break;
		}
	}
	/* if this was the last one */
	if (tracker->finished == TRUE && tracker->read_outstanding == 0) {
	
		if (tracker->abort == TRUE && tracker->force == FALSE) {

			if (tracker->cb != NULL)
				tracker->cb(tracker->old, FALSE, tracker->error, tracker->data);

			purple_keyring_drop_passwords(tracker->new);

			close = purple_keyring_get_close_keyring(tracker->new);
			if (close != NULL)
				close(NULL);

			purple_debug_info("keyring",
				"Failed to change keyring, aborting");

			/**
			 * FIXME : call purple_notify()
			 */
			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);

	return;
}

GList *
purple_keyring_get_options()
{
	const GList * keyrings;
	PurpleKeyring * keyring;
	GList * list = NULL;

	for (keyrings = purple_keyring_get_keyrings();
	     keyrings != NULL;
	     keyrings = keyrings->next) {

		keyring = keyrings->data;
		list = g_list_append(list, keyring->name);
		list = g_list_append(list, keyring->id);
		purple_debug_info("keyring", "adding pair : %s, %s.\n",
			keyring->name, keyring->id);
	}

	return list;
}

PurpleKeyring *
purple_keyring_get_keyring_by_id(const char * id)
{
	const GList * keyrings;
	PurpleKeyring * keyring;

	for (keyrings = purple_keyring_get_keyrings();
	     keyrings != NULL;
	     keyrings = keyrings->next) {

		keyring = keyrings->data;
		if (g_strcmp0(id, keyring->id) == 0)
			return keyring;

	}
	return NULL;
}



void 
purple_keyring_register(PurpleKeyring * keyring)
{
	const char * keyring_id;
	PurpleCore * core;

	g_return_if_fail(keyring != NULL);
	
	keyring_id = purple_keyring_get_id(keyring);

	/* keyring with no ID. Add error handling ? */
	g_return_if_fail(keyring_id != NULL);


	purple_debug_info("keyring", "Registering keyring : %s\n",
		keyring->id);

	/* If this is the configured keyring, use it. */
	if (purple_keyring_inuse == NULL &&
	    g_strcmp0(keyring_id, purple_keyring_to_use) == 0) {

		purple_debug_info("keyring", "Keyring %s matches keyring to use, using it.\n",
			keyring->id);
		purple_keyring_set_inuse(keyring, TRUE, NULL, NULL);

	}

	core = purple_get_core();

	purple_signal_emit(core, "keyring-register", keyring_id, keyring);
	purple_debug_info("keyring", "registered keyring : %s.\n", keyring_id);

	purple_keyring_keyrings = g_list_prepend(purple_keyring_keyrings,
		keyring);
}


void 
purple_keyring_unregister(PurpleKeyring * keyring)
{
	PurpleCore * core;
	const PurpleKeyring * inuse;
	PurpleKeyring * fallback;
	const char * keyring_id;

	g_return_if_fail(keyring != NULL);
	
	purple_debug_info("keyring", 
		"Unregistering keyring %s",
		purple_keyring_get_id(keyring));

	core = purple_get_core();
	keyring_id = purple_keyring_get_id(keyring);
	purple_signal_emit(core, "keyring-unregister", keyring_id, keyring);

	inuse = purple_keyring_get_inuse();
	fallback = purple_keyring_find_keyring_by_id(FALLBACK_KEYRING);

	if (inuse == keyring) {
		if (inuse != fallback) {
			purple_keyring_set_inuse(fallback, TRUE, NULL, NULL);

		} else {
			fallback = NULL;
			purple_keyring_set_inuse(NULL, TRUE, NULL, NULL);
		}
	}

	purple_keyring_keyrings = g_list_remove(purple_keyring_keyrings,
		keyring);

	purple_debug_info("keyring", "Keyring %s unregistered", keyring->id);
}


/*@}*/


/***************************************/
/** @name Keyring plugin wrappers      */
/***************************************/
/*@{*/


gboolean
purple_keyring_import_password(PurpleAccount * account, 
			       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;
	PurpleKeyringCbInfo * cbinfo;

	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 {
				cbinfo = g_malloc(sizeof(PurpleKeyringCbInfo));
				cbinfo->cb = cb;
				cbinfo->data = data;
				save(account, password, destroypassword,
					purple_keyring_set_password_async, data);
			}
		}
	}

	return;
}

void 
purple_keyring_set_password_async_cb(PurpleAccount * account, 
				     GError * error,
				     gpointer data)
{
	PurpleKeyringCbInfo * cbinfo = data;
	PurpleKeyringSaveCallback cb = cbinfo->cb;

	if (error != NULL) {
		/* FIXME : purple_notify_warning() */
	}

	if (cb != NULL)
		cb(account, error, cbinfo->data);
	g_free(data);
}

/**
 * This should be dropped at 3.0 (it's here for compatibility)
 */
const char * 
purple_keyring_get_password_sync(const PurpleAccount * account)
{
	PurpleKeyringReadSync read;
	const PurpleKeyring * inuse;

	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");
}

/*}@*/

mercurial