Index: bitlbee.c
===================================================================
--- bitlbee.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ bitlbee.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -267,14 +267,7 @@
 	if( st == size )
 	{
-		if( irc->status & USTATUS_SHUTDOWN )
-		{
-			irc_free( irc );
-		}
-		else
-		{
-			g_free( irc->sendbuffer );
-			irc->sendbuffer = NULL;
-			irc->w_watch_source_id = 0;
-		}
+		g_free( irc->sendbuffer );
+		irc->sendbuffer = NULL;
+		irc->w_watch_source_id = 0;
 		
 		return FALSE;
Index: configure
===================================================================
--- configure	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ configure	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -39,5 +39,4 @@
 
 events=glib
-ldap=0
 ssl=auto
 
@@ -325,21 +324,4 @@
 		ret=0;
 	fi;
-}
-
-detect_ldap()
-{
-	TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
-	if $CC -o $TMPFILE -shared -lldap 2>/dev/null >/dev/null; then
-		cat<<EOF>>Makefile.settings
-EFLAGS+=-lldap
-CFLAGS+=
-EOF
-		ldap=1
-		rm -f $TMPFILE
-		ret=1
-	else
-		ldap=0
-		ret=0
-	fi
 }
 
@@ -457,20 +439,4 @@
 STORAGES="xml"
 
-if [ "$ldap" = "auto" ]; then
-	detect_ldap
-fi
-
-if [ "$ldap" = 0 ]; then
-	echo "#undef WITH_LDAP" >> config.h
-elif [ "$ldap" = 1 ]; then
-	echo
-	echo 'LDAP support is a work in progress and does NOT work AT ALL right now.'
-	echo
-	exit 1
-	
-	echo "#define WITH_LDAP 1" >> config.h
-	STORAGES="$STORAGES ldap"
-fi
-
 for i in $STORAGES; do
 	STORAGE_OBJS="$STORAGE_OBJS storage_$i.o"
