Index: Makefile
===================================================================
--- Makefile	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
+++ Makefile	(revision c121f8945f7249520342ad86ff00f4986642ca0a)
@@ -10,5 +10,5 @@
 
 # Program variables
-objects = account.o bitlbee.o conf.o crypting.o help.o ini.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_text.o unix.o url.o user.o util.o
+objects = account.o bitlbee.o conf.o crypting.o help.o ini.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_text.o storage_xml.o unix.o url.o user.o util.o
 headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ini.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h url.h user.h protocols/http_client.h protocols/md5.h protocols/nogaim.h protocols/proxy.h protocols/sha.h protocols/ssl_client.h
 subdirs = protocols
Index: conf.c
===================================================================
--- conf.c	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
+++ conf.c	(revision c121f8945f7249520342ad86ff00f4986642ca0a)
@@ -55,5 +55,5 @@
 	conf->nofork = 0;
 	conf->verbose = 0;
-	conf->primary_storage = "text";
+	conf->primary_storage = "xml";
 	conf->runmode = RUNMODE_INETD;
 	conf->authmode = AUTHMODE_OPEN;
Index: root_commands.c
===================================================================
--- root_commands.c	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
+++ root_commands.c	(revision c121f8945f7249520342ad86ff00f4986642ca0a)
@@ -142,6 +142,7 @@
 		irc_umode_set( irc, "+R", 1 );
 		break;
+	case STORAGE_OTHER_ERROR:
 	default:
-		irc_usermsg( irc, "Something very weird happened" );
+		irc_usermsg( irc, "Unknown error while loading configuration" );
 		break;
 	}
Index: storage.c
===================================================================
--- storage.c	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
+++ storage.c	(revision c121f8945f7249520342ad86ff00f4986642ca0a)
@@ -31,7 +31,7 @@
 
 extern storage_t storage_text;
+extern storage_t storage_xml;
 
-static GList text_entry = { &storage_text, NULL, NULL };
-static GList *storage_backends = &text_entry;
+static GList *storage_backends = NULL;
 
 void register_storage_backend(storage_t *backend)
@@ -65,5 +65,8 @@
 	int i;
 	storage_t *storage;
-
+	
+	register_storage_backend(&storage_text);
+	register_storage_backend(&storage_xml);
+	
 	storage = storage_init_single(primary);
 	if (storage == NULL)
Index: storage.h
===================================================================
--- storage.h	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
+++ storage.h	(revision c121f8945f7249520342ad86ff00f4986642ca0a)
@@ -33,6 +33,6 @@
 	STORAGE_ALREADY_EXISTS,
 	STORAGE_OTHER_ERROR /* Error that isn't caused by user input, such as 
-						   a database that is unreachable. log() will be 
-						   used for the exact error message */
+	                       a database that is unreachable. log() will be 
+	                       used for the exact error message */
 } storage_status_t;
 
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
+++ storage_xml.c	(revision c121f8945f7249520342ad86ff00f4986642ca0a)
@@ -27,4 +27,13 @@
 #include "bitlbee.h"
 
+typedef enum
+{
+	XML_PASS_CHECK_ONLY = -1,
+	XML_PASS_UNKNOWN = 0,
+	XML_PASS_OK
+} xml_pass_st;
+
+#define XML_PASS_ERRORMSG "Wrong username or password"
+
 struct xml_parsedata
 {
@@ -32,4 +41,7 @@
 	char *current_setting;
 	account_t *current_account;
+	char *given_nick;
+	char *given_pass;
+	xml_pass_st pass_st;
 };
 
@@ -40,30 +52,58 @@
 	for( i = 0; attr_names[i]; i ++ )
 		if( g_strcasecmp( attr_names[i], key ) == 0 )
-			return attr_values[i];
+			return (char*) attr_values[i];
 	
 	return NULL;
 }
 