Index: c/bitlbee.schema
===================================================================
--- doc/bitlbee.schema	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ 	(revision )
@@ -1,62 +1,0 @@
-## LDAP Schema file for BitlBee
-## Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>
-##
-## We need the following object classes and related attributes:
-##
-## bitlBeeBuddy:
-##  - nick
-##  - handle
-
-## each bitlBeeNick has zero or more bitlBeeAccount subentries
-## and bitlBeeAccount entries contain zero or more bitlBeeBuddy entries
-
-## The admin needs to setup the LDAP server to:
-## - allow anonymous users to auth against bitlBeeNick objects on the 
-##   password field
-## - allow anonymous users to create new objects that start with nick=
-## - allow read/write for a user that is authenticated only to his/her own 
-##   object and subentries
-
-##  - userid 
-##  - userPassword
-##  - setting (multiple values)
-##  depends: top, account
-
-attributetype ( 1.3.6.1.4.1.25873.2.1.1 NAME 'bitlBeeAutoConnect'
-	DESC 'Autoconnect setting'
-	EQUALITY booleanMatch
-	SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
-
-attributetype ( 1.3.6.1.4.1.25873.2.1.2 NAME 'bitlBeeAccountNo'
-	DESC 'Account number'
-	EQUALITY integerMatch
-	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
-
-objectclass ( 1.3.6.1.4.1.25873.2.2.3 NAME 'bitlBeeAccount' SUP account STRUCTURAL
-	DESC 'BitlBee User Account '
-	MUST ( userid, userPassword )
-	MAY ( ) )
-
-## bitlBeeAccount:
-##  - accountNo 1.3.6.1.4.1.1466.115.121.1.27
-##  - protocol (msn, oscar, jabber, yahoo, ...)
-##  - username
-##  - password
-##  - server name
-##  - autoconnect (true/false) 1.3.6.1.4.1.1466.115.121.1.7
-##  depends: top
-
-objectclass ( 1.3.6.1.4.1.25873.2.2.1 NAME 'bitlBeeIMAccount' SUP account STRUCTURAL
-	DESC 'BitlBee IM Account '
-	MUST ( bitlBeeAccountNo, userid, userPassword )
-	MAY ( host, bitlBeeAutoconnect ) )
-
-objectclass ( 1.3.6.1.4.1.25873.2.2.2 NAME 'bitlBeeSetting' SUP top STRUCTURAL
-	DESC 'BitlBee Configuration Setting'
-	MUST ( bitlBeeSettingName )
-	MAY ( bitlBeeSettingValue ) )
-
-objectclass ( 1.3.6.1.4.1.25873.2.2.3 NAME 'bitlBeeBuddy' SUP top STRUCTURAL
-	DESC 'BitlBee Nick Mapping'
-	MUST ( bitlBeeBuddyHandle )
-	MAY ( ircNick ) )
Index: lib/xmltree.c
===================================================================
--- lib/xmltree.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ lib/xmltree.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -282,5 +282,5 @@
 }
 
-static void xt_to_string_real( struct xt_node *node, GString *str )
+static void xt_to_string_real( struct xt_node *node, GString *str, int indent )
 {
 	char *buf;
@@ -288,4 +288,8 @@
 	int i;
 	
+	if( indent > 1 )
+		g_string_append_len( str, "\n\t\t\t\t\t\t\t\t",
+		                     indent < 8 ? indent : 8 );
+	
 	g_string_append_printf( str, "<%s", node->name );
 	
@@ -312,5 +316,9 @@
 	
 	for( c = node->children; c; c = c->next )
-		xt_to_string_real( c, str );
+		xt_to_string_real( c, str, indent ? indent + 1 : 0 );
+	
+	if( indent > 0 && node->children )
+		g_string_append_len( str, "\n\t\t\t\t\t\t\t\t",
+		                     indent < 8 ? indent : 8 );
 	
 	g_string_append_printf( str, "</%s>", node->name );
@@ -320,72 +328,25 @@
 {
 	GString *ret;
-	char *real;
 	
 	ret = g_string_new( "" );
-	xt_to_string_real( node, ret );
-	
-	real = ret->str;
-	g_string_free( ret, FALSE );
-	
-	return real;
+	xt_to_string_real( node, ret, 0 );
+	return g_string_free( ret, FALSE );
+}
+
+/* WITH indentation! */
+char *xt_to_string_i( struct xt_node *node )
+{
+	GString *ret;
+	
+	ret = g_string_new( "" );
+	xt_to_string_real( node, ret, 1 );
+	return g_string_free( ret, FALSE );
 }
 
 void xt_print( struct xt_node *node )
 {
-	int i;
-	struct xt_node *c;
-	
-	/* Indentation */
-	for( c = node; c->parent; c = c->parent )
-		fprintf( stderr, "    " );
-	
-	/* Start the tag */
-	fprintf( stderr, "<%s", node->name );
-	
-	/* Print the attributes */
-	for( i = 0; node->attr[i].key; i ++ )
-	{
-		char *v = g_markup_escape_text( node->attr[i].value, -1 );
-		fprintf( stderr, " %s=\"%s\"", node->attr[i].key, v );
-		g_free( v );
-	}
-	
-	/* /> in case there's really *nothing* inside this tag, otherwise
-	   just >. */
-	/* If this tag doesn't have any content at all... */
-	if( node->text == NULL && node->children == NULL )
-	{
-		fprintf( stderr, "/>\n" );
-		return;
-		/* Then we're finished! */
-	}
-	
-	/* Otherwise... */
-	fprintf( stderr, ">" );
-	
-	/* Only print the text if it contains more than whitespace (TEST). */
-	if( node->text_len > 0 )
-	{
-		for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
-		if( node->text[i] )
-		{
-			char *v = g_markup_escape_text( node->text, -1 );
-			fprintf( stderr, "%s", v );
-			g_free( v );
-		}
-	}
-	
-	if( node->children )
-		fprintf( stderr, "\n" );
-	
-	for( c = node->children; c; c = c->next )
-		xt_print( c );
-	
-	if( node->children )
-		for( c = node; c->parent; c = c->parent )
-			fprintf( stderr, "    " );
-	
-	/* Non-empty tag is now finished. */
-	fprintf( stderr, "</%s>\n", node->name );
+	char *str = xt_to_string_i( node );
+	fprintf( stderr, "%s", str );
+	g_free( str );
 }
 
Index: lib/xmltree.h
===================================================================
--- lib/xmltree.h	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ lib/xmltree.h	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -84,4 +84,5 @@
 struct xt_node *xt_from_string( const char *in, int text_len );
 char *xt_to_string( struct xt_node *node );
+char *xt_to_string_i( struct xt_node *node );
 void xt_print( struct xt_node *node );
 struct xt_node *xt_dup( struct xt_node *node );
Index: protocols/account.c
===================================================================
--- protocols/account.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ protocols/account.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -53,5 +53,5 @@
 	
 	s = set_add( &a->set, "auto_connect", "true", set_eval_account, a );
-	s->flags |= ACC_SET_NOSAVE;
+	s->flags |= SET_NOSAVE;
 	
 	s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a );
@@ -61,14 +61,14 @@
 	
 	s = set_add( &a->set, "nick_source", "handle", set_eval_nick_source, a );
-	s->flags |= ACC_SET_NOSAVE; /* Just for bw compatibility! */
+	s->flags |= SET_NOSAVE; /* Just for bw compatibility! */
 	
 	s = set_add( &a->set, "password", NULL, set_eval_account, a );
-	s->flags |= ACC_SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
+	s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
 	
 	s = set_add( &a->set, "tag", NULL, set_eval_account, a );
-	s->flags |= ACC_SET_NOSAVE;
+	s->flags |= SET_NOSAVE;
 	
 	s = set_add( &a->set, "username", NULL, set_eval_account, a );
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
+	s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
 	set_setstr( &a->set, "username", user );
 	
Index: protocols/account.h
===================================================================
--- protocols/account.h	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ protocols/account.h	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -61,5 +61,4 @@
 typedef enum
 {
-	ACC_SET_NOSAVE = 0x01,          /* Don't save this setting (i.e. stored elsewhere). */
 	ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
 	ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ protocols/jabber/jabber.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -80,5 +80,5 @@
 	
 	s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
+	s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
 	
 	s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc );
Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ protocols/msn/msn.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -39,5 +39,5 @@
 	
 	s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc );
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
+	s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
 	
 	set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ protocols/oscar/oscar.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -378,5 +378,5 @@
 	            icq ? AIM_DEFAULT_LOGIN_SERVER_ICQ
 	                : AIM_DEFAULT_LOGIN_SERVER_AIM, set_eval_account, acc);
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
+	s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
 	
 	if (icq) {
Index: protocols/skype/skype.c
===================================================================
--- protocols/skype/skype.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ protocols/skype/skype.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -1561,5 +1561,5 @@
 	s = set_add(&acc->set, "display_name", NULL, skype_set_display_name,
 		acc);
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
+	s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
 
 	s = set_add(&acc->set, "mood_text", NULL, skype_set_mood_text, acc);
@@ -1567,8 +1567,8 @@
 
 	s = set_add(&acc->set, "call", NULL, skype_set_call, acc);
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
+	s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
 
 	s = set_add(&acc->set, "balance", NULL, skype_set_balance, acc);
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
+	s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
 
 	s = set_add(&acc->set, "skypeout_offline", "true", set_eval_bool, acc);
Index: protocols/twitter/twitter_lib.c
===================================================================
--- protocols/twitter/twitter_lib.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ protocols/twitter/twitter_lib.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -39,12 +39,4 @@
 #include <ctype.h>
 #include <errno.h>
-
-/* GLib < 2.12.0 doesn't have g_ascii_strtoll(), work around using system strtoll(). */
-/* GLib < 2.12.4 can be buggy: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=488013 */
-#if !GLIB_CHECK_VERSION(2,12,5)
-#include <stdlib.h>
-#include <limits.h>
-#define g_ascii_strtoll strtoll
-#endif
 
 #define TXL_STATUS 1
Index: root_commands.c
===================================================================
--- root_commands.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ root_commands.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -281,5 +281,5 @@
 {
 	if( ( irc->status & USTATUS_IDENTIFIED ) == 0 )
-		irc_rootmsg( irc, "Please create an account first" );
+		irc_rootmsg( irc, "Please create an account first (see \x02help register\x02)" );
 	else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK )
 		irc_rootmsg( irc, "Configuration saved" );
Index: set.h
===================================================================
--- set.h	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ set.h	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -45,9 +45,9 @@
 typedef enum
 {
-	SET_NOSAVE = 0x0001,
-	SET_NULL_OK = 0x0100,
-	SET_HIDDEN = 0x0200,
-	SET_PASSWORD = 0x0400,
-	SET_HIDDEN_DEFAULT = 0x0800,
+	SET_NOSAVE = 0x0001,   /* Don't save this setting (i.e. stored elsewhere). */
+	SET_NULL_OK = 0x0100,  /* set->value == NULL is allowed. */
+	SET_HIDDEN = 0x0200,   /* Don't show up in setting lists. Mostly for internal storage. */
+	SET_PASSWORD = 0x0400, /* Value shows up in settings list as "********". */
+	SET_HIDDEN_DEFAULT = 0x0800, /* Hide unless changed from default. */
 } set_flags_t;
 
Index: storage.c
===================================================================
--- storage.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ storage.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -195,45 +195,2 @@
 	return ret;
 }