+static void xml_destroy_xd( gpointer data )
+{
+	struct xml_parsedata *xd = data;
+	
+	g_free( xd->given_nick );
+	g_free( xd->given_pass );
+	g_free( xd );
+}
+
 static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
 {
 	struct xml_parsedata *xd = data;
-	irc_t *irc = data->irc;
+	irc_t *irc = xd->irc;
 	
 	if( g_strcasecmp( element_name, "user" ) == 0 )
 	{
 		char *nick = xml_attr( attr_names, attr_values, "nick" );
-		
-		if( nick && g_strcasecmp( nick, irc->nick ) == 0 )
-		{
-			/* Okay! */
-		}
+		char *pass = xml_attr( attr_names, attr_values, "password" );
+		
+		if( !nick || !pass )
+		{
+			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+			             "Missing attributes for %s element", element_name );
+		}
+		else if( strcmp( nick, xd->given_nick ) == 0 &&
+		         strcmp( pass, xd->given_pass ) == 0 )
+		{
+			if( xd->pass_st != XML_PASS_CHECK_ONLY )
+				xd->pass_st = XML_PASS_OK;
+		}
+		else
+		{
+			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+			             XML_PASS_ERRORMSG );
+		}
+	}
+	else if( xd->pass_st < XML_PASS_OK )
+	{
+		/* Let's not parse anything else if we only have to check
+		   the password. */
 	}
 	else if( g_strcasecmp( element_name, "account" ) == 0 )
 	{
-		char *protocol, *handle, *password;
+		char *protocol, *handle, *server, *password;
 		struct prpl *prpl = NULL;
 		
 		handle = xml_attr( attr_names, attr_values, "handle" );
 		password = xml_attr( attr_names, attr_values, "password" );
+		server = xml_attr( attr_names, attr_values, "server" );
 		
 		protocol = xml_attr( attr_names, attr_values, "protocol" );
@@ -71,7 +111,15 @@
 			prpl = find_protocol( protocol );
 		
-		if( handle && password && prpl )
-		{
-			xd->current_account = account_add( irc, prpl, handle, password )
+		if( !handle || !password )
+			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+			             "Missing attributes for %s element", element_name );
+		else if( !prpl )
+			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+			             "Missing or unknown protocol %s element", element_name );
+		else
+		{
+			xd->current_account = account_add( irc, prpl, handle, password );
+			if( server )
+				xd->current_account->server = g_strdup( server );
 		}
 	}
@@ -80,16 +128,40 @@
 		if( xd->current_account == NULL )
 		{
-			current_setting = xml_attr( attr_names, attr_values, "name" );
+			char *setting;
+			
+			if( xd->current_setting )
+			{
+				g_free( xd->current_setting );
+				xd->current_setting = NULL;
+			}
+			
+			if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) )
+				xd->current_setting = g_strdup( setting );
+			else
+				g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+				             "Missing attributes for %s element", element_name );
 		}
 	}
 	else if( g_strcasecmp( element_name, "buddy" ) == 0 )
 	{
-	}
-	else if( g_strcasecmp( element_name, "password" ) == 0 )
-	{
+		char *handle, *nick;
+		
+		handle = xml_attr( attr_names, attr_values, "handle" );
+		nick = xml_attr( attr_names, attr_values, "nick" );
+		
+		if( xd->current_account && handle && nick )
+		{
+			nick_set( irc, handle, xd->current_account->prpl, nick );
+		}
+		else
+		{
+			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+			             "Missing attributes for %s element", element_name );
+		}
 	}
 	else
 	{
-		/* Return "unknown element" error. */
+		g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+		             "Unkown element: %s", element_name );
 	}
 }
@@ -102,9 +174,17 @@
 {
 	struct xml_parsedata *xd = data;
-	irc_t *irc = data->irc;
-	
-	if( xd->current_setting )
-	{
-		set_setstr( irc, xd->current_setting, text );
+	irc_t *irc = xd->irc;
+	
+	if( xd->pass_st < XML_PASS_OK )
+	{
+		/* Let's not parse anything else if we only have to check
+		   the password. */
+	}
+	else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 &&
+	         xd->current_setting && xd->current_account == NULL )
+	{
+		set_setstr( irc, xd->current_setting, (char*) text );
+		g_free( xd->current_setting );
+		xd->current_setting = NULL;
 	}
 }
@@ -131,59 +211,56 @@
 }
 