-
-#if 0
-Not using this yet. Test thoroughly before adding UI hooks to this function.
-
-storage_status_t storage_rename (const char *onick, const char *nnick, const char *password)
-{
-	storage_status_t status;
-	GList *gl = global.storage;
-	storage_t *primary_storage = gl->data;
-	irc_t *irc;
-
-	/* First, try to rename in the current write backend, assuming onick 
-	 * is stored there */
-	status = primary_storage->rename(onick, nnick, password);
-	if (status != STORAGE_NO_SUCH_USER)
-		return status;
-
-	/* Try to load from a migration backend and save to the current backend. 
-	 * Explicitly remove the account from the migration backend as otherwise 
-	 * it'd still be usable under the old name */
-	
-	irc = g_new0(irc_t, 1);
-	status = storage_load(onick, password, irc);
-	if (status != STORAGE_OK) {
-		irc_free(irc);
-		return status;
-	}
-
-	g_free(irc->nick);
-	irc->nick = g_strdup(nnick);
-
-	status = storage_save(irc, FALSE);
-	if (status != STORAGE_OK) {
-		irc_free(irc);
-		return status;
-	}
-	irc_free(irc);
-
-	storage_remove(onick, password);
-
-	return STORAGE_OK;
-}
-#endif
Index: storage.h
===================================================================
--- storage.h	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ storage.h	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -59,6 +59,4 @@
 storage_status_t storage_remove (const char *nick, const char *password);
 
-/* storage_status_t storage_rename (const char *onick, const char *nnick, const char *password); */
-
 void register_storage_backend(storage_t *);
 G_GNUC_MALLOC GList *storage_init(const char *primary, char **migrate);
Index: orage_ldap.c
===================================================================
--- storage_ldap.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ 	(revision )
@@ -1,177 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2004 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Storage backend that uses a LDAP database */
-
-/* Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> */
-
-/*
-  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 with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-  Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include <ldap.h>
-
-#define BB_LDAP_HOST "localhost"
-#define BB_LDAP_BASE ""
-
-static char *nick_dn(const char *nick)
-{
-	return g_strdup_printf("bitlBeeNick=%s%s%s", nick, BB_LDAP_BASE?",":"", BB_LDAP_BASE?BB_LDAP_BASE:"");
-}
-
-static storage_status_t nick_connect(const char *nick, const char *password, LDAP **ld)
-{
-	char *mydn;
-	int ret;
-	storage_status_t status;
-	*ld = ldap_init(BB_LDAP_HOST, LDAP_PORT);
-
-	if (!ld) {
-		log_message( LOGLVL_WARNING, "Unable to connect to LDAP server at %s", BB_LDAP_HOST );
-		return STORAGE_OTHER_ERROR;
-	}
-
-	mydn = nick_dn(nick);
-
-	ret = ldap_simple_bind_s(*ld, mydn, password);
-
-	switch (ret) {
-	 case LDAP_SUCCESS: status = STORAGE_OK; break;
-	 case LDAP_INVALID_CREDENTIALS: status = STORAGE_INVALID_PASSWORD; break;
-	 default: 
-		log_message( LOGLVL_WARNING, "Unable to authenticate %s: %s", mydn, ldap_err2string(ret) );
-		status = STORAGE_OTHER_ERROR;
-		break;
-	}
-
-	g_free(mydn);
-
-	return status;
-}
-
-static storage_status_t sldap_load ( const char *my_nick, const char* password, irc_t *irc )
-{
-	LDAPMessage *res, *msg;
-	LDAP *ld;
-	int ret, i;
-	storage_status_t status;
-	char *mydn; 
-
-	status = nick_connect(my_nick, password, &ld);
-	if (status != STORAGE_OK)
-		return status;
-
-	mydn = nick_dn(my_nick);
-
-	ret = ldap_search_s(ld, mydn, LDAP_SCOPE_BASE, "(objectClass=*)", NULL, 0, &res);
-
-	if (ret != LDAP_SUCCESS) {
-		log_message( LOGLVL_WARNING, "Unable to search for %s: %s", mydn, ldap_err2string(ret) );
-		ldap_unbind_s(ld);
-		return STORAGE_OTHER_ERROR;
-	}
-
-	g_free(mydn);
-
-	for (msg = ldap_first_entry(ld, res); msg; msg = ldap_next_entry(ld, msg)) {
-	}
-
-	/* FIXME: Store in irc_t */
-
-	ldap_unbind_s(ld);
-	
-	return STORAGE_OK;
-}
-
-static storage_status_t sldap_check_pass( const char *nick, const char *password )
-{
-	LDAP *ld;
-	storage_status_t status;
-
-	status = nick_connect(nick, password, &ld);
-
-	ldap_unbind_s(ld);
-
-	return status;
-}
-
-static storage_status_t sldap_remove( const char *nick, const char *password )
-{
-	storage_status_t status;
-	LDAP *ld;
-	char *mydn;
-	int ret;
-	
-	status = nick_connect(nick, password, &ld);
-
-	if (status != STORAGE_OK)
-		return status;
-
-	mydn = nick_dn(nick);
-	
-	ret = ldap_delete(ld, mydn);
-
-	if (ret != LDAP_SUCCESS) {
-		log_message( LOGLVL_WARNING, "Error removing %s: %s", mydn, ldap_err2string(ret) );
-		ldap_unbind_s(ld);
-		return STORAGE_OTHER_ERROR;
-	}
-
-	ldap_unbind_s(ld);
-
-	g_free(mydn);
-	return STORAGE_OK;
-}
-
-static storage_status_t sldap_save( irc_t *irc, int overwrite )
-{
-	LDAP *ld;
-	char *mydn;
-	storage_status_t status;
-	LDAPMessage *msg;
-
-	status = nick_connect(irc->nick, irc->password, &ld);
-	if (status != STORAGE_OK)
-		return status;
-
-	mydn = nick_dn(irc->nick);
-
-	/* FIXME: Make this a bit more atomic? What if we crash after 
-	 * removing the old account but before adding the new one ? */
-	if (overwrite) 
-		sldap_remove(irc->nick, irc->password);
-
-	g_free(mydn);
-
-	ldap_unbind_s(ld);
-	
-	return STORAGE_OK;
-}
-
-
-
-storage_t storage_ldap = {
-	.name = "ldap",
-	.check_pass = sldap_check_pass,
-	.remove = sldap_remove,
-	.load = sldap_load,
-	.save = sldap_save
-};
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision 9b2a8c10b61540c3c6892a4de7f52bf8657d455e)
+++ storage_xml.c	(revision e31e5b8f340a162180830dbe42dd438e59591cfd)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2006 Wilmer van der Gaast and others                *
+  * Copyright 2002-2012 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -29,12 +29,7 @@
 #include "arc.h"
 #include "md5.h"
-
-#if GLIB_CHECK_VERSION(2,8,0)
+#include "xmltree.h"
+
 #include <glib/gstdio.h>
-#else
-/* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */
-#include <unistd.h>
-#define g_access access
-#endif
 
 typedef enum
@@ -47,300 +42,11 @@
 
 /* To make it easier later when extending the format: */