-static storage_status_t xml_load ( const char *my_nick, const char* password, irc_t *irc )
+static storage_status_t xml_load( const char *my_nick, const char *password, irc_t *irc )
 {
 	GMarkupParseContext *ctx;
-	
-	ctx = g_markup_parse_context_new( parser, 0, xd, NULL );
+	struct xml_parsedata *xd;
+	char *fn, buf[512];
+	GError *gerr = NULL;
+	int fd, st;
+	
 	if( irc->status >= USTATUS_IDENTIFIED )
 		return( 1 );
 	
-	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".accounts" );
-   	fp = fopen( s, "r" );
-   	if( !fp ) return STORAGE_NO_SUCH_USER;
-	
-	fscanf( fp, "%32[^\n]s", s );
-
-	if (checkpass (password, s) != 0) 
-	{
-		fclose( fp );
-		return STORAGE_INVALID_PASSWORD;
-	}
-	
-	/* Do this now. If the user runs with AuthMode = Registered, the
-	   account command will not work otherwise. */
+	xd = g_new0( struct xml_parsedata, 1 );
+	xd->irc = irc;
+	xd->given_nick = g_strdup( my_nick );
+	xd->given_pass = g_strdup( password );
+	nick_lc( xd->given_nick );
+	
+	fn = g_strdup_printf( "%s%s%s", global.conf->configdir, xd->given_nick, ".xml" );
+	if( ( fd = open( fn, O_RDONLY ) ) < 0 )
+	{
+		xml_destroy_xd( xd );
+		g_free( fn );
+		return STORAGE_NO_SUCH_USER;
+	}
+	g_free( fn );
+	
+	ctx = g_markup_parse_context_new( &xml_parser, 0, xd, xml_destroy_xd );
+	
+	while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 )
+	{
+		if( !g_markup_parse_context_parse( ctx, buf, st, &gerr ) || gerr )
+		{
+			g_markup_parse_context_free( ctx );
+			
+			/* TODO: Display useful error msg */
+			
+			if( gerr && strcmp( gerr->message, XML_PASS_ERRORMSG ) == 0 )
+				return STORAGE_INVALID_PASSWORD;
+			else
+				return STORAGE_OTHER_ERROR;
+		}
+	}
+	
+	g_markup_parse_context_free( ctx );
+	
 	irc->status = USTATUS_IDENTIFIED;
 	
-	while( fscanf( fp, "%511[^\n]s", s ) > 0 )
-	{
-		fgetc( fp );
-		line = deobfucrypt( s, password );
-		if (line == NULL) return STORAGE_OTHER_ERROR;
-		root_command_string( irc, ru, line, 0 );
-		g_free( line );
-	}
-	fclose( fp );
-	
-	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".nicks" );
-	fp = fopen( s, "r" );
-	if( !fp ) return STORAGE_NO_SUCH_USER;
-	while( fscanf( fp, "%s %d %s", s, &proto, nick ) > 0 )
-	{
-		struct prpl *prpl;
-
-		prpl = find_protocol_by_id(proto);
-
-		if (!prpl)
-			continue;
-
-		http_decode( s );
-		nick_set( irc, s, prpl, nick );
-	}
-	fclose( fp );
-	
 	if( set_getint( irc, "auto_connect" ) )
 	{
-		strcpy( s, "account on" );	/* Can't do this directly because r_c_s alters the string */
-		root_command_string( irc, ru, s, 0 );
+		/* Can't do this directly because r_c_s alters the string */
+		strcpy( buf, "account on" );
+		root_command_string( irc, NULL, buf, 0 );
 	}
 	
@@ -191,17 +268,7 @@
 }
 