-#define XML_FORMAT_VERSION 1
+#define XML_FORMAT_VERSION "1"
 
 struct xml_parsedata
 {
 	irc_t *irc;
-	char *current_setting;
-	account_t *current_account;
-	irc_channel_t *current_channel;
-	set_t **current_set_head;
-	char *given_nick;
+	char given_nick[MAX_NICK_LENGTH+1];
 	char *given_pass;
-	xml_pass_st pass_st;
-	int unknown_tag;
-};
-
-static char *xml_attr( const gchar **attr_names, const gchar **attr_values, const gchar *key )
-{
-	int i;
-	
-	for( i = 0; attr_names[i]; i ++ )
-		if( g_strcasecmp( attr_names[i], key ) == 0 )
-			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 = xd->irc;
-	
-	if( xd->unknown_tag > 0 )
-	{
-		xd->unknown_tag ++;
-	}
-	else if( g_strcasecmp( element_name, "user" ) == 0 )
-	{
-		char *nick = xml_attr( attr_names, attr_values, "nick" );
-		char *pass = xml_attr( attr_names, attr_values, "password" );
-		int st;
-		
-		if( !nick || !pass )
-		{
-			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-			             "Missing attributes for %s element", element_name );
-		}
-		else if( ( st = md5_verify_password( xd->given_pass, pass ) ) == -1 )
-		{
-			xd->pass_st = XML_PASS_WRONG;
-			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-			             "Error while decoding password attribute" );
-		}
-		else if( st == 0 )
-		{
-			if( xd->pass_st != XML_PASS_CHECK_ONLY )
-				xd->pass_st = XML_PASS_OK;
-		}
-		else
-		{
-			xd->pass_st = XML_PASS_WRONG;
-			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-			             "Password mismatch" );
-		}
-	}
-	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, *server, *password = NULL, *autoconnect, *tag;
-		char *pass_b64 = NULL;
-		unsigned char *pass_cr = NULL;
-		int pass_len;
-		struct prpl *prpl = NULL;
-		
-		handle = xml_attr( attr_names, attr_values, "handle" );
-		pass_b64 = xml_attr( attr_names, attr_values, "password" );
-		server = xml_attr( attr_names, attr_values, "server" );
-		autoconnect = xml_attr( attr_names, attr_values, "autoconnect" );
-		tag = xml_attr( attr_names, attr_values, "tag" );
-		
-		protocol = xml_attr( attr_names, attr_values, "protocol" );
-		if( protocol )
-			prpl = find_protocol( protocol );
-		
-		if( !handle || !pass_b64 || !protocol )
-			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,
-			             "Unknown protocol: %s", protocol );
-		else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) &&
-		         arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 )
-		{
-			xd->current_account = account_add( irc->b, prpl, handle, password );
-			if( server )
-				set_setstr( &xd->current_account->set, "server", server );
-			if( autoconnect )
-				set_setstr( &xd->current_account->set, "auto_connect", autoconnect );
-			if( tag )
-				set_setstr( &xd->current_account->set, "tag", tag );
-		}
-		else
-		{
-			/* Actually the _decode functions don't even return error codes,
-			   but maybe they will later... */
-			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-			             "Error while decrypting account password" );
-		}
-		
-		g_free( pass_cr );
-		g_free( password );
-	}
-	else if( g_strcasecmp( element_name, "setting" ) == 0 )
-	{
-		char *setting;
-		
-		if( xd->current_setting )
-		{
-			g_free( xd->current_setting );
-			xd->current_setting = NULL;
-		}
-		
-		if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) )
-		{
-			if( xd->current_channel != NULL )
-				xd->current_set_head = &xd->current_channel->set;
-			else if( xd->current_account != NULL )
-				xd->current_set_head = &xd->current_account->set;
-			else
-				xd->current_set_head = &xd->irc->b->set;
-			
-			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 )
-	{
-		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_raw( xd->current_account, handle, nick );
-		}
-		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, "channel" ) == 0 )
-	{
-		char *name, *type;
-		
-		name = xml_attr( attr_names, attr_values, "name" );
-		type = xml_attr( attr_names, attr_values, "type" );
-		
-		if( !name || !type )
-		{
-			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-			             "Missing attributes for %s element", element_name );
-			return;
-		}
-		
-		/* The channel may exist already, for example if it's &bitlbee.
-		   Also, it's possible that the user just reconnected and the
-		   IRC client already rejoined all channels it was in. They
-		   should still get the right settings. */
-		if( ( xd->current_channel = irc_channel_by_name( irc, name ) ) ||
-		    ( xd->current_channel = irc_channel_new( irc, name ) ) )
-			set_setstr( &xd->current_channel->set, "type", type );
-	}
-	/* Backward compatibility: Keep this around for a while for people
-	   switching from BitlBee 1.2.4+. */
-	else if( g_strcasecmp( element_name, "chat" ) == 0 )
-	{
-		char *handle, *channel;
-		
-		handle = xml_attr( attr_names, attr_values, "handle" );
-		channel = xml_attr( attr_names, attr_values, "channel" );
-		
-		if( xd->current_account && handle && channel )
-		{
-			irc_channel_t *ic;
-			
-			if( ( ic = irc_channel_new( irc, channel ) ) &&
-			    set_setstr( &ic->set, "type", "chat" ) &&
-			    set_setstr( &ic->set, "chat_type", "room" ) &&
-			    set_setstr( &ic->set, "account", xd->current_account->tag ) &&
-			    set_setstr( &ic->set, "room", handle ) )
-			{
-				/* Try to pick up some settings where possible. */
-				xd->current_channel = ic;
-			}
-			else if( ic )
-				irc_channel_free( ic );
-		}
-		else
-		{
-			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-			             "Missing attributes for %s element", element_name );
-		}
-	}
-	else
-	{
-		xd->unknown_tag ++;
-		irc_rootmsg( irc, "Warning: Unknown XML tag found in configuration file (%s). "
-		                  "This may happen when downgrading BitlBee versions. "
-		                  "This tag will be skipped and the information will be lost "
-		                  "once you save your settings.", element_name );
-		/*
-		g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
-		             "Unkown element: %s", element_name );
-		*/
-	}
-}
-
-static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
-{
-	struct xml_parsedata *xd = data;
-	
-	if( xd->unknown_tag > 0 )
-	{
-		xd->unknown_tag --;
-	}
-	else if( g_strcasecmp( element_name, "setting" ) == 0 && xd->current_setting )
-	{
-		g_free( xd->current_setting );
-		xd->current_setting = NULL;
-	}
-	else if( g_strcasecmp( element_name, "account" ) == 0 )
-	{
-		xd->current_account = NULL;
-	}
-	else if( g_strcasecmp( element_name, "channel" ) == 0 ||
-	         g_strcasecmp( element_name, "chat" ) == 0 )
-	{
-		xd->current_channel = NULL;
-	}
-}
-
-static void xml_text( GMarkupParseContext *ctx, const gchar *text_orig, gsize text_len, gpointer data, GError **error )
-{
-	char text[text_len+1];
-	struct xml_parsedata *xd = data;
-	
-	strncpy( text, text_orig, text_len );
-	text[text_len] = 0;
-	
-	if( xd->pass_st < XML_PASS_OK )
-	{
-		/* Let's not parse anything else if we only have to check
-		   the password, or if we didn't get the chance to check it
-		   yet. */
-	}
-	else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 && xd->current_setting )
-	{
-		if( xd->current_account )
-		{
-			set_t *s = set_find( xd->current_set_head, xd->current_setting );
-			if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) )
-			{
-				g_free( xd->current_setting );
-				xd->current_setting = NULL;
-				return;
-			}
-		}
-		set_setstr( xd->current_set_head, xd->current_setting, (char*) text );
-		g_free( xd->current_setting );
-		xd->current_setting = NULL;
-	}
-}
-
-GMarkupParser xml_parser =
-{
-	xml_start_element,
-	xml_end_element,
-	xml_text,
-	NULL,
-	NULL
 };
 
@@ -354,64 +60,181 @@
 }
 
+static void handle_settings( struct xt_node *node, set_t **head )
+{
+	struct xt_node *c;
+	
+	for( c = node->children; ( c = xt_find_node( c, "setting" ) ); c = c->next )
+	{
+		char *name = xt_find_attr( c, "name" );
+		
+		if( !name )
+			continue;
+		
+		if( strcmp( node->name, "account" ) == 0 )
+		{
+			set_t *s = set_find( head, name );
+			if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) )
+				continue; /* U can't touch this! */
+		}
+		set_setstr( head, name, c->text );
+	}
+}
+
+static xt_status handle_account( struct xt_node *node, gpointer data )
+{
+	struct xml_parsedata *xd = data;
+	char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag;
+	char *pass_b64 = NULL;
+	unsigned char *pass_cr = NULL;
+	int pass_len;
+	struct prpl *prpl = NULL;
+	account_t *acc;
+	struct xt_node *c;
+	
+	handle = xt_find_attr( node, "handle" );
+	pass_b64 = xt_find_attr( node, "password" );
+	server = xt_find_attr( node, "server" );
+	autoconnect = xt_find_attr( node, "autoconnect" );
+	tag = xt_find_attr( node, "tag" );
+	
+	protocol = xt_find_attr( node, "protocol" );
+	if( protocol )
+		prpl = find_protocol( protocol );
+	
+	if( !handle || !pass_b64 || !protocol || !prpl )
+		return XT_ABORT;
+	else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) &&
+	         arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 )
+	{
+		acc = account_add( xd->irc->b, prpl, handle, password );
+		if( server )
+			set_setstr( &acc->set, "server", server );
+		if( autoconnect )
+			set_setstr( &acc->set, "auto_connect", autoconnect );
+		if( tag )
+			set_setstr( &acc->set, "tag", tag );
+	}
+	else
+		return XT_ABORT;
+	
+	g_free( pass_cr );
+	g_free( password );
+	
+	handle_settings( node, &acc->set );
+	
+	for( c = node->children; ( c = xt_find_node( c, "buddy" ) ); c = c->next )
+	{
+		char *handle, *nick;
+		
+		handle = xt_find_attr( c, "handle" );
+		nick = xt_find_attr( c, "nick" );
+		
+		if( handle && nick )
+			nick_set_raw( acc, handle, nick );
+		else
+			return XT_ABORT;
+	}	
+	return XT_HANDLED;
+}
+
+static xt_status handle_channel( struct xt_node *node, gpointer data )
+{
+	struct xml_parsedata *xd = data;
+	irc_channel_t *ic;
+	char *name, *type;
+	
+	name = xt_find_attr( node, "name" );
+	type = xt_find_attr( node, "type" );
+	
+	if( !name || !type )
+		return XT_ABORT;
+	
+	/* The channel may exist already, for example if it's &bitlbee.
+	   Also, it's possible that the user just reconnected and the
+	   IRC client already rejoined all channels it was in. They
+	   should still get the right settings. */
+	if( ( ic = irc_channel_by_name( xd->irc, name ) ) ||
+	    ( ic = irc_channel_new( xd->irc, name ) ) )
+		set_setstr( &ic->set, "type", type );
+	
+	handle_settings( node, &ic->set );
+	
+	return XT_HANDLED;
+}
+
+static const struct xt_handler_entry handlers[] = {
+	{ "account", "user", handle_account, },
+	{ "channel", "user", handle_channel, },
+	{ NULL,      NULL,   NULL, },
+};
+
 static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action )
 {
-	GMarkupParseContext *ctx;
-	struct xml_parsedata *xd;
-	char *fn, buf[512];
-	GError *gerr = NULL;
+	struct xml_parsedata xd[1];
+	char *fn, buf[2048];
 	int fd, st;
-	
-	xd = g_new0( struct xml_parsedata, 1 );
+	struct xt_parser *xp;
+	struct xt_node *node;
+	storage_status_t ret = STORAGE_OTHER_ERROR;
+	
 	xd->irc = irc;
-	xd->given_nick = g_strdup( my_nick );
-	xd->given_pass = g_strdup( password );
-	xd->pass_st = action;
+	strncpy( xd->given_nick, my_nick, MAX_NICK_LENGTH );
+	xd->given_nick[MAX_NICK_LENGTH] = '\0';
 	nick_lc( xd->given_nick );
-	
-	fn = g_strdup_printf( "%s%s%s", global.conf->configdir, xd->given_nick, ".xml" );
+	xd->given_pass = password;
+	
+	fn = g_strconcat( global.conf->configdir, xd->given_nick, ".xml", NULL );
 	if( ( fd = open( fn, O_RDONLY ) ) < 0 )
 	{
-		xml_destroy_xd( xd );
-		g_free( fn );
-		return STORAGE_NO_SUCH_USER;
-	}
+		ret = STORAGE_NO_SUCH_USER;
+		goto error;
+	}
+	
+	xp = xt_new( handlers, xd );
+	while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 )
+	{
+		st = xt_feed( xp, buf, st );
+		if( st != 1 )
+			break;
+	}
+	close( fd );
+	if( st != 0 )
+		goto error;
+	
+	node = xp->root;
+	if( node == NULL || node->next != NULL || strcmp( node->name, "user" ) != 0 )
+		goto error;
+	
+	{
+		char *nick = xt_find_attr( node, "nick" );
+		char *pass = xt_find_attr( node, "password" );
+		
+		if( !nick || !pass )
+		{
+			goto error;
+		}
+		else if( ( st = md5_verify_password( xd->given_pass, pass ) ) != 0 )
+		{
+			ret = STORAGE_INVALID_PASSWORD;
+			goto error;
+		}
+	}
+	
+	if( action == XML_PASS_CHECK_ONLY )
+	{
+		ret = STORAGE_OK;
+		goto error;
+	}
+	
+	/* DO NOT call xt_handle() before verifying the password! */
+	if( xt_handle( xp, NULL, 1 ) == XT_HANDLED )
+		ret = STORAGE_OK;
+	
+	handle_settings( node, &xd->irc->b->set );
+	
+error:
+	xt_free( xp );
 	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 )