-static storage_status_t text_save( irc_t *irc, int overwrite )
-{
-	char s[512];
-	char path[512], new_path[512];
-	char *line;
-	nick_t *n;
-	set_t *set;
-	mode_t ou = umask( 0077 );
-	account_t *a;
-	FILE *fp;
-	char *hash;
-
-	if (!overwrite) {
+static storage_status_t xml_save( irc_t *irc, int overwrite )
+{
+/*	if (!overwrite) {
 		g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" );
 		if (access( path, F_OK ) != -1)
@@ -212,192 +279,5 @@
 			return STORAGE_ALREADY_EXISTS;
 	}
-	
-	/*\
-	 *  [SH] Nothing should be saved if no password is set, because the
-	 *  password is not set if it was wrong, or if one is not identified
-	 *  yet. This means that a malicious user could easily overwrite
-	 *  files owned by someone else:
-	 *  a Bad Thing, methinks
-	\*/
-
-	/* [WVG] No? Really? */
-
-	/*\
-	 *  [SH] Okay, okay, it wasn't really Wilmer who said that, it was
-	 *  me. I just thought it was funny.
-	\*/
-	
-	hash = hashpass( irc->password );
-	if( hash == NULL )
-	{
-		irc_usermsg( irc, "Please register yourself if you want to save your settings." );
-		return STORAGE_OTHER_ERROR;
-	}
-	
-	g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks~" );
-	fp = fopen( path, "w" );
-	if( !fp ) return STORAGE_OTHER_ERROR;
-	for( n = irc->nicks; n; n = n->next )
-	{
-		strcpy( s, n->handle );
-		s[169] = 0; /* Prevent any overflow (169 ~ 512 / 3) */
-		http_encode( s );
-		g_snprintf( s + strlen( s ), 510 - strlen( s ), " %d %s", find_protocol_id(n->proto->name), n->nick );
-		if( fprintf( fp, "%s\n", s ) != strlen( s ) + 1 )
-		{
-			irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
-			fclose( fp );
-			return STORAGE_OTHER_ERROR;
-		}
-	}
-	if( fclose( fp ) != 0 )
-	{
-		irc_usermsg( irc, "fclose() reported an error. Disk full?" );
-		return STORAGE_OTHER_ERROR;
-	}
-  
-	g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" );
-	if( unlink( new_path ) != 0 )
-	{
-		if( errno != ENOENT )
-		{
-			irc_usermsg( irc, "Error while removing old .nicks file" );
-			return STORAGE_OTHER_ERROR;
-		}
-	}
-	if( rename( path, new_path ) != 0 )
-	{
-		irc_usermsg( irc, "Error while renaming new .nicks file" );
-		return STORAGE_OTHER_ERROR;
-	}
-	
-	g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts~" );
-	fp = fopen( path, "w" );
-	if( !fp ) return STORAGE_OTHER_ERROR;
-	if( fprintf( fp, "%s", hash ) != strlen( hash ) )
-	{
-		irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
-		fclose( fp );
-		return STORAGE_OTHER_ERROR;
-	}
-	g_free( hash );
-
-	for( a = irc->accounts; a; a = a->next )
-	{
-		if( !strcmp(a->prpl->name, "oscar") )
-			g_snprintf( s, sizeof( s ), "account add oscar \"%s\" \"%s\" %s", a->user, a->pass, a->server );
-		else
-			g_snprintf( s, sizeof( s ), "account add %s \"%s\" \"%s\" \"%s\"",
-			            a->prpl->name, a->user, a->pass, a->server ? a->server : "" );
-		
-		line = obfucrypt( s, irc->password );
-		if( *line )
-		{
-			if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 )
-			{
-				irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
-				fclose( fp );
-				return STORAGE_OTHER_ERROR;
-			}
-		}
-		g_free( line );
-	}
-	
-	for( set = irc->set; set; set = set->next )
-	{
-		if( set->value && set->def )
-		{
-			g_snprintf( s, sizeof( s ), "set %s \"%s\"", set->key, set->value );
-			line = obfucrypt( s, irc->password );
-			if( *line )
-			{
-				if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 )
-				{
-					irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
-					fclose( fp );
-					return STORAGE_OTHER_ERROR;
-				}
-			}
-			g_free( line );
-		}
-	}
-	
-	if( strcmp( irc->mynick, ROOT_NICK ) != 0 )
-	{
-		g_snprintf( s, sizeof( s ), "rename %s %s", ROOT_NICK, irc->mynick );
-		line = obfucrypt( s, irc->password );
-		if( *line )
-		{
-			if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 )
-			{
-				irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
-				fclose( fp );
-				return STORAGE_OTHER_ERROR;
-			}
-		}
-		g_free( line );
-	}
-	if( fclose( fp ) != 0 )
-	{
-		irc_usermsg( irc, "fclose() reported an error. Disk full?" );
-		return STORAGE_OTHER_ERROR;
-	}
-	
- 	g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" );
- 	if( unlink( new_path ) != 0 )
-	{
-		if( errno != ENOENT )
-		{
-			irc_usermsg( irc, "Error while removing old .accounts file" );
-			return STORAGE_OTHER_ERROR;
-		}
-	}
-	if( rename( path, new_path ) != 0 )
-	{
-		irc_usermsg( irc, "Error while renaming new .accounts file" );
-		return STORAGE_OTHER_ERROR;
-	}
-	
-	umask( ou );
-	
-	return STORAGE_OK;
-}
-
-static storage_status_t text_check_pass( const char *nick, const char *password )
-{
-	char s[512];
-	FILE *fp;
-	
-	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" );
-	fp = fopen( s, "r" );
-	if (!fp)
-		return STORAGE_NO_SUCH_USER;
-
-	fscanf( fp, "%32[^\n]s", s );
-	fclose( fp );
-
-	if (checkpass( password, s) == -1)
-		return STORAGE_INVALID_PASSWORD;
-
-	return STORAGE_OK;
-}
-
-static storage_status_t text_remove( const char *nick, const char *password )
-{
-	char s[512];
-	storage_status_t status;
-
-	status = text_check_pass( nick, password );
-	if (status != STORAGE_OK)
-		return status;
-
-	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" );
-	if (unlink( s ) == -1)
-		return STORAGE_OTHER_ERROR;
-	
-	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".nicks" );
-	if (unlink( s ) == -1)
-		return STORAGE_OTHER_ERROR;
-
+*/	
 	return STORAGE_OK;
 }
@@ -406,6 +286,6 @@
 	.name = "xml",
 	.init = xml_init,
-	.check_pass = xml_check_pass,
-	.remove = xml_remove,
+//	.check_pass = xml_check_pass,
+//	.remove = xml_remove,
 	.load = xml_load,
 	.save = xml_save