-		{
-			xml_pass_st pass_st = xd->pass_st;
-			
-			g_markup_parse_context_free( ctx );
-			close( fd );
-			
-			if( pass_st == XML_PASS_WRONG )
-			{
-				g_clear_error( &gerr );
-				return STORAGE_INVALID_PASSWORD;
-			}
-			else
-			{
-				if( gerr && irc )
-					irc_rootmsg( irc, "Error from XML-parser: %s", gerr->message );
-				
-				g_clear_error( &gerr );
-				return STORAGE_OTHER_ERROR;
-			}
-		}
-	}
-	/* Just to be sure... */
-	g_clear_error( &gerr );
-	
-	g_markup_parse_context_free( ctx );
-	close( fd );
-	
-	if( action == XML_PASS_CHECK_ONLY )
-		return STORAGE_OK;
-	
-	return STORAGE_OK;
+	return ret;
 }
 
@@ -423,57 +246,19 @@
 static storage_status_t xml_check_pass( const char *my_nick, const char *password )
 {
-	/* This is a little bit risky because we have to pass NULL for the
-	   irc_t argument. This *should* be fine, if I didn't miss anything... */
 	return xml_load_real( NULL, my_nick, password, XML_PASS_CHECK_ONLY );
 }
 
-static int xml_printf( int fd, int indent, char *fmt, ... )
-{
-	va_list params;
-	char *out;
-	char tabs[9] = "\t\t\t\t\t\t\t\t";
-	int len;
-	
-	/* Maybe not very clean, but who needs more than 8 levels of indentation anyway? */
-	if( write( fd, tabs, indent <= 8 ? indent : 8 ) != indent )
-		return 0;
-	
-	va_start( params, fmt );
-	out = g_markup_vprintf_escaped( fmt, params );
-	va_end( params );
-	
-	len = strlen( out );
-	len -= write( fd, out, len );
-	g_free( out );
-	
-	return len == 0;
-}
-
-static gboolean xml_save_nick( gpointer key, gpointer value, gpointer data );
-
-static storage_status_t xml_save( irc_t *irc, int overwrite )
-{
-	char path[512], *path2, *pass_buf = NULL;
-	set_t *set;
+
+static gboolean xml_generate_nick( gpointer key, gpointer value, gpointer data );
+static void xml_generate_settings( struct xt_node *cur, set_t **head );
+
+struct xt_node *xml_generate( irc_t *irc )
+{
+	char *pass_buf = NULL;
 	account_t *acc;
-	int fd;
 	md5_byte_t pass_md5[21];
 	md5_state_t md5_state;
 	GSList *l;
-	
-	path2 = g_strdup( irc->user->nick );
-	nick_lc( path2 );
-	g_snprintf( path, sizeof( path ) - 2, "%s%s%s", global.conf->configdir, path2, ".xml" );
-	g_free( path2 );
-	
-	if( !overwrite && g_access( path, F_OK ) == 0 )
-		return STORAGE_ALREADY_EXISTS;
-	
-	strcat( path, ".XXXXXX" );
-	if( ( fd = mkstemp( path ) ) < 0 )
-	{
-		irc_rootmsg( irc, "Error while opening configuration file." );
-		return STORAGE_OTHER_ERROR;
-	}
+	struct xt_node *root, *cur;
 	
 	/* Generate a salted md5sum of the password. Use 5 bytes for the salt
@@ -488,13 +273,12 @@
 	pass_buf = base64_encode( pass_md5, 21 );
 	
-	if( !xml_printf( fd, 0, "<user nick=\"%s\" password=\"%s\" version=\"%d\">\n", irc->user->nick, pass_buf, XML_FORMAT_VERSION ) )
-		goto write_error;
+	root = cur = xt_new_node( "user", NULL, NULL );
+	xt_add_attr( cur, "nick", irc->user->nick );
+	xt_add_attr( cur, "password", pass_buf );
+	xt_add_attr( cur, "version", XML_FORMAT_VERSION );
 	
 	g_free( pass_buf );
 	
-	for( set = irc->b->set; set; set = set->next )
-		if( set->value && !( set->flags & SET_NOSAVE ) )
-			if( !xml_printf( fd, 1, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) )
-				goto write_error;
+	xml_generate_settings( cur, &irc->b->set );
 	
 	for( acc = irc->b->accounts; acc; acc = acc->next )
@@ -508,35 +292,27 @@
 		g_free( pass_cr );
 		
-		if( !xml_printf( fd, 1, "<account protocol=\"%s\" handle=\"%s\" password=\"%s\" "
-		                        "autoconnect=\"%d\" tag=\"%s\"", acc->prpl->name, acc->user,
-		                        pass_b64, acc->auto_connect, acc->tag ) )
-		{
-			g_free( pass_b64 );
-			goto write_error;
-		}
+		cur = xt_new_node( "account", NULL, NULL );
+		xt_add_attr( cur, "protocol", acc->prpl->name );
+		xt_add_attr( cur, "handle", acc->user );
+		xt_add_attr( cur, "password", pass_b64 );
+		xt_add_attr( cur, "autoconnect", acc->auto_connect ? "true" : "false" );
+		xt_add_attr( cur, "tag", acc->tag );
+		if( acc->server && acc->server[0] )
+			xt_add_attr( cur, "server", acc->server );
+		
 		g_free( pass_b64 );
-		
-		if( acc->server && acc->server[0] && !xml_printf( fd, 0, " server=\"%s\"", acc->server ) )
-			goto write_error;
-		if( !xml_printf( fd, 0, ">\n" ) )
-			goto write_error;
-		
-		for( set = acc->set; set; set = set->next )
-			if( set->value && !( set->flags & ACC_SET_NOSAVE ) )
-				if( !xml_printf( fd, 2, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) )
-					goto write_error;
 		
 		/* This probably looks pretty strange. g_hash_table_foreach
 		   is quite a PITA already (but it can't get much better in
-		   C without using #define, I'm afraid), and since it
+		   C without using #define, I'm afraid), and it
 		   doesn't seem to be possible to abort the foreach on write
 		   errors, so instead let's use the _find function and
 		   return TRUE on write errors. Which means, if we found
 		   something, there was an error. :-) */
-		if( g_hash_table_find( acc->nicks, xml_save_nick, & fd ) )
-			goto write_error;
-		
-		if( !xml_printf( fd, 1, "</account>\n" ) )
-			goto write_error;
+		g_hash_table_find( acc->nicks, xml_generate_nick, cur );
+		
+		xml_generate_settings( cur, &acc->set );
+		
+		xt_add_child( root, cur );
 	}
 	
@@ -548,51 +324,93 @@
 			continue;
 		
-		if( !xml_printf( fd, 1, "<channel name=\"%s\" type=\"%s\">\n",
-		                 ic->name, set_getstr( &ic->set, "type" ) ) )
-			goto write_error;
-		
-		for( set = ic->set; set; set = set->next )
-			if( set->value && strcmp( set->key, "type" ) != 0 )
-				if( !xml_printf( fd, 2, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) )
-					goto write_error;
-		
-		if( !xml_printf( fd, 1, "</channel>\n" ) )
-			goto write_error;
-	}
-	
-	if( !xml_printf( fd, 0, "</user>\n" ) )
-		goto write_error;
-	
-	fsync( fd );
-	close( fd );
+		cur = xt_new_node( "channel", NULL, NULL );
+		xt_add_attr( cur, "name", ic->name );
+		xt_add_attr( cur, "type", set_getstr( &ic->set, "type" ) );
+		
+		xml_generate_settings( cur, &ic->set );
+		
+		xt_add_child( root, cur );
+	}
+	
+	return root;
+}
+
+static gboolean xml_generate_nick( gpointer key, gpointer value, gpointer data )
+{
+	struct xt_node *node = xt_new_node( "buddy", NULL, NULL );
+	xt_add_attr( node, "handle", key );
+	xt_add_attr( node, "nick", value );
+	xt_add_child( (struct xt_node *) data, node );
+	
+	return FALSE;
+}
+
+static void xml_generate_settings( struct xt_node *cur, set_t **head )
+{
+	set_t *set;
+	
+	for( set = *head; set; set = set->next )
+		if( set->value && !( set->flags & SET_NOSAVE ) )
+		{
+			struct xt_node *xset;
+			xt_add_child( cur, xset = xt_new_node( "setting", set->value, NULL ) );
+			xt_add_attr( xset, "name", set->key );
+		}
+}
+
+static storage_status_t xml_save( irc_t *irc, int overwrite )
+{
+	storage_status_t ret = STORAGE_OK;
+	char path[512], *path2 = NULL, *xml = NULL;
+	struct xt_node *tree = NULL;
+	size_t len;
+	int fd;
+	
+	path2 = g_strdup( irc->user->nick );
+	nick_lc( path2 );
+	g_snprintf( path, sizeof( path ) - 20, "%s%s%s", global.conf->configdir, path2, ".xml" );
+	g_free( path2 );
+	
+	if( !overwrite && g_access( path, F_OK ) == 0 )
+		return STORAGE_ALREADY_EXISTS;
+	
+	strcat( path, ".XXXXXX" );
+	if( ( fd = mkstemp( path ) ) < 0 )
+	{
+		irc_rootmsg( irc, "Error while opening configuration file." );
+		return STORAGE_OTHER_ERROR;
+	}
+	
+	tree = xml_generate( irc );
+	xml = xt_to_string_i( tree );
+	len = strlen( xml );
+	if( write( fd, xml, len ) != len ||
+	    fsync( fd ) != 0 || /* #559 */
+	    close( fd ) != 0 )
+		goto error;
 	
 	path2 = g_strndup( path, strlen( path ) - 7 );
 	if( rename( path, path2 ) != 0 )
 	{
-		irc_rootmsg( irc, "Error while renaming temporary configuration file." );
-		
 		g_free( path2 );
-		unlink( path );
-		
-		return STORAGE_OTHER_ERROR;
-	}
-	
+		goto error;
+	}
 	g_free( path2 );
 	
-	return STORAGE_OK;
-
-write_error:
-	g_free( pass_buf );
-	
+	goto finish;
+
+error:
 	irc_rootmsg( irc, "Write error. Disk full?" );
+	ret = STORAGE_OTHER_ERROR;
+
+finish:	
 	close( fd );
-	
-	return STORAGE_OTHER_ERROR;
-}
-
-static gboolean xml_save_nick( gpointer key, gpointer value, gpointer data )
-{
-	return !xml_printf( *( (int*) data ), 2, "<buddy handle=\"%s\" nick=\"%s\" />\n", key, value );
-}
+	unlink( path );
+	g_free( xml );
+	xt_free_node( tree );
+	
+	return ret;
+}
+
 
 static storage_status_t xml_remove( const char *nick, const char *password )
