Index: .bzrignore
===================================================================
--- .bzrignore	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ .bzrignore	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -17,2 +17,5 @@
 *.gcno
 *.o
+coverage
+bitlbee.info
+bitlbee.exe
Index: Makefile
===================================================================
--- Makefile	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ Makefile	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -10,7 +10,17 @@
 
 # Program variables
-objects = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o otr.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) unix.o user.o
+objects = account.o bitlbee.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o
 headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h otr.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h
 subdirs = lib protocols
+
+ifeq ($(TARGET),i586-mingw32msvc)
+objects += win32.o
+LFLAGS+=-lws2_32
+EFLAGS+=-lsecur32
+OUTFILE=bitlbee.exe
+else
+objects += unix.o conf.o log.o otr.o
+OUTFILE=bitlbee
+endif
 
 # Expansion of variables
Index: account.c
===================================================================
--- account.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ account.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -182,17 +182,15 @@
 	account_t *a, *l = NULL;
 	
+	if( acc->ic )
+		/* Caller should have checked, accounts still in use can't be deleted. */
+		return;
+	
 	for( a = irc->accounts; a; a = (l=a)->next )
 		if( a == acc )
 		{
-			if( a->ic ) return; /* Caller should have checked, accounts still in use can't be deleted. */
-			
 			if( l )
-			{
 				l->next = a->next;
-			}
 			else
-			{
 				irc->accounts = a->next;
-			}
 			
 			while( a->set )
@@ -203,5 +201,5 @@
 			g_free( a->user );
 			g_free( a->pass );
-			if( a->server ) g_free( a->server );
+			g_free( a->server );
 			if( a->reconnect )	/* This prevents any reconnect still queued to happen */
 				cancel_auto_reconnect( a );
Index: bitlbee.c
===================================================================
--- bitlbee.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ bitlbee.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -54,9 +54,9 @@
 	;
 
-	i = getaddrinfo( global.conf->iface, global.conf->port, &hints, &addrinfo_bind );
+	i = getaddrinfo( global.conf->iface_in, global.conf->port, &hints, &addrinfo_bind );
 	if( i )
 	{
 		log_message( LOGLVL_ERROR, "Couldn't parse address `%s': %s",
-		                           global.conf->iface, gai_strerror(i) );
+		                           global.conf->iface_in, gai_strerror(i) );
 		return -1;
 	}
@@ -118,9 +118,10 @@
 	
 	if( global.conf->runmode == RUNMODE_FORKDAEMON )
-		ipc_master_load_state();
+		ipc_master_load_state( getenv( "_BITLBEE_RESTART_STATE" ) );
 
 	if( global.conf->runmode == RUNMODE_DAEMON || global.conf->runmode == RUNMODE_FORKDAEMON )
 		ipc_master_listen_socket();
 	
+#ifndef _WIN32
 	if( ( fp = fopen( global.conf->pidfile, "w" ) ) )
 	{
@@ -132,4 +133,5 @@
 		log_message( LOGLVL_WARNING, "Warning: Couldn't write PID to `%s'", global.conf->pidfile );
 	}
+#endif
 	
 	return( 0 );
@@ -140,7 +142,4 @@
 	if( !irc_new( 0 ) )
 		return( 1 );
-	
-	log_link( LOGLVL_ERROR, LOGOUTPUT_IRC );
-	log_link( LOGLVL_WARNING, LOGOUTPUT_IRC );
 	
 	return( 0 );
@@ -226,10 +225,14 @@
 	if( st == size )
 	{
-		g_free( irc->sendbuffer );
-		irc->sendbuffer = NULL;
-		irc->w_watch_source_id = 0;
-		
 		if( irc->status & USTATUS_SHUTDOWN )
+		{
 			irc_free( irc );
+		}
+		else
+		{
+			g_free( irc->sendbuffer );
+			irc->sendbuffer = NULL;
+			irc->w_watch_source_id = 0;
+		}
 		
 		return FALSE;
@@ -250,5 +253,4 @@
 	struct sockaddr_in conn_info;
 	int new_socket = accept( global.listen_socket, (struct sockaddr *) &conn_info, &size );
-	pid_t client_pid = 0;
 	
 	if( new_socket == -1 )
@@ -258,6 +260,8 @@
 	}
 	
+#ifndef _WIN32
 	if( global.conf->runmode == RUNMODE_FORKDAEMON )
 	{
+		pid_t client_pid = 0;
 		int fds[2];
 		
@@ -316,4 +320,5 @@
 	}
 	else
+#endif
 	{
 		log_message( LOGLVL_INFO, "Creating new connection with fd %d.", new_socket );
Index: bitlbee.conf
===================================================================
--- bitlbee.conf	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ bitlbee.conf	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -10,8 +10,7 @@
 ##
 ##  Inetd -- Run from inetd (default)
-##  Daemon -- Run as a stand-alone daemon -- EXPERIMENTAL! BitlBee is not yet
-##    stable enough to serve lots of users from one process. Because of this
-##    and other reasons, the use of daemon-mode is *STRONGLY* discouraged,
-##    don't even *think* of reporting bugs when you use this.
+##  Daemon -- Run as a stand-alone daemon, serving all users from one process.
+##    This saves memory if there are more users, the downside is that when one
+##    user hits a crash-bug, all other users will also lose their connection.
 ##  ForkDaemon -- Run as a stand-alone daemon, but keep all clients in separate
 ##    child processes. This should be pretty safe and reliable to use instead
@@ -35,4 +34,11 @@
 # DaemonPort = 6667
 
+## ClientInterface:
+##
+## If for any reason, you want BitlBee to use a specific address/interface
+## for outgoing traffic (IM connections, HTTP(S), etc.), set it here.
+##
+# ClientInterface = 0.0.0.0
+
 ## AuthMode
 ##
@@ -49,6 +55,11 @@
 ##
 ## Password the user should enter when logging into a closed BitlBee server.
+## You can also have an MD5-encrypted password here. Format: "md5:", followed
+## by a hash as generated for the <user password=""> attribute in a BitlBee
+## XML file (for now there's no easier way to generate the hash).
 ##
 # AuthPassword = ItllBeBitlBee   ## Heh.. Our slogan. ;-)
+## or
+# AuthPassword = md5:gzkK0Ox/1xh+1XTsQjXxBJ571Vgl
 
 ## OperPassword
@@ -57,4 +68,6 @@
 ##
 # OperPassword = ChangeMe!
+## or
+# OperPassword = md5:I0mnZbn1t4R731zzRdDN2/pK7lRX
 
 ## HostName
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ bitlbee.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -29,9 +29,12 @@
 #define _GNU_SOURCE /* Stupid GNU :-P */
 
+/* Depend on Windows 2000 for now since we need getaddrinfo() */
+#define _WIN32_WINNT 0x0501
+
 #define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "1.1.1dev"
+#define BITLBEE_VERSION "1.2.1"
 #define VERSION BITLBEE_VERSION
 
-#define MAX_STRING 128
+#define MAX_STRING 511
 
 #if HAVE_CONFIG_H
@@ -48,7 +51,8 @@
 #include <stdio.h>
 #include <ctype.h>
+#include <errno.h>
+
 #ifndef _WIN32
 #include <syslog.h>
-#include <errno.h>
 #endif
 
@@ -94,8 +98,4 @@
 #undef g_main_quit
 #define g_main_quit		__PLEASE_USE_B_MAIN_QUIT__
-
-#ifndef F_OK
-#define F_OK 0
-#endif
 
 #ifndef G_GNUC_MALLOC
@@ -162,4 +162,6 @@
 gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond );
 
+char *set_eval_root_nick( set_t *set, char *new_nick );
+
 extern global_t global;
 
Index: conf.c
===================================================================
--- conf.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ conf.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -45,5 +45,6 @@
 	conf = g_new0( conf_t, 1 );
 	
-	conf->iface = NULL;
+	conf->iface_in = NULL;
+	conf->iface_out = NULL;
 	conf->port = g_strdup( "6667" );
 	conf->nofork = 0;
@@ -78,10 +79,10 @@
 	}
 	
-	while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:u:" ) ) >= 0 )
+	while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hu:" ) ) >= 0 )
 	/*     ^^^^ Just to make sure we skip this step from the REHASH handler. */
 	{
 		if( opt == 'i' )
 		{
-			conf->iface = g_strdup( optarg );
+			conf->iface_in = g_strdup( optarg );
 		}
 		else if( opt == 'p' )
@@ -132,5 +133,5 @@
 			        "\n"
 			        "  -I  Classic/InetD mode. (Default)\n"
-			        "  -D  Daemon mode. (Still EXPERIMENTAL!)\n"
+			        "  -D  Daemon mode. (one process serves all)\n"
 			        "  -F  Forking daemon. (one process per client)\n"
 				"  -u  Run daemon as specified user.\n"
@@ -146,12 +147,4 @@
 			return NULL;
 		}
-		else if( opt == 'R' )
-		{
-			/* We can't load the statefile yet (and should make very sure we do this
-			   only once), so set the filename here and load the state information
-			   when initializing ForkDaemon. (This option only makes sense in that
-			   mode anyway!) */
-			ipc_master_set_statefile( optarg );
-		}
 		else if( opt == 'u' )
 		{
@@ -203,6 +196,6 @@
 			else if( g_strcasecmp( ini->key, "daemoninterface" ) == 0 )
 			{
-				g_free( conf->iface );
-				conf->iface = g_strdup( ini->value );
+				g_free( conf->iface_in );
+				conf->iface_in = g_strdup( ini->value );
 			}
 			else if( g_strcasecmp( ini->key, "daemonport" ) == 0 )
@@ -210,4 +203,9 @@
 				g_free( conf->port );
 				conf->port = g_strdup( ini->value );
+			}
+			else if( g_strcasecmp( ini->key, "clientinterface" ) == 0 )
+			{
+				g_free( conf->iface_out );
+				conf->iface_out = g_strdup( ini->value );
 			}
 			else if( g_strcasecmp( ini->key, "authmode" ) == 0 )
@@ -258,5 +256,5 @@
 			{
 				g_strfreev( conf->migrate_storage );
-				conf->migrate_storage = g_strsplit( ini->value, " \t,;", -1 );
+				conf->migrate_storage = g_strsplit_set( ini->value, " \t,;", -1 );
 			}
 			else if( g_strcasecmp( ini->key, "pinginterval" ) == 0 )
Index: conf.h
===================================================================
--- conf.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ conf.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -32,5 +32,5 @@
 typedef struct conf
 {
-	char *iface;
+	char *iface_in, *iface_out;
 	char *port;
 	int nofork;
Index: configure
===================================================================
--- configure	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ configure	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -20,4 +20,5 @@
 ipcsocket='/var/run/bitlbee.sock'
 pcdir='$prefix/lib/pkgconfig'
+systemlibdirs="/lib /usr/lib /usr/local/lib"
 
 msn=1
@@ -76,4 +77,6 @@
 --ssl=...	SSL library to use (gnutls, nss, openssl, bogus, auto)
 							$ssl
+
+--target=...	Cross compilation target 		same as host
 EOF
 		exit;
@@ -109,7 +112,7 @@
 PCDIR=$pcdir
 
+TARGET=$target
 ARCH=$arch
 CPU=$cpu
-OUTFILE=bitlbee
 
 DESTDIR=
@@ -134,4 +137,16 @@
 EOF
 
+
+
+if [ -n "$target" ]; then
+	PKG_CONFIG_LIBDIR=/usr/$target/lib/pkgconfig
+	export PKG_CONFIG_LIBDIR
+	PATH=/usr/$target/bin:$PATH
+	CC=$target-cc
+	LD=$target-ld
+	systemlibdirs="/usr/$target/lib"
+fi
+
+
 if [ "$debug" = "1" ]; then
 	[ -z "$CFLAGS" ] && CFLAGS=-g
@@ -160,12 +175,14 @@
 echo "CC=$CC" >> Makefile.settings;
 
-if [ -n "$LD" ]; then
-	echo "LD=$LD" >> Makefile.settings;
-elif type ld > /dev/null 2> /dev/null; then
-	echo "LD=ld" >> Makefile.settings;
-else
-	echo 'Cannot find ld, aborting.'
-	exit 1;
-fi
+if [ -z "$LD" ]; then
+	if type ld > /dev/null 2> /dev/null; then
+		LD=ld
+	else
+		echo 'Cannot find ld, aborting.'
+		exit 1;
+	fi
+fi
+
+echo "LD=$LD" >> Makefile.settings
 
 if [ -z "$PKG_CONFIG" ]; then
@@ -215,5 +232,12 @@
 detect_gnutls()
 {
-	if libgnutls-config --version > /dev/null 2> /dev/null; then
+	if $PKG_CONFIG --exists gnutls; then
+		cat <<EOF>>Makefile.settings
+EFLAGS+=`$PKG_CONFIG --libs gnutls`
+CFLAGS+=`$PKG_CONFIG --cflags gnutls`
+EOF
+		ssl=gnutls
+		ret=1
+	elif libgnutls-config --version > /dev/null 2> /dev/null; then
 		cat <<EOF>>Makefile.settings
 EFLAGS+=`libgnutls-config --libs`
@@ -269,4 +293,6 @@
 elif [ "$ssl" = "nss" ]; then
 	detect_nss
+elif [ "$ssl" = "sspi" ]; then
+	echo
 elif [ "$ssl" = "openssl" ]; then
 	echo
@@ -325,5 +351,5 @@
 echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings
 
-for i in /lib /usr/lib /usr/local/lib; do
+for i in $systemlibdirs; do
 	if [ -f $i/libresolv.a ]; then
 		echo '#define HAVE_RESOLV_A' >> config.h
@@ -377,6 +403,6 @@
 
 if [ "$gcov" = "1" ]; then
-	echo "CFLAGS+=-ftest-coverage -fprofile-arcs" >> Makefile.settings
-	echo "EFLAGS+=-lgcov" >> Makefile.settings
+	echo "CFLAGS+=--coverage" >> Makefile.settings
+	echo "EFLAGS+=--coverage" >> Makefile.settings
 fi
 
@@ -387,17 +413,24 @@
 fi
 
+otrprefix=""
+for i in / /usr /usr/local; do
+	if [ -f ${i}/lib/libotr.a ]; then
+		otrprefix=${i}
+		break
+	fi
+done
 if [ "$otr" = "auto" ]; then
-	for i in /lib /usr/lib /usr/local/lib; do
-		if [ -f $i/libotr.a ]; then
-			otr=1
-			break
-		fi
-	done
-fi
-if [ "$otr" = 0 ]; then
+	if [ -n "$otrprefix" ]; then
+		otr=1
+	else
+		otr=0
+	fi
+fi
+if [ "$otr" = 1 ]; then
+	echo '#define WITH_OTR' >> config.h
+	echo "EFLAGS+=-L${otrprefix}/lib -lotr" >> Makefile.settings
+	echo "CFLAGS+=-I${otrprefix}/include" >> Makefile.settings
+else
 	echo '#undef WITH_OTR' >> config.h
-else
-	echo '#define WITH_OTR' >> config.h
-	echo "EFLAGS+=-lotr" >> Makefile.settings
 fi
 
@@ -500,4 +533,6 @@
 	echo 'Cygwin is not officially supported.'
 ;;
+Windows )
+;;
 * )
 	echo 'We haven'\''t tested BitlBee on many platforms yet, yours is untested. YMMV.'
@@ -506,4 +541,8 @@
 esac
 
+if [ -n "$target" ]; then
+	echo "Cross-compiling for: $target"
+fi
+
 echo
 echo 'Configuration done:'
Index: debian/README.Debian
===================================================================
--- debian/README.Debian	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ debian/README.Debian	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -1,5 +1,5 @@
-                   *** NEWS (Version 1.1 and later) ***
+                   *** NEWS (Version 1.2 and later) ***
 
-Starting from version 1.1, BitlBee has a forking daemon mode. The Debian
+Starting from version 1.2, BitlBee has a forking daemon mode. The Debian
 package now uses this mode by default, instead of inetd mode. If you don't
 want to use this, you can disable the init scripts (best way to do this is
@@ -7,5 +7,19 @@
 should be necessary only once, it won't be touched during upgrades.
 
---------------------------------------------------------------------------
+Another important change in BitlBee 1.2 is the file format used for your
+personal settings. Everything's now saved in a single .xml (per account,
+of course) file instead of $nick.accounts and $nick.nicks. One advantage
+of this new format is that the passwords are actually encrypted instead of
+just vaguely obfuscated. BitlBee can still read the old files, and will
+save things in the new format when you save/disconnect. After that, you
+can safely remove the old-style files (this is recommended).
+
+I tried making this transition (the new file format but especially, in this
+case, the inetd->forkdaemon mode change) as smooth as possible, but I'm
+aware that many BitlBee users will have their own hacks already to run the
+program. I hope the package won't break any of this for anyone. 1.2-2
+should fix at least some of the issues.
+
+---------------------------------------------------------------------------
 
 Debconf should have asked you on what port you want BitlBee to run. If it
Index: debian/bitlbee.init
===================================================================
--- debian/bitlbee.init	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ debian/bitlbee.init	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -1,3 +1,10 @@
 #! /bin/sh
+### BEGIN INIT INFO
+# Provides:          bitlbee
+# Required-Start:    $remote_fs $syslog
+# Required-Stop:     $remote_fs $syslog
+# Default-Start:     2 3 4 5
+# Default-Stop:      1
+### END INIT INFO
 #
 # Init script for BitlBee Debian package. Based on skeleton init script:
@@ -18,5 +25,5 @@
 # Default value
 BITLBEE_PORT=6667
-DAEMON_OPT=-F
+BITLBEE_OPTS=-F
 
 # Read config file if it is present.
@@ -37,6 +44,5 @@
 	
 	start-stop-daemon --start --quiet --pidfile $PIDFILE \
-		-c bitlbee -g nogroup \
-		--exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $DAEMON_OPT
+		--exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $BITLBEE_OPTS
 }
 
Index: debian/changelog
===================================================================
--- debian/changelog	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ debian/changelog	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -1,11 +1,76 @@
-bitlbee (1.1.1dev-0pre) unstable; urgency=low
-
+bitlbee (1.2-6) UNRELEASED; urgency=low
+
+  * Add Homepage and Vcs-Bzr fields.
+
+ -- Jelmer Vernooij <jelmer@samba.org>  Sun, 11 May 2008 14:18:16 +0200
+
+bitlbee (1.2-5) unstable; urgency=low
+
+  * Add myself to uploaders.
+  * Bump standards version to 3.8.0.
+  * Fix FSF address.
+  * Avoid changing uid from init script. (Closes: #474589)
+
+ -- Jelmer Vernooij <jelmer@samba.org>  Mon, 16 Jun 2008 00:53:20 +0200
+
+bitlbee (1.2-4) unstable; urgency=low
+
+  * Fixed init script to use the BITLBEE_OPTS variable, not an undefined
+    DAEMON_OPT. (Closes: #474583)
+  * Added dependency information to the init script. (Closes: #472567)
+  * Added bitlbee-dev package. Patch from RISKO Gergely <risko@debian.org>
+    with some small modifications. (Closes: #473480)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Wed, 07 May 2008 22:40:40 -0700
+
+bitlbee (1.2-3) unstable; urgency=low
+
+  * Removed DEB_BUILD_OPTIONS again (forgot to apply that change to the 1.2
+    branch when I finished 1.0.4-2, things diverged too much anyway.)
+    Closes: #472540.
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 24 Mar 2008 21:10:14 +0000
+
+bitlbee (1.2-2) unstable; urgency=low
+
+  * Fixed some packaging issues reported by IRC and e-mail. (Closes: #472373)
+  * Fixed proxy support. (Closes: #472395)
+  * Added a BitlBee group so only root can edit the configs and BitlBee can
+    just *read* it.
+  * Manually deleting /var/lib/bitlbee/ when purging, deluser doesn't want to
+    do it.
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 24 Mar 2008 19:48:24 +0000
+
+bitlbee (1.2-1) unstable; urgency=low
+
+  * New upstream release. (Closes: #325017, #386914, #437515)
+  * With hopefully completely sane charset handling (Closes: #296145)
   * Switched to the new forking daemon mode. Added /etc/default/bitlbee
     file, an init script. People who want to stick with inetd can do so, see
     the defaults file.
+    (Closes: #460741, #466171, #294585, #345038, #306452, #392682)
   * Got rid of debconf Woody compatibility stuff.
   * No more MPL code in BitlBee, thanks to the Jabber module rewrite!
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Fri, 06 Jul 2007 09:09:36 +0100
+  * Added Italian translation, sorry for taking so long! (Closes: #448238)
+  * Added libevent dependency (more reliable event handling).
+  * Removed GLib 1.x dependency because BitlBee really requires GLib >=2.4.
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Tue, 18 Mar 2008 23:44:19 +0000
+
+bitlbee (1.0.4-2) unstable; urgency=low
+
+  * Removed $DEB_BUILD_OPTIONS because apparently buildds fill it with crap.
+    (Closes: #458717)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 11 Feb 2008 19:15:33 +0000
+
+bitlbee (1.0.4-1) unstable; urgency=low
+
+  * New upstream release.
+  * Changed libnss-dev dependency. (Closes: #370442)
+  * Added build-indep rule to debian/rules. (Closes: #395673)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Wed, 29 Aug 2007 20:24:28 +0100
 
 bitlbee (1.0.3-1.3) unstable; urgency=low
Index: debian/conffiles
===================================================================
--- debian/conffiles	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ debian/conffiles	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -1,2 +1,3 @@
 /etc/bitlbee/motd.txt
 /etc/bitlbee/bitlbee.conf
+/etc/init.d/bitlbee
Index: debian/control
===================================================================
--- debian/control	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ debian/control	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -3,6 +3,10 @@
 Priority: optional
 Maintainer: Wilmer van der Gaast <wilmer@gaast.net>
-Standards-Version: 3.5.9
-Build-Depends: libglib2.0-dev | libglib-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf
+Uploaders: Jelmer Vernooij <jelmer@samba.org>
+Standards-Version: 3.8.0
+Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf
+Homepage: http://www.bitlbee.org/
+Vcs-Bzr: http://code.bitlbee.org/bitlbee/
+DM-Upload-Allowed: yes
 
 Package: bitlbee
@@ -12,2 +16,11 @@
  This program can be used as an IRC server which forwards everything you
  say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo.
+
+Package: bitlbee-dev
+Architecture: all
+Depends: bitlbee (= ${binary:Version})
+Description: An IRC to other chat networks gateway
+ This program can be used as an IRC server which forwards everything you
+ say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo.
+ .
+ This package holds development stuff for compiling plug-ins.
Index: debian/copyright
===================================================================
--- debian/copyright	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ debian/copyright	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -26,6 +26,6 @@
   You should have received a copy of the GNU General Public License with
   the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-  Suite 330, Boston, MA  02111-1307  USA
+  if not, write to the Free Software Foundation, Inc., 51 Franklin St, 
+  Fifth Floor, Boston, MA 02110-1301, USA.
 ============================================================================
 
@@ -40,5 +40,5 @@
 
  Copyright (C) 2000  Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 	51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
Index: debian/po/it.po
===================================================================
--- debian/po/it.po	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ debian/po/it.po	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,36 @@
+# Italian translation of the bitlbee debconf template
+# This file is distributed under the same license as the bitlbee package
+# Copyright (C) 2007 Free Software Foundation, Inc.
+# Luca Monducci <luca.mo@tiscali.it>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: bitlbee\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-08-30 04:31+0200\n"
+"PO-Revision-Date: 2007-10-27 11:52+0200\n"
+"Last-Translator: Luca Monducci <luca.mo@tiscali.it>\n"
+"Language-Team: Italian <debian-l10n-italian@lists.debian.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: string
+#. Description
+#: ../bitlbee.templates.master:1001
+msgid "On what TCP port should BitlBee listen for connections?"
+msgstr "Su quale porta TCP si deve mettere in ascolto BitlBee?"
+
+#. Type: string
+#. Description
+#: ../bitlbee.templates.master:1001
+msgid ""
+"BitlBee normally listens on the regular IRC port, 6667. This might not be a "
+"very good idea when you're running a real IRC daemon as well. 6668 might be "
+"a good alternative. Leaving this value blank means that BitlBee will not be "
+"run automatically."
+msgstr ""
+"Normalmente BitlBee si mette in ascolto sulla consueta porta IRC, la 6667. "
+"Questa potrebbe non essere una buona idea se è in esecuzione anche un reale "
+"demone IRC. La porta 6668 potrebbe essere una valida alternativa. Lasciando "
+"vuoto questo valore BitlBee non viene avviato automaticamente."
Index: debian/postinst
===================================================================
--- debian/postinst	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ debian/postinst	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -14,9 +14,18 @@
 BITLBEE_DISABLED=0
 BITLBEE_UPGRADE_DONT_RESTART=0
-[ -r /etc/default/bitlbee ] && source /etc/default/bitlbee
+[ -r /etc/default/bitlbee ] && . /etc/default/bitlbee
 
-if [ "$BITLBEE_DISABLED" = "0" ]; then
-	## In case it's still there (if we're upgrading right now)
+if [ "$BITLBEE_DISABLED" = "0" ] && type update-inetd > /dev/null 2> /dev/null &&
+   ( expr "$2" : '0\..*' > /dev/null || expr "$2" : '1\.0\..*' > /dev/null ); then
+	## Make sure the inetd entry is gone (can still be there from a
+	## previous version.
 	update-inetd --remove '.*/usr/sbin/bitlbee'
+	if grep -q /usr/sbin/bitlbee /etc/inetd.conf 2> /dev/null; then
+		# Thanks for breaking update-inetd! (bugs.debian.org/311111)
+		# I hope that it works at least with xinetd, because this
+		# emergency hack doesn't:
+		perl -pi -e 's:^[^#].*/usr/sbin/bitlbee$:## Now using daemon mode\: # $&:' /etc/inetd.conf
+		killall -HUP inetd || true
+	fi
 fi
 
@@ -65,11 +74,19 @@
 fi
 
-adduser --system --home /var/lib/bitlbee/ --disabled-login --disabled-password bitlbee
+adduser --system --group --disabled-login --disabled-password --home /var/lib/bitlbee/ bitlbee
 chmod 700 /var/lib/bitlbee/
 
 ## Can't do this in packaging phase: Don't know the UID yet. Access to
-## the file should be limited, now that it stores passwords.
-chmod 600 /etc/bitlbee/bitlbee.conf
-chown bitlbee /etc/bitlbee/bitlbee.conf
+## the file should be limited, now that it stores passwords. Added
+## --group later for a little more security, but have to see if I can
+## apply this change to existing installations on upgrades. Will think
+## about that later.
+if getent group bitlbee > /dev/null; then
+	chmod 640 /etc/bitlbee/bitlbee.conf
+	chown root:bitlbee /etc/bitlbee/bitlbee.conf
+else
+	chmod 600 /etc/bitlbee/bitlbee.conf
+	chown bitlbee /etc/bitlbee/bitlbee.conf
+fi
 
 if [ -z "$2" ]; then
Index: debian/postrm
===================================================================
--- debian/postrm	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ debian/postrm	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -9,3 +9,6 @@
 
 update-rc.d bitlbee remove > /dev/null 2>&1 || true
-deluser --remove-home bitlbee || true
+rm -f /etc/default/bitlbee
+
+deluser --system --remove-home bitlbee || true
+rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var
Index: debian/rules
===================================================================
--- debian/rules	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ debian/rules	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -3,13 +3,16 @@
 DEBUG ?= 0
 
+ifdef BITLBEE_VERSION
+BITLBEE_FORCE_VERSION=1
+else
 # Want to use the full package version number instead of just the release.
 BITLBEE_VERSION ?= "$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')"
 export BITLBEE_VERSION
-
+endif
 
 build-arch: build-arch-stamp
 build-arch-stamp:
-	if [ ! -d debian ]; then exit 1; fi
-	./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee $(DEB_BUILD_OPTIONS)
+	[ -d debian ]
+	./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent
 	$(MAKE)
 #	$(MAKE) -C doc/ all
@@ -17,12 +20,12 @@
 
 clean:
-	if [ "`whoami`" != "root" -o ! -d debian ]; then exit 1; fi
-	rm -rf build-arch-stamp debian/bitlbee debian/*.substvars debian/files
-	-$(MAKE) distclean
+	[ "`whoami`" = "root" -a -d debian ]
+	rm -rf build-arch-stamp debian/bitlbee debian/*.substvars debian/files debian/bitlbee-dev
+	$(MAKE) distclean
 #	-$(MAKE) -C doc/ clean
 				
 
 install-arch: build-arch
-	if [ "`whoami`" != "root" -o ! -d debian ]; then exit 1; fi
+	[ "`whoami`" = "root" -a -d debian ]
 	mkdir -p debian/bitlbee/DEBIAN/
 	$(MAKE) install install-etc DESTDIR=`pwd`/debian/bitlbee
@@ -32,6 +35,13 @@
 	cp doc/user-guide/user-guide.html debian/bitlbee/usr/share/doc/bitlbee/
 
+install-indep: install-arch
+	[ "`whoami`" = "root" -a -d debian ]
+	mkdir -p debian/bitlbee-dev/DEBIAN/
+	$(MAKE) install-dev DESTDIR=`pwd`/debian/bitlbee-dev
+
+	mkdir -p debian/bitlbee-dev/usr/share/doc/bitlbee-dev/
+
 binary-arch: build-arch install-arch
-	if [ "`whoami`" != "root" -o ! -d debian ]; then exit 1; fi
+	[ "`whoami`" = "root" -a -d debian ]
 
 	chmod 755 debian/post* debian/pre* debian/config debian/bitlbee.init
@@ -49,5 +59,5 @@
 		        doc/bitlbee/examples/* man/man8/bitlbee.8 man/man5/bitlbee.conf.5
 	
-	chown -R root.root debian/bitlbee/
+	chown -R root:root debian/bitlbee/
 	find debian/bitlbee/usr/share/ -type d -exec chmod 755 {} \;
 	find debian/bitlbee/usr/share/ -type f -exec chmod 644 {} \;
@@ -66,5 +76,5 @@
 		find usr -type f -exec md5sum {} \; > DEBIAN/md5sums
 	dpkg-shlibdeps -Tdebian/bitlbee.substvars -dDepends debian/bitlbee/usr/sbin/bitlbee
-ifdef BITLBEE_VERSION
+ifdef BITLBEE_FORCE_VERSION
 	dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -v1:$(BITLBEE_VERSION)-0 -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0'
 else
@@ -74,10 +84,25 @@
 	dpkg --build debian/bitlbee ..
 
-debug-build:
-	BITLBEE_VERSION=\"`date +%Y%m%d`-`hostname`-debug\" debian/rules clean binary DEBUG=1
+binary-indep: install-indep
+	[ "`whoami`" = "root" -a -d debian ]
 
-binary: binary-arch
+	chown -R root.root debian/bitlbee-dev/
+	find debian/bitlbee-dev/usr/share/ -type d -exec chmod 755 {} \;
+	find debian/bitlbee-dev/usr/share/ -type f -exec chmod 644 {} \;
+
+	cp debian/changelog debian/bitlbee-dev/usr/share/doc/bitlbee-dev/changelog.Debian
+	gzip -9 debian/bitlbee-dev/usr/share/doc/bitlbee-dev/changelog.Debian
+	cp debian/copyright debian/bitlbee-dev/usr/share/doc/bitlbee-dev/copyright
+
+	cd debian/bitlbee-dev; \
+		find usr -type f -exec md5sum {} \; > DEBIAN/md5sums
+
+	dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev
+
+	dpkg --build debian/bitlbee-dev ..
+
+binary: binary-arch binary-indep
 build: build-arch
-install: install-arch
+install: install-arch install-indep
 
-.PHONY: build-arch build clean binary-arch binary install-arch install
+.PHONY: build-arch build clean binary-arch binary install-arch install binary-indep install-indep
Index: doc/BUILD.win32
===================================================================
--- doc/BUILD.win32	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ doc/BUILD.win32	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,10 @@
+Instructions for building BitlBee for Windows
+=============================================
+
+1) Install the mingw32 compiler
+
+2) Compile GLib2 for the target i586-mingw32msvc
+
+3) Cross-compile BitlBee:
+
+$ ./configure --target=i586-mingw32msvc --ssl=bogus --arch=Windows
Index: doc/CHANGES
===================================================================
--- doc/CHANGES	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ doc/CHANGES	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -1,6 +1,36 @@
+This ChangeLog mostly lists changes relevant to users. A full log can be
+found in the bzr commit logs, for example you can try:
+
+http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on
+
+Version 1.2.1:
+- Fixed proxy support.
+- Fixed stalling issues while connecting to Jabber when using the OpenSSL
+  module.
+- Fixed problem with GLib and ForkDaemon where processes didn't die when
+  the client disconnects.
+- Fixed handling of "set charset none". (Which pretty much breaks the account
+  completely in 1.2.)
+- You can now automatically identify yourself to BitlBee by setting a server
+  password in your IRC client.
+- Compatible with all crazy kinds of line endings that clients can send.
+- Changed root nicknames are now saved.
+- Added ClientInterface setting to bind() outgoing connections to a specific
+  network interface.
+- Support for receiving Jabber chatroom invitations.
+- Relaxed port restriction of the Jabber module: added ports 80 and 443.
+- Preserving case in Jabber resources of buddies, since these should
+  officially be treated as case sensitive.
+- Fully stripping spaces from AIM screennames, this didn't happen completely
+  which severly breaks the IRC protocol.
+- Removed all the yellow tape around daemon mode, it's pretty mature by now:
+  testing.bitlbee.org serves all (~30) SSL users from one daemon mode
+  process without any serious stability issues.
+- Fixed GLib <2.6 compatibility issue.
+- Misc. memory leak/crash fixes.
+
+Finished 24 Jun 2008
+
 Version 1.2:
-- First BitlBee development/testing RELEASE. This should be quite stable
-  though (and for most people more stable than 1.0.x). It just has a couple
-  of rough edges and needs a bit more testing.
 - Added ForkDaemon mode next to the existing Daemon- and inetd modes. With
   ForkDaemon you can run BitlBee as a stand-alone daemon and every connection
@@ -21,7 +51,16 @@
   1.x is so old that supporting it really isn't necessary anymore.
 - Many, many, MANY little changes, improvements, fixes. Using non-blocking
-  I/O as much as possible, fixed lots of little bugs (including bugs that
-  affected daemon mode stability). See the bzr logs for more information.
-- Added units tests, will have to add some more before the real release.
+  I/O as much as possible, replaced the Gaim (0.59, IOW heavily outdated)
+  API, fixed lots of little bugs (including bugs that affected daemon mode
+  stability). See the bzr logs for more information.
+- One of the user-visible changes from the API change: You can finally see
+  all away states/messages properly.
+- Added units tests. Test coverage is very minimal for now.
+- Better charset handling: Everything is just converted from/to UTF-8 right
+  in the IRC core, and charset mismatches are detected (if possible) and the
+  user is asked to resolve this before continuing. Also, UTF-8 is the default
+  setting now, since that's how the world seems to work these days.
+- One can now keep hashed passwords in bitlbee.conf instead of the cleartext
+  version.
 - Most important change: New file format for user data (accounts, nicks and
   settings). Migration to the new format should happen transparently,
@@ -69,6 +108,11 @@
   * An XML console (add xmlconsole to your contact list or see "help set
     xmlconsole" if you want it permanently).
-
-Finished ???
+- The Yahoo! module now says it supports YMSG protocol version 12, which will
+  hopefully keep the Yahoo module working after 2008-04-02 (when Yahoo! is
+  dropping support for version 6.x of their client).
+- MSN switchboard handling changes. Hopefully less messages will get lost now,
+  although things are still not perfect.
+
+Finished 17 Mar 2008
 
 Version 1.0.4:
Index: doc/README
===================================================================
--- doc/README	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ doc/README	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -41,4 +41,23 @@
 by default) and chown it to the UID BitlBee is running as. Make sure this
 directory is read-/writable by this user only.
+
+--- (Fork)Daemon mode
+
+If you don't want to run any inetd daemon, you can run BitlBee in Daemon
+mode. Right now, daemon mode may be a bad idea on servers with multiple
+users, since possible fatal BitlBee bugs will crash the BitlBee process and
+disconnect all connected users at once. Instead, you can use ForkDaemon
+mode, which serves every user from a separate process, without depending on
+an inetd daemon.
+
+To use BitlBee in daemon mode, just start it with the right flags or enable
+it in bitlbee.conf. You probably want to write an init script to start
+BitlBee automatically after a reboot. (This is where you realise using
+a package from your distro would've been a better idea. :-P)
+
+Note that the BitlBee code is getting stable enough for daemon mode to be
+useful. Some public servers use it, and it saves a lot of memory by serving
+tens of users from a single process. One crash affects all users, but these
+are becoming quite rare.
 
 
@@ -88,32 +107,4 @@
 versions of make, we'd love to hear it, but it seems this just isn't
 possible.
-
-
-RUNNING ON SERVERS WITH MANY USERS
-==================================
-
-BitlBee is not yet bug-free. Sometimes a bug causes the program to get into
-an infinite loop. Something you really don't want on a public server,
-especially when that machine is also used for other (mission-critical) things.
-For now we can't do much about it. We haven't seen that happen for a long
-time already on our own machines, but some people still manage to get
-themselves in nasty situations we haven't seen before.
-
-For now the best we can offer against this problem is bitlbeed, which allows
-you to setrlimit() the child processes to use no more than a specified
-number of CPU seconds. Not the best solution (not really a solution anyway),
-but certainly trashing one busy daemon process is better than trashing your
-whole machine.
-
-We don't believe adding a limit for bitlbee to /etc/security/limits.conf will
-work, because that file is only read by PAM (ie just for real login users,
-not daemons).
-
-See utils/bitlbeed.c for more information about the program.
-
-Just a little note: Now that we reach version 1.0, this shouldn't be that
-much of an issue anymore. However, on a public server, especially if you
-also use it for other things, it can't hurt to protect yourself against
-possible problems.
 
 
Index: doc/bitlbee.8
===================================================================
--- doc/bitlbee.8	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ doc/bitlbee.8	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -44,11 +44,6 @@
 
 \fBbitlbee\fP should be called by
-.BR inetd (8).
-(Or \fBbitlbeed\fP,
-if you can't run and/or configure \fBinetd\fP.) There is an experimental
-daemon mode too, in which BitlBee will serve all clients in one process
-(and does not require inetd), but this mode is still experimental.
-There are still some bugs left in BitlBee, and if they cause a crash,
-that would terminate the BitlBee connection for all clients.
+.BR inetd (8),
+or you can run it as a stand-alone daemon.
 .PP
 .SH OPTIONS
@@ -62,8 +57,7 @@
 Run in daemon mode. In this mode, BitlBee forks to the background and
 waits for new connections. All clients will be served from one process.
-This is still experimental. See the note above for more information.
 .IP "-F"
 Run in ForkDaemon mode. This is similar to ordinary daemon mode, but every
-client gets its own process. Easier to set up than inetd mode, but without
+client gets its own process. Easier to set up than inetd mode, and without
 the possible stability issues.
 .IP "-i \fIaddress\fP"
Index: doc/user-guide/Support.xml
===================================================================
--- doc/user-guide/Support.xml	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ doc/user-guide/Support.xml	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -4,10 +4,11 @@
 
 <sect1>
-<title>BitlBee is beta software</title>
+<title>Disclaimer</title>
 
 <para>
-Although BitlBee has quite some functionality it is still beta. That means it 
-can crash at any time, corrupt your data or whatever. Don't use it in 
-any production environment and don't rely on it.
+BitlBee doesn't come with a warranty and is still (and will probably always
+be) under development. That means it can crash at any time, corrupt your
+data or whatever. Don't use it in any production environment and don't rely
+on it, or at least don't blame us if things blow up. :-)
 </para>
 
Index: doc/user-guide/commands.xml
===================================================================
--- doc/user-guide/commands.xml	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ doc/user-guide/commands.xml	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -163,9 +163,5 @@
 
 			<para>
-				If you want, you can also tell BitlBee what nick to give the new contact. Of course you can also use the <emphasis>rename</emphasis> command for that, but sometimes this might be more convenient.
-			</para>
-			
-			<para>
-				Adding -tmp adds the buddy to the internal BitlBee structures only, not to the real contact list (like done by <emphasis>set handle_unknown add</emphasis>). This allows you to talk to people who are not in your contact list.
+				If you want, you can also tell BitlBee what nick to give the new contact. The -tmp option adds the buddy to the internal BitlBee structures only, not to the real contact list (like done by <emphasis>set handle_unknown add</emphasis>). This allows you to talk to people who are not in your contact list. This normally won't show you any presence notifications.
 			</para>
 		</description>
@@ -532,14 +528,14 @@
 
 	<bitlbee-setting name="charset" type="string" scope="global">
-		<default>iso8859-1</default>
+		<default>utf-8</default>
 		<possible-values>you can get a list of all possible values by doing 'iconv -l' in a shell</possible-values>
 
 		<description>
 			<para>
-				The charset setting enables you to use different character sets in BitlBee. These get converted to UTF-8 before sending and from UTF-8 when receiving.
-			</para>
-
-			<para>
-				If you don't know what's the best value for this, at least iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://czyborra.com/charsets/iso8859.html
+				This setting tells BitlBee what your IRC client sends and expects. It should be equal to the charset setting of your IRC client if you want to be able to send and receive non-ASCII text properly.
+			</para>
+
+			<para>
+				Most systems use UTF-8 these days. On older systems, an iso8859 charset may work better. For example, iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://www.unicodecharacter.com/charsets/iso8859.html
 			</para>
 		</description>
@@ -804,4 +800,14 @@
 	</bitlbee-setting>
 
+	<bitlbee-setting name="root_nick" type="string" scope="global">
+		<default>root</default>
+
+		<description>
+			<para>
+				Normally the "bot" that takes all your BitlBee commands is called "root". If you don't like this name, you can rename it to anything else using the <emphasis>rename</emphasis> command, or by changing this setting.
+			</para>
+		</description>
+	</bitlbee-setting>
+
 	<bitlbee-setting name="save_on_quit" type="boolean" scope="global">
 		<default>true</default>
@@ -888,5 +894,5 @@
 		<description>
 			<para>
-				Sends you a /notice when a user starts typing a message (if the protocol supports it, MSN for example). This is a bug, not a feature. (But please don't report it.. ;-) You don't want to use it. Really. In fact the typing-notification is just one of the least useful 'innovations' ever. It's just there because some guy will probably ask me about it anyway. ;-)
+				Sends you a /notice when a user starts typing a message (if supported by the IM protocol and the user's client). To use this, you most likely want to use a script in your IRC client to show this information in a more sensible way.
 			</para>
 		</description>
@@ -1057,19 +1063,15 @@
 		<short-description>Change friendly name, nick</short-description>
 		<syntax>nick &lt;connection&gt; [&lt;new nick&gt;]</syntax>
-		<syntax>nick</syntax>
-
-		<description>
-			<para>
-				This command allows to set the friendly name of an im account. If no new name is specified the command will report the current name. When the name contains spaces, don't forget to quote the whole nick in double quotes. Currently this command is only supported by the MSN protocol.
-			</para>
-
-			<para>
-				It is recommended to use the per-account <emphasis>display_name</emphasis> setting to read and change this information. The <emphasis>nick</emphasis> command is deprecated.
+		<syntax>nick &lt;connection&gt;</syntax>
+
+		<description>
+			<para>
+				Deprecated: Use the per-account <emphasis>display_name</emphasis> setting to read and change this information.
 			</para>
 		</description>
 
 		<ircexample>
-			<ircline nick="wouter">nick 1 "Wouter Paesen"</ircline>
-			<ircline nick="root">Setting your name on connection 1 to `Wouter Paesen'</ircline>
+			<ircline nick="wouter">account set 1/display_name "The majestik møøse"</ircline>
+			<ircline nick="root">display_name = `The majestik møøse'</ircline>
 		</ircexample>
 
Index: doc/user-guide/misc.xml
===================================================================
--- doc/user-guide/misc.xml	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ doc/user-guide/misc.xml	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -47,8 +47,4 @@
 </variablelist>
 
-<para>
-This list was extracted from <ulink url="http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=">http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=</ulink>.
-</para>
-
 </sect1>
 
@@ -56,5 +52,5 @@
 <title>Groupchats</title>
 <para>
-Since version 0.8x, BitlBee supports groupchats on the MSN and Yahoo! networks. This text will try to explain you how they work.
+BitlBee now supports groupchats on all IM networks. This text will try to explain you how they work.
 </para>
 
@@ -73,5 +69,5 @@
 
 <para>
-If you want to start a groupchat with the person <emphasis>jim_msn</emphasis> in it, just join the channel <emphasis>#jim_msn</emphasis>. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and jim_msn in it.
+If you want to start a groupchat with the person <emphasis>lisa_msn</emphasis> in it, just join the channel <emphasis>#lisa_msn</emphasis>. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and lisa_msn in it.
 </para>
 
@@ -82,21 +78,4 @@
 <para>
 Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>join_chat</emphasis> command to join them. See <emphasis>help join_chat</emphasis> for more information.
-</para>
-
-<para>
-This is all you'll probably need to know. If you have any problems, please read <emphasis>help groupchats3</emphasis>.
-</para>
-
-</sect1>
-
-<sect1 id="groupchats3">
-<title>Groupchat channel names</title>
-
-<para>
-Obviously the (numbered) channel names don't make a lot of sense. Problem is that groupchats usually don't have names at all in the IM-world, while IRC insists on a name. So BitlBee just generates something random, just don't pay attention to it. :-)
-</para>
-
-<para>
-Please also note that BitlBee doesn't support groupchats for all protocols yet. BitlBee will tell you so. Support for other protocols will hopefully come later.
 </para>
 
@@ -121,4 +100,5 @@
 	<member>On the phone, Phone, On phone</member>
 	<member>Out to lunch, Lunch, Food</member>
+	<member>Invisible, Hidden</member>
 </simplelist>
 
@@ -128,5 +108,5 @@
 
 <para>
-You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Protocols like Yahoo! and Jabber will also show this complete away message to your buddies.
+You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Most IM-protocols can also show this additional information to your buddies.
 </para>
 
Index: doc/user-guide/quickstart.xml
===================================================================
--- doc/user-guide/quickstart.xml	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ doc/user-guide/quickstart.xml	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -61,9 +61,9 @@
 
 <para>
-For most protocols (currently MSN, Jabber, Yahoo and AOL) BitlBee can download the contact list automatically from the IM server and all the on-line users should appear in the control channel when you log in.
+Now BitlBee logs in and downloads the contact list from the IM server. In a few seconds, all your on-line buddies should show up in the control channel.
 </para>
 
 <para>
-BitlBee will convert names into irc-friendly form (for instance: tux@example.com will be given the nickname tux). If you have more than one person who would have the same name by this logic (for instance: tux@example.com and tux@bitlbee.org) the second one to log on will be tux_. The same is true if you have a tux log on to AOL and a tux log on from Yahoo.
+BitlBee will convert names into IRC-friendly form (for instance: tux@example.com will be given the nickname tux). If you have more than one person who would have the same name by this logic (for instance: tux@example.com and tux@bitlbee.org) the second one to log on will be tux_. The same is true if you have a tux log on to AOL and a tux log on from Yahoo.
 </para>
 
@@ -127,9 +127,13 @@
 <ircexample>
 	<ircline nick="you">tux: hey, how's the weather down there?</ircline>
-	<ircline nick="tux"> you: a bit chilly!</ircline>
+	<ircline nick="tux">you: a bit chilly!</ircline>
 </ircexample>
 
 <para>
-If you'd rather chat with them in a separate window use the <emphasis>/msg</emphasis> or <emphasis>/query</emphasis> command, just like you would for a private message in IRC.  If you want to have messages automatically come up in private messages rather than in the &amp;bitlbee channel, use the <emphasis>set private</emphasis> command: <emphasis>set private true</emphasis> (<emphasis>set private false</emphasis> to change back).
+Note that, although all contacts are in the &amp;bitlbee channel, only tux will actually receive this message. The &amp;bitlbee channel shouldn't be confused with a real IRC channel.
+</para>
+
+<para>
+If you prefer chatting in a separate window, use the <emphasis>/msg</emphasis> or <emphasis>/query</emphasis> command, just like on real IRC. BitlBee will remember how you talk to someone and show his/her responses the same way. If you want to change the default behaviour (for people you haven't talked to yet), see <emphasis>help set private</emphasis>.
 </para>
 
Index: ipc.c
===================================================================
--- ipc.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ ipc.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -33,5 +33,4 @@
 
 GSList *child_list = NULL;
-static char *statefile = NULL;
 
 static void ipc_master_cmd_client( irc_t *data, char **cmd )
@@ -63,4 +62,23 @@
 }
 
+static void ipc_master_cmd_deaf( irc_t *data, char **cmd )
+{
+	if( global.conf->runmode == RUNMODE_DAEMON )
+	{
+		b_event_remove( global.listen_watch_source_id );
+		close( global.listen_socket );
+		
+		global.listen_socket = global.listen_watch_source_id = -1;
+	
+		ipc_to_children_str( "OPERMSG :Closed listening socket, waiting "
+		                     "for all users to disconnect." );
+	}
+	else
+	{
+		ipc_to_children_str( "OPERMSG :The DEAF command only works in "
+		                     "normal daemon mode. Try DIE instead." );
+	}
+}
+
 void ipc_master_cmd_rehash( irc_t *data, char **cmd )
 {
@@ -98,4 +116,5 @@
 	{ "hello",      0, ipc_master_cmd_client,     0 },
 	{ "die",        0, ipc_master_cmd_die,        0 },
+	{ "deaf",       0, ipc_master_cmd_deaf,       0 },
 	{ "wallops",    1, NULL,                      IPC_CMD_TO_CHILDREN },
 	{ "wall",       1, NULL,                      IPC_CMD_TO_CHILDREN },
@@ -209,10 +228,10 @@
 }
 
+/* Return just one line. Returns NULL if something broke, an empty string
+   on temporary "errors" (EAGAIN and friends). */
 static char *ipc_readline( int fd )
 {
-	char *buf, *eol;
+	char buf[513], *eol;
 	int size;
-	
-	buf = g_new0( char, 513 );
 	
 	/* Because this is internal communication, it should be pretty safe
@@ -221,5 +240,5 @@
 	   sockets and limites message length, messages should always be
 	   complete. Saves us quite a lot of code and buffering. */
-	size = recv( fd, buf, 512, MSG_PEEK );
+	size = recv( fd, buf, sizeof( buf ) - 1, MSG_PEEK );
 	if( size == 0 || ( size < 0 && !sockerr_again() ) )
 		return NULL;
@@ -229,19 +248,13 @@
 		buf[size] = 0;
 	
-	eol = strstr( buf, "\r\n" );
-	if( eol == NULL )
+	if( ( eol = strstr( buf, "\r\n" ) ) == NULL )
 		return NULL;
 	else
 		size = eol - buf + 2;
-	
-	g_free( buf );
-	buf = g_new0( char, size + 1 );
 	
 	if( recv( fd, buf, size, 0 ) != size )
 		return NULL;
 	else
-		buf[size-2] = 0;
-	
-	return buf;
+		return g_strndup( buf, size - 2 );
 }
 
@@ -254,132 +267,133 @@
 		cmd = irc_parse_line( buf );
 		if( cmd )
+		{
 			ipc_command_exec( data, cmd, ipc_master_commands );
-	}
-	else
+			g_free( cmd );
+		}
+		g_free( buf );
+	}
+	else
+	{
+		ipc_master_free_fd( source );
+	}
+	
+	return TRUE;
+}
+
+gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond )
+{
+	char *buf, **cmd;
+	
+	if( ( buf = ipc_readline( source ) ) )
+	{
+		cmd = irc_parse_line( buf );
+		if( cmd )
+		{
+			ipc_command_exec( data, cmd, ipc_child_commands );
+			g_free( cmd );
+		}
+		g_free( buf );
+	}
+	else
+	{
+		ipc_child_disable();
+	}
+	
+	return TRUE;
+}
+
+void ipc_to_master( char **cmd )
+{
+	if( global.conf->runmode == RUNMODE_FORKDAEMON )
+	{
+		char *s = irc_build_line( cmd );
+		ipc_to_master_str( "%s", s );
+		g_free( s );
+	}
+	else if( global.conf->runmode == RUNMODE_DAEMON )
+	{
+		ipc_command_exec( NULL, cmd, ipc_master_commands );
+	}
+}
+
+void ipc_to_master_str( char *format, ... )
+{
+	char *msg_buf;
+	va_list params;
+
+	va_start( params, format );
+	msg_buf = g_strdup_vprintf( format, params );
+	va_end( params );
+	
+	if( strlen( msg_buf ) > 512 )
+	{
+		/* Don't send it, it's too long... */
+	}
+	else if( global.conf->runmode == RUNMODE_FORKDAEMON )
+	{
+		if( global.listen_socket >= 0 )
+			if( write( global.listen_socket, msg_buf, strlen( msg_buf ) ) <= 0 )
+				ipc_child_disable();
+	}
+	else if( global.conf->runmode == RUNMODE_DAEMON )
+	{
+		char **cmd, *s;
+		
+		if( ( s = strchr( msg_buf, '\r' ) ) )
+			*s = 0;
+		
+		cmd = irc_parse_line( msg_buf );
+		ipc_command_exec( NULL, cmd, ipc_master_commands );
+		g_free( cmd );
+	}
+	
+	g_free( msg_buf );
+}
+
+void ipc_to_children( char **cmd )
+{
+	if( global.conf->runmode == RUNMODE_FORKDAEMON )
+	{
+		char *msg_buf = irc_build_line( cmd );
+		ipc_to_children_str( "%s", msg_buf );
+		g_free( msg_buf );
+	}
+	else if( global.conf->runmode == RUNMODE_DAEMON )
 	{
 		GSList *l;
-		struct bitlbee_child *c;
-		
-		for( l = child_list; l; l = l->next )
+		
+		for( l = irc_connection_list; l; l = l->next )
+			ipc_command_exec( l->data, cmd, ipc_child_commands );
+	}
+}
+
+void ipc_to_children_str( char *format, ... )
+{
+	char *msg_buf;
+	va_list params;
+
+	va_start( params, format );
+	msg_buf = g_strdup_vprintf( format, params );
+	va_end( params );
+	
+	if( strlen( msg_buf ) > 512 )
+	{
+		/* Don't send it, it's too long... */
+	}
+	else if( global.conf->runmode == RUNMODE_FORKDAEMON )
+	{
+		int msg_len = strlen( msg_buf );
+		GSList *l, *next;
+		
+		for( l = child_list; l; l = next )
 		{
-			c = l->data;
-			if( c->ipc_fd == source )
+			struct bitlbee_child *c = l->data;
+			
+			next = l->next;
+			if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 )
 			{
 				ipc_master_free_one( c );
 				child_list = g_slist_remove( child_list, c );
-				break;
 			}
-		}
-	}
-	
-	return TRUE;
-}
-
-gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond )
-{
-	char *buf, **cmd;
-	
-	if( ( buf = ipc_readline( source ) ) )
-	{
-		cmd = irc_parse_line( buf );
-		if( cmd )
-			ipc_command_exec( data, cmd, ipc_child_commands );
-	}
-	else
-	{
-		b_event_remove( global.listen_watch_source_id );
-		close( global.listen_socket );
-		
-		global.listen_socket = -1;
-	}
-	
-	return TRUE;
-}
-
-void ipc_to_master( char **cmd )
-{
-	if( global.conf->runmode == RUNMODE_FORKDAEMON )
-	{
-		char *s = irc_build_line( cmd );
-		ipc_to_master_str( "%s", s );
-		g_free( s );
-	}
-	else if( global.conf->runmode == RUNMODE_DAEMON )
-	{
-		ipc_command_exec( NULL, cmd, ipc_master_commands );
-	}
-}
-
-void ipc_to_master_str( char *format, ... )
-{
-	char *msg_buf;
-	va_list params;
-
-	va_start( params, format );
-	msg_buf = g_strdup_vprintf( format, params );
-	va_end( params );
-	
-	if( strlen( msg_buf ) > 512 )
-	{
-		/* Don't send it, it's too long... */
-	}
-	else if( global.conf->runmode == RUNMODE_FORKDAEMON )
-	{
-		write( global.listen_socket, msg_buf, strlen( msg_buf ) );
-	}
-	else if( global.conf->runmode == RUNMODE_DAEMON )
-	{
-		char **cmd, *s;
-		
-		if( ( s = strchr( msg_buf, '\r' ) ) )
-			*s = 0;
-		
-		cmd = irc_parse_line( msg_buf );
-		ipc_command_exec( NULL, cmd, ipc_master_commands );
-		g_free( cmd );
-	}
-	
-	g_free( msg_buf );
-}
-
-void ipc_to_children( char **cmd )
-{
-	if( global.conf->runmode == RUNMODE_FORKDAEMON )
-	{
-		char *msg_buf = irc_build_line( cmd );
-		ipc_to_children_str( "%s", msg_buf );
-		g_free( msg_buf );
-	}
-	else if( global.conf->runmode == RUNMODE_DAEMON )
-	{
-		GSList *l;
-		
-		for( l = irc_connection_list; l; l = l->next )
-			ipc_command_exec( l->data, cmd, ipc_child_commands );
-	}
-}
-
-void ipc_to_children_str( char *format, ... )
-{
-	char *msg_buf;
-	va_list params;
-
-	va_start( params, format );
-	msg_buf = g_strdup_vprintf( format, params );
-	va_end( params );
-	
-	if( strlen( msg_buf ) > 512 )
-	{
-		/* Don't send it, it's too long... */
-	}
-	else if( global.conf->runmode == RUNMODE_FORKDAEMON )
-	{
-		int msg_len = strlen( msg_buf );
-		GSList *l;
-		
-		for( l = child_list; l; l = l->next )
-		{
-			struct bitlbee_child *c = l->data;
-			write( c->ipc_fd, msg_buf, msg_len );
 		}
 	}
@@ -410,4 +424,21 @@
 }
 
+void ipc_master_free_fd( int fd )
+{
+	GSList *l;
+	struct bitlbee_child *c;
+	
+	for( l = child_list; l; l = l->next )
+	{
+		c = l->data;
+		if( c->ipc_fd == fd )
+		{
+			ipc_master_free_one( c );
+			child_list = g_slist_remove( child_list, c );
+			break;
+		}
+	}
+}
+
 void ipc_master_free_all()
 {
@@ -421,4 +452,13 @@
 }
 
+void ipc_child_disable()
+{
+	b_event_remove( global.listen_watch_source_id );
+	close( global.listen_socket );
+	
+	global.listen_socket = -1;
+}
+
+#ifndef _WIN32
 char *ipc_master_save_state()
 {
@@ -461,9 +501,4 @@
 }
 
-void ipc_master_set_statefile( char *fn )
-{
-	statefile = g_strdup( fn );
-}
-
 
 static gboolean new_ipc_client( gpointer data, gint serversock, b_input_condition cond )
@@ -486,5 +521,4 @@
 }
 
-#ifndef _WIN32
 int ipc_master_listen_socket()
 {
@@ -523,8 +557,12 @@
 }
 #else
+int ipc_master_listen_socket()
+{
 	/* FIXME: Open named pipe \\.\BITLBEE */
+	return 0;
+}
 #endif
 
-int ipc_master_load_state()
+int ipc_master_load_state( char *statefile )
 {
 	struct bitlbee_child *child;
@@ -534,4 +572,5 @@
 	if( statefile == NULL )
 		return 0;
+	
 	fp = fopen( statefile, "r" );
 	unlink( statefile );	/* Why do it later? :-) */
Index: ipc.h
===================================================================
--- ipc.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ ipc.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -44,5 +44,8 @@
 
 void ipc_master_free_one( struct bitlbee_child *child );
+void ipc_master_free_fd( int fd );
 void ipc_master_free_all();
+
+void ipc_child_disable();
 
 void ipc_to_master( char **cmd );
@@ -55,6 +58,5 @@
 
 char *ipc_master_save_state();
-void ipc_master_set_statefile( char *fn );
-int ipc_master_load_state();
+int ipc_master_load_state( char *statefile );
 int ipc_master_listen_socket();
 
Index: irc.c
===================================================================
--- irc.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ irc.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -26,4 +26,5 @@
 #define BITLBEE_CORE
 #include "bitlbee.h"
+#include "sock.h"
 #include "crypting.h"
 #include "ipc.h"
@@ -45,4 +46,33 @@
 }
 
+static char *set_eval_charset( set_t *set, char *value )
+{
+	irc_t *irc = set->data;
+	GIConv ic, oc;
+
+	if( g_strcasecmp( value, "none" ) == 0 )
+		value = g_strdup( "utf-8" );
+
+	if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
+	{
+		return NULL;
+	}
+	if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
+	{
+		g_iconv_close( ic );
+		return NULL;
+	}
+	
+	if( irc->iconv != (GIConv) -1 )
+		g_iconv_close( irc->iconv );
+	if( irc->oconv != (GIConv) -1 )
+		g_iconv_close( irc->oconv );
+	
+	irc->iconv = ic;
+	irc->oconv = oc;
+
+	return value;
+}
+
 irc_t *irc_new( int fd )
 {
@@ -67,4 +97,7 @@
 	irc->mynick = g_strdup( ROOT_NICK );
 	irc->channel = g_strdup( ROOT_CHAN );
+	
+	irc->iconv = (GIConv) -1;
+	irc->oconv = (GIConv) -1;
 	
 	if( global.conf->hostname )
@@ -126,4 +159,5 @@
 	set_add( &irc->set, "private", "true", set_eval_bool, irc );
 	set_add( &irc->set, "query_order", "lifo", NULL, irc );
+	set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
 	set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
 	set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
@@ -137,4 +171,7 @@
 	irc->otr = otr_new();
 	
+	/* Evaluator sets the iconv/oconv structures. */
+	set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
+	
 	return( irc );
 }
@@ -174,10 +211,12 @@
 	if( irc->sendbuffer && !immed )
 	{
-		/* We won't read from this socket anymore. Instead, we'll connect a timer
-		   to it that should shut down the connection in a second, just in case
-		   bitlbee_.._write doesn't do it first. */
+		/* Set up a timeout event that should shut down the connection
+		   in a second, just in case ..._write doesn't do it first. */
 		
 		b_event_remove( irc->r_watch_source_id );
-		irc->r_watch_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
+		irc->r_watch_source_id = 0;
+		
+		b_event_remove( irc->ping_source_id );
+		irc->ping_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
 	}
 	else
@@ -195,7 +234,6 @@
 
 /* Because we have no garbage collection, this is quite annoying */
-void irc_free(irc_t * irc)
-{
-	account_t *account;
+void irc_free( irc_t * irc )
+{
 	user_t *user, *usertmp;
 	
@@ -206,78 +244,92 @@
 			irc_usermsg( irc, "Error while saving settings!" );
 	
-	closesocket( irc->fd );
-	
-	if( irc->ping_source_id > 0 )
-		b_event_remove( irc->ping_source_id );
-	b_event_remove( irc->r_watch_source_id );
-	if( irc->w_watch_source_id > 0 )
-		b_event_remove( irc->w_watch_source_id );
-	
 	irc_connection_list = g_slist_remove( irc_connection_list, irc );
 	
-	for (account = irc->accounts; account; account = account->next) {
-		if (account->ic) {
-			imc_logout(account->ic, TRUE);
-		} else if (account->reconnect) {
-			cancel_auto_reconnect(account);
-		}
-	}
-	
-	g_free(irc->sendbuffer);
-	g_free(irc->readbuffer);
-	
-	g_free(irc->nick);
-	g_free(irc->user);
-	g_free(irc->host);
-	g_free(irc->realname);
-	g_free(irc->password);
-	
-	g_free(irc->myhost);
-	g_free(irc->mynick);
-	
-	g_free(irc->channel);
-	
-	while (irc->queries != NULL)
-		query_del(irc, irc->queries);
-	
-	while (irc->accounts)
-		if (irc->accounts->ic == NULL)
-			account_del(irc, irc->accounts);
+	while( irc->accounts )
+	{
+		if( irc->accounts->ic )
+			imc_logout( irc->accounts->ic, FALSE );
+		else if( irc->accounts->reconnect )
+			cancel_auto_reconnect( irc->accounts );
+		
+		if( irc->accounts->ic == NULL )
+			account_del( irc, irc->accounts );
 		else
 			/* Nasty hack, but account_del() doesn't work in this
 			   case and we don't want infinite loops, do we? ;-) */
 			irc->accounts = irc->accounts->next;
-	
-	while (irc->set)
-		set_del(&irc->set, irc->set->key);
-	
-	if (irc->users != NULL) {
+	}
+	
+	while( irc->queries != NULL )
+		query_del( irc, irc->queries );
+	
+	while( irc->set )
+		set_del( &irc->set, irc->set->key );
+	
+	if (irc->users != NULL)
+	{
 		user = irc->users;
-		while (user != NULL) {
-			g_free(user->nick);
-			g_free(user->away);
-			g_free(user->handle);
-			if(user->user!=user->nick) g_free(user->user);
-			if(user->host!=user->nick) g_free(user->host);
-			if(user->realname!=user->nick) g_free(user->realname);
-			b_event_remove(user->sendbuf_timer);
+		while( user != NULL )
+		{
+			g_free( user->nick );
+			g_free( user->away );
+			g_free( user->handle );
+			if( user->user != user->nick ) g_free( user->user );
+			if( user->host != user->nick ) g_free( user->host );
+			if( user->realname != user->nick ) g_free( user->realname );
+			b_event_remove( user->sendbuf_timer );
 					
 			usertmp = user;
 			user = user->next;
-			g_free(usertmp);
-		}
-	}
-	
-	g_hash_table_foreach_remove(irc->userhash, irc_free_hashkey, NULL);
-	g_hash_table_destroy(irc->userhash);
-	
-	g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);
-	g_hash_table_destroy(irc->watches);
+			g_free( usertmp );
+		}
+	}
+	
+	if( irc->ping_source_id > 0 )
+		b_event_remove( irc->ping_source_id );
+	if( irc->r_watch_source_id > 0 )
+		b_event_remove( irc->r_watch_source_id );
+	if( irc->w_watch_source_id > 0 )
+		b_event_remove( irc->w_watch_source_id );
+	
+	closesocket( irc->fd );
+	irc->fd = -1;
+	
+	g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
+	g_hash_table_destroy( irc->userhash );
+	
+	g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
+	g_hash_table_destroy( irc->watches );
+	
+	if( irc->iconv != (GIConv) -1 )
+		g_iconv_close( irc->iconv );
+	if( irc->oconv != (GIConv) -1 )
+		g_iconv_close( irc->oconv );
+	
+	g_free( irc->sendbuffer );
+	g_free( irc->readbuffer );
+	
+	g_free( irc->nick );
+	g_free( irc->user );
+	g_free( irc->host );
+	g_free( irc->realname );
+	g_free( irc->password );
+	
+	g_free( irc->myhost );
+	g_free( irc->mynick );
+	
+	g_free( irc->channel );
+	
+	g_free( irc->last_target );
 	
 	otr_free(irc->otr);
 	
-	g_free(irc);
-	
-	if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON )
+	g_free( irc );
+	
+	if( global.conf->runmode == RUNMODE_INETD ||
+	    global.conf->runmode == RUNMODE_FORKDAEMON ||
+	    ( global.conf->runmode == RUNMODE_DAEMON &&
+	      global.listen_socket == -1 &&
+	      irc_connection_list == NULL ) )
 		b_main_quit();
 }
@@ -298,5 +350,5 @@
 void irc_process( irc_t *irc )
 {
-	char **lines, *temp, **cmd, *cs;
+	char **lines, *temp, **cmd;
 	int i;
 
@@ -307,9 +359,8 @@
 		for( i = 0; *lines[i] != '\0'; i ++ )
 		{
-			char conv[IRC_MAX_LINE+1];
+			char *conv = NULL;
 			
-			/* [WvG] Because irc_tokenize splits at every newline, the lines[] list
-			    should end with an empty string. This is why this actually works.
-			    Took me a while to figure out, Maurits. :-P */
+			/* [WvG] If the last line isn't empty, it's an incomplete line and we
+			   should wait for the rest to come in before processing it. */
 			if( lines[i+1] == NULL )
 			{
@@ -321,8 +372,12 @@
 			}
 			
-			if( ( cs = set_getstr( &irc->set, "charset" ) ) )
-			{
-				conv[IRC_MAX_LINE] = 0;
-				if( do_iconv( cs, "UTF-8", lines[i], conv, 0, IRC_MAX_LINE - 2 ) == -1 )
+			if( irc->iconv != (GIConv) -1 )
+			{
+				gsize bytes_read, bytes_written;
+				
+				conv = g_convert_with_iconv( lines[i], -1, irc->iconv,
+				                             &bytes_read, &bytes_written, NULL );
+				
+				if( conv == NULL || bytes_read != strlen( lines[i] ) )
 				{
 					/* GLib can do strange things if things are not in the expected charset,
@@ -336,13 +391,16 @@
 						                  "expect by changing the charset setting. See "
 						                  "`help set charset' for more information. Your "
-						                  "message was ignored.", cs );
-						*conv = 0;
+						                  "message was ignored.",
+						                  set_getstr( &irc->set, "charset" ) );
+						
+						g_free( conv );
+						conv = NULL;
 					}
 					else
 					{
 						irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
-						           "Warning: invalid (non-UTF8) characters received at login time." );
+						           "Warning: invalid characters received at login time." );
 						
-						strncpy( conv, lines[i], IRC_MAX_LINE );
+						conv = g_strdup( lines[i] );
 						for( temp = conv; *temp; temp ++ )
 							if( *temp & 0x80 )
@@ -353,9 +411,13 @@
 			}
 			
-			if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
-				continue;
-			irc_exec( irc, cmd );
+			if( lines[i] )
+			{
+				if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
+					continue;
+				irc_exec( irc, cmd );
+				g_free( cmd );
+			}
 			
-			g_free( cmd );
+			g_free( conv );
 			
 			/* Shouldn't really happen, but just in case... */
@@ -381,40 +443,39 @@
 char **irc_tokenize( char *buffer )
 {
-	int i, j;
+	int i, j, n = 3;
 	char **lines;
 
-	/* Count the number of elements we're gonna need. */
-	for( i = 0, j = 1; buffer[i] != '\0'; i ++ )
-	{
-		if( buffer[i] == '\n' )
-			if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
-				j ++;
-	}
-	
-	/* Allocate j+1 elements. */
-	lines = g_new( char *, j + 1 );
+	/* Allocate n+1 elements. */
+	lines = g_new( char *, n + 1 );
+	
+	lines[0] = buffer;
+	
+	/* Split the buffer in several strings, and accept any kind of line endings,
+	 * knowing that ERC on Windows may send something interesting like \r\r\n,
+	 * and surely there must be clients that think just \n is enough... */
+	for( i = 0, j = 0; buffer[i] != '\0'; i ++ )
+	{
+		if( buffer[i] == '\r' || buffer[i] == '\n' )
+		{
+			while( buffer[i] == '\r' || buffer[i] == '\n' )
+				buffer[i++] = '\0';
+			
+			lines[++j] = buffer + i;
+			
+			if( j >= n )
+			{
+				n *= 2;
+				lines = g_renew( char *, lines, n + 1 );
+			}
+
+			if( buffer[i] == '\0' )
+				break;
+		}
+	}
 	
 	/* NULL terminate our list. */ 
-	lines[j] = NULL;
-	
-	lines[0] = buffer;
-	
-	/* Split the buffer in several strings, using \r\n as our seperator, where \r is optional.
-	 * Although this is not in the RFC, some braindead ircds (newnet's) use this, so some clients might too. 
-	 */
-	for( i = 0, j = 0; buffer[i] != '\0'; i ++)
-	{
-		if( buffer[i] == '\n' )
-		{
-			buffer[i] = '\0';
-			
-			if( i > 0 && buffer[i-1] == '\r' )
-				buffer[i-1] = '\0';
-			if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
-				lines[++j] = buffer + i + 1;
-		}
-	}
-	
-	return( lines );
+	lines[++j] = NULL;
+	
+	return lines;
 }
 
@@ -550,5 +611,4 @@
 
 	return;
-
 }
 
@@ -556,5 +616,5 @@
 {
 	int size;
-	char line[IRC_MAX_LINE+1], *cs;
+	char line[IRC_MAX_LINE+1];
 		
 	/* Don't try to write anything new anymore when shutting down. */
@@ -562,17 +622,22 @@
 		return;
 	
-	line[IRC_MAX_LINE] = 0;
+	memset( line, 0, sizeof( line ) );
 	g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
-	
 	strip_newlines( line );
-	if( ( cs = set_getstr( &irc->set, "charset" ) ) && ( g_strcasecmp( cs, "utf-8" ) != 0 ) )
-	{
-		char conv[IRC_MAX_LINE+1];
-		
-		conv[IRC_MAX_LINE] = 0;
-		if( do_iconv( "UTF-8", cs, line, conv, 0, IRC_MAX_LINE - 2 ) != -1 )
-			strcpy( line, conv );
-	}
-	strcat( line, "\r\n" );
+	
+	if( irc->oconv != (GIConv) -1 )
+	{
+		gsize bytes_read, bytes_written;
+		char *conv;
+		
+		conv = g_convert_with_iconv( line, -1, irc->oconv,
+		                             &bytes_read, &bytes_written, NULL );
+
+		if( bytes_read == strlen( line ) )
+			strncpy( line, conv, IRC_MAX_LINE - 2 );
+		
+		g_free( conv );
+	}
+	g_strlcat( line, "\r\n", IRC_MAX_LINE + 1 );
 	
 	if( irc->sendbuffer != NULL )
@@ -753,6 +818,5 @@
 	irc->umode[0] = '\0';
 	irc_umode_set( irc, "+" UMODE, 1 );
-
-	u = user_add( irc, irc->mynick );
+u = user_add( irc, irc->mynick );
 	u->host = g_strdup( irc->myhost );
 	u->realname = g_strdup( ROOT_FN );
@@ -782,4 +846,14 @@
 	
 	irc->status |= USTATUS_LOGGED_IN;
+	
+	/* This is for bug #209 (use PASS to identify to NickServ). */
+	if( irc->password != NULL )
+	{
+		char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
+		
+		irc_setpass( irc, NULL );
+		root_command( irc, send_cmd );
+		g_free( send_cmd[1] );
+	}
 }
 
@@ -791,5 +865,12 @@
 	if( !f )
 	{
-		irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\nIf you've never used BitlBee before, please do read the help information using the \x02help\x02 command. Lots of FAQs are answered there.\n\nOTR users please note: Private key files are owned by the user BitlBee is running as." );
+		irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
+	        	          "If you've never used BitlBee before, please do read the help "
+		                  "information using the \x02help\x02 command. Lots of FAQs are "
+		                  "answered there.\n"
+				  "OTR users please note: Private key files are owned by the user "
+				  "BitlBee is running as.\n"
+		                  "If you already have an account on this server, just use the "
+		                  "\x02identify\x02 command to identify yourself." );
 	}
 	else
Index: irc.h
===================================================================
--- irc.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ irc.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -63,4 +63,5 @@
 	char *sendbuffer;
 	char *readbuffer;
+	GIConv iconv, oconv;
 
 	int sentbytes;
@@ -71,5 +72,7 @@
 	char *host;
 	char *realname;
-	char *password;
+	char *password; /* HACK: Used to save the user's password, but before
+	                   logging in, this may contain a password we should
+	                   send to identify after USER/NICK are received. */
 
 	char umode[8];
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ irc_commands.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -30,12 +30,33 @@
 static void irc_cmd_pass( irc_t *irc, char **cmd )
 {
-	if( global.conf->auth_pass && strcmp( cmd[1], global.conf->auth_pass ) == 0 )
+	if( irc->status & USTATUS_LOGGED_IN )
+	{
+		char *send_cmd[] = { "identify", cmd[1], NULL };
+		
+		/* We're already logged in, this client seems to send the PASS
+		   command last. (Possibly it won't send it at all if it turns
+		   out we don't require it, which will break this feature.)
+		   Try to identify using the given password. */
+		return root_command( irc, send_cmd );
+	}
+	/* Handling in pre-logged-in state, first see if this server is
+	   password-protected: */
+	else if( global.conf->auth_pass &&
+	    ( strncmp( global.conf->auth_pass, "md5:", 4 ) == 0 ?
+	        md5_verify_password( cmd[1], global.conf->auth_pass + 4 ) == 0 :
+	        strcmp( cmd[1], global.conf->auth_pass ) == 0 ) )
 	{
 		irc->status |= USTATUS_AUTHORIZED;
 		irc_check_login( irc );
 	}
-	else
+	else if( global.conf->auth_pass )
 	{
 		irc_reply( irc, 464, ":Incorrect password" );
+	}
+	else
+	{
+		/* Remember the password and try to identify after USER/NICK. */
+		irc_setpass( irc, cmd[1] );
+		irc_check_login( irc );
 	}
 }
@@ -88,5 +109,8 @@
 static void irc_cmd_oper( irc_t *irc, char **cmd )
 {
-	if( global.conf->oper_pass && strcmp( cmd[2], global.conf->oper_pass ) == 0 )
+	if( global.conf->oper_pass &&
+	    ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
+	        md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
+	        strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
 	{
 		irc_umode_set( irc, "+o", 1 );
@@ -254,6 +278,5 @@
 			if( cmd[1] != irc->last_target )
 			{
-				if( irc->last_target )
-					g_free( irc->last_target );
+				g_free( irc->last_target );
 				irc->last_target = g_strdup( cmd[1] );
 			}
@@ -575,5 +598,5 @@
 
 static const command_t irc_commands[] = {
-	{ "pass",        1, irc_cmd_pass,        IRC_CMD_PRE_LOGIN },
+	{ "pass",        1, irc_cmd_pass,        0 },
 	{ "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
 	{ "nick",        1, irc_cmd_nick,        0 },
@@ -603,4 +626,5 @@
 	{ "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
 	{ "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
+	{ "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
 	{ "wallops",     1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
 	{ "wall",        1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
Index: lib/Makefile
===================================================================
--- lib/Makefile	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/Makefile	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -10,5 +10,5 @@
 
 # [SH] Program variables
-objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o
+objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
 
 CFLAGS += -Wall
Index: lib/arc.c
===================================================================
--- lib/arc.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/arc.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -131,15 +131,37 @@
    
    Both functions return the number of bytes in the result string.
+   
+   Note that if you use the pad_to argument, you will need zero-termi-
+   nation to find back the original string length after decryption. So
+   it shouldn't be used if your string contains \0s by itself!
 */
 
-int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password )
+int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to )
 {
 	struct arc_state *st;
 	unsigned char *key;
-	int key_len, i;
+	char *padded = NULL;
+	int key_len, i, padded_len;
 	
 	key_len = strlen( password ) + ARC_IV_LEN;
 	if( clear_len <= 0 )
 		clear_len = strlen( clear );
+	
+	/* Pad the string to the closest multiple of pad_to. This makes it
+	   impossible to see the exact length of the password. */
+	if( pad_to > 0 && ( clear_len % pad_to ) > 0 )
+	{
+		padded_len = clear_len + pad_to - ( clear_len % pad_to );
+		padded = g_malloc( padded_len );
+		memcpy( padded, clear, clear_len );
+		
+		/* First a \0 and then random data, so we don't have to do
+		   anything special when decrypting. */
+		padded[clear_len] = 0;
+		random_bytes( (unsigned char*) padded + clear_len + 1, padded_len - clear_len - 1 );
+		
+		clear = padded;
+		clear_len = padded_len;
+	}
 	
 	/* Prepare buffers and the key + IV */
@@ -161,4 +183,5 @@
 	
 	g_free( st );
+	g_free( padded );
 	
 	return clear_len + ARC_IV_LEN;
Index: lib/arc.h
===================================================================
--- lib/arc.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/arc.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -31,6 +31,10 @@
 };
 
-struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );
+#ifndef G_GNUC_MALLOC
+#define G_GNUC_MALLOC
+#endif
+
+G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );
 unsigned char arc_getbyte( struct arc_state *st );
-int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password );
+int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to );
 int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password );
Index: lib/base64.c
===================================================================
--- lib/base64.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/base64.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -118,5 +118,5 @@
 	int i, outlen = 0;
 	
-	for( i = 0; in[i]; i += 4 )
+	for( i = 0; in[i] && in[i+1] && in[i+2] && in[i+3]; i += 4 )
 	{
 		int sx;
Index: lib/events_glib.c
===================================================================
--- lib/events_glib.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/events_glib.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -51,9 +51,10 @@
 } GaimIOClosure;
 
-static GMainLoop *loop;
+static GMainLoop *loop = NULL;
 
 void b_main_init()
 {
-	loop = g_main_new( FALSE );
+	if( loop == NULL )
+		loop = g_main_new( FALSE );
 }
 
Index: lib/misc.c
===================================================================
--- lib/misc.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/misc.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -33,4 +33,5 @@
 #define BITLBEE_CORE
 #include "nogaim.h"
+#include "base64.h"
 #include <stdio.h>
 #include <stdlib.h>
@@ -59,29 +60,4 @@
 	strcpy(text, text2);
 	g_free(text2);
-}
-
-char *normalize(const char *s)
-{
-	static char buf[BUF_LEN];
-	char *t, *u;
-	int x = 0;
-
-	g_return_val_if_fail((s != NULL), NULL);
-
-	u = t = g_strdup(s);
-
-	strcpy(t, s);
-	g_strdown(t);
-
-	while (*t && (x < BUF_LEN - 1)) {
-		if (*t != ' ') {
-			buf[x] = *t;
-			x++;
-		}
-		t++;
-	}
-	buf[x] = '\0';
-	g_free(u);
-	return buf;
 }
 
@@ -408,4 +384,5 @@
 void random_bytes( unsigned char *buf, int count )
 {
+#ifndef _WIN32
 	static int use_dev = -1;
 	
@@ -457,4 +434,5 @@
 	
 	if( !use_dev )
+#endif
 	{
 		int i;
@@ -608,2 +586,38 @@
 		return sockerr_again();
 }
+
+/* Returns values: -1 == Failure (base64-decoded to something unexpected)
+                    0 == Okay
+                    1 == Password doesn't match the hash. */
+int md5_verify_password( char *password, char *hash )
+{
+	md5_byte_t *pass_dec = NULL;
+	md5_byte_t pass_md5[16];
+	md5_state_t md5_state;
+	int ret = -1, i;
+	
+	if( base64_decode( hash, &pass_dec ) == 21 )
+	{
+		md5_init( &md5_state );
+		md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
+		md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
+		md5_finish( &md5_state, pass_md5 );
+		
+		for( i = 0; i < 16; i ++ )
+		{
+			if( pass_dec[i] != pass_md5[i] )
+			{
+				ret = 1;
+				break;
+			}
+		}
+		
+		/* If we reached the end of the loop, it was a match! */
+		if( i == 16 )
+			ret = 0;
+	}
+	
+	g_free( pass_dec );
+
+	return ret;
+}
Index: lib/misc.h
===================================================================
--- lib/misc.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/misc.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -41,5 +41,4 @@
 G_MODULE_EXPORT char *add_cr( char *text );
 G_MODULE_EXPORT char *strip_newlines(char *source);
-G_MODULE_EXPORT char *normalize( const char *s );
 
 G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );
@@ -67,3 +66,5 @@
 G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl );
 
+G_MODULE_EXPORT int md5_verify_password( char *password, char *hash );
+
 #endif
Index: lib/proxy.c
===================================================================
--- lib/proxy.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/proxy.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -114,4 +114,5 @@
 {
 	struct sockaddr_in *sin;
+	struct sockaddr_in me;
 	int fd = -1;
 
@@ -127,4 +128,14 @@
 
 	sock_make_nonblocking(fd);
+	
+	if( global.conf->iface_out )
+	{
+		me.sin_family = AF_INET;
+		me.sin_port = 0;
+		me.sin_addr.s_addr = inet_addr( global.conf->iface_out );
+		
+		if( bind( fd, (struct sockaddr *) &me, sizeof( me ) ) != 0 )
+			event_debug( "bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out );
+	}
 	
 	event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
@@ -530,5 +541,5 @@
 	struct PHB *phb;
 	
-	if (!host || !port || (port == -1) || !func || strlen(host) > 128) {
+	if (!host || port <= 0 || !func || strlen(host) > 128) {
 		return -1;
 	}
@@ -538,5 +549,5 @@
 	phb->data = data;
 	
-	if ((proxytype == PROXY_NONE) || strlen(proxyhost) > 0 || !proxyport || (proxyport == -1))
+	if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0)
 		return proxy_connect_none(host, port, phb);
 	else if (proxytype == PROXY_HTTP)
Index: lib/ssl_bogus.c
===================================================================
--- lib/ssl_bogus.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/ssl_bogus.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -65,2 +65,7 @@
 	return GAIM_INPUT_READ;
 }
+
+int ssl_pending( void *conn )
+{
+	return 0;
+}
Index: lib/ssl_client.h
===================================================================
--- lib/ssl_client.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/ssl_client.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -63,4 +63,7 @@
 G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len );
 
+/* See ssl_openssl.c for an explanation. */
+G_MODULE_EXPORT int ssl_pending( void *conn );
+
 /* Abort the SSL connection and disconnect the socket. Do not use close()
    directly, both the SSL library and the peer will be unhappy! */
Index: lib/ssl_gnutls.c
===================================================================
--- lib/ssl_gnutls.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/ssl_gnutls.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -216,4 +216,10 @@
 }
 
+/* See ssl_openssl.c for an explanation. */
+int ssl_pending( void *conn )
+{
+	return 0;
+}
+
 void ssl_disconnect( void *conn_ )
 {
Index: lib/ssl_nss.c
===================================================================
--- lib/ssl_nss.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/ssl_nss.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -175,4 +175,10 @@
 }
 
+/* See ssl_openssl.c for an explanation. */
+int ssl_pending( void *conn )
+{
+	return 0;
+}
+
 void ssl_disconnect( void *conn_ )
 {
Index: lib/ssl_openssl.c
===================================================================
--- lib/ssl_openssl.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/ssl_openssl.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -68,13 +68,13 @@
 	
 	conn->fd = proxy_connect( host, port, ssl_connected, conn );
+	if( conn->fd < 0 )
+	{
+		g_free( conn );
+		return NULL;
+	}
+	
 	conn->func = func;
 	conn->data = data;
 	conn->inpa = -1;
-	
-	if( conn->fd < 0 )
-	{
-		g_free( conn );
-		return NULL;
-	}
 	
 	return conn;
@@ -236,4 +236,19 @@
 }
 
+/* Only OpenSSL *really* needs this (and well, maybe NSS). See for more info:
+   http://www.gnu.org/software/gnutls/manual/gnutls.html#index-gnutls_005frecord_005fcheck_005fpending-209
+   http://www.openssl.org/docs/ssl/SSL_pending.html
+   
+   Required because OpenSSL empties the TCP buffer completely but doesn't
+   necessarily give us all the unencrypted data.
+   
+   Returns 0 if there's nothing left or if we don't have to care (GnuTLS),
+   1 if there's more data. */
+int ssl_pending( void *conn )
+{
+	return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ?
+	       SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0;
+}
+
 void ssl_disconnect( void *conn_ )
 {
Index: lib/ssl_sspi.c
===================================================================
--- lib/ssl_sspi.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ lib/ssl_sspi.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,278 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* SSL module - SSPI backend */
+
+/* Copyright (C) 2005 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
+*/
+
+#include "ssl_client.h"
+#include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <sspi.h>
+#include <schannel.h>
+#include "sock.h"
+
+static gboolean initialized = FALSE;
+int ssl_errno;
+
+struct scd
+{
+	int fd;
+	ssl_input_function func;
+	gpointer data;
+	gboolean established;
+  	CredHandle cred;		/* SSL credentials */
+	CtxtHandle context;		/* SSL context */
+	SecPkgContext_StreamSizes sizes;
+
+	char *host;
+
+	char *pending_raw_data;
+	gsize pending_raw_data_len;
+	char *pending_data;
+	gsize pending_data_len;
+};
+
+static void ssl_connected(gpointer, gint, GaimInputCondition);
+
+void sspi_global_init(void)
+{
+	/* FIXME */
+}
+
+void sspi_global_deinit(void)
+{
+	/* FIXME */
+}
+
+void *ssl_connect(char *host, int port, ssl_input_function func, gpointer data)
+{
+	struct scd *conn = g_new0(struct scd, 1);
+		
+	conn->fd = proxy_connect(host, port, ssl_connected, conn);
+	sock_make_nonblocking(conn->fd);
+	conn->func = func;
+	conn->data = data;
+	conn->host = g_strdup(host);
+	
+	if (conn->fd < 0)
+	{
+		g_free(conn);
+		return NULL;
+	}
+	
+	if (!initialized)
+	{
+		sspi_global_init();
+		initialized = TRUE;
+		atexit(sspi_global_deinit);
+	}
+
+	return conn;
+}
+
+static void ssl_connected(gpointer _conn, gint fd, GaimInputCondition cond)
+{
+	struct scd *conn = _conn;
+	SCHANNEL_CRED ssl_cred;
+	TimeStamp timestamp;
+	SecBuffer ibuf[2],obuf[1];
+	SecBufferDesc ibufs,obufs;
+	ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+    	ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+      	ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+		ISC_REQ_MANUAL_CRED_VALIDATION;
+	ULONG a;
+	gsize size = 0;
+	gchar *data = NULL;
+
+	memset(&ssl_cred, 0, sizeof(SCHANNEL_CRED));
+	ssl_cred.dwVersion = SCHANNEL_CRED_VERSION;
+	ssl_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
+
+	SECURITY_STATUS st = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &ssl_cred, NULL, NULL, &conn->cred, &timestamp);
+
+	if (st != SEC_E_OK) {
+		conn->func(conn->data, NULL, cond);
+		return;
+	}
+	
+	do {
+		/* initialize buffers */
+	    ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = data;
+	    ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NULL;
+	    obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NULL;
+    	ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+	    ibuf[1].BufferType = SECBUFFER_EMPTY;
+
+		/* initialize buffer descriptors */
+	    ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+	    ibufs.cBuffers = 2; obufs.cBuffers = 1;
+	    ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+
+		st = InitializeSecurityContext(&conn->cred, size?&conn->context:NULL, conn->host, req, 0, SECURITY_NETWORK_DREP, size?&ibufs:NULL, 0, &conn->context, &obufs, &a, &timestamp);  
+    	if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+			/* FIXME: Check return value */
+			send(conn->fd, obuf[0].pvBuffer, obuf[0].cbBuffer, 0);
+		}
+
+		switch (st) {
+		case SEC_I_INCOMPLETE_CREDENTIALS:
+			break;
+		case SEC_I_CONTINUE_NEEDED:
+			break;
+		case SEC_E_INCOMPLETE_MESSAGE:
+			break;
+		case SEC_E_OK:
+			break;
+		}
+	
+		QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->sizes);
+	} while (1);
+
+	conn->func(conn->data, conn, cond);
+}
+
+int ssl_read(void *conn, char *retdata, int len)
+{
+	struct scd *scd = conn;
+	SecBufferDesc msg;
+	SecBuffer buf[4];
+	int ret = -1, i;
+	char *data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer);
+
+	/* FIXME: Try to read some data */
+
+  	msg.ulVersion = SECBUFFER_VERSION;
+	msg.cBuffers = 4;
+	msg.pBuffers = buf;
+	
+	buf[0].BufferType = SECBUFFER_DATA;
+	buf[0].cbBuffer = len;
+	buf[0].pvBuffer = data;
+
+	buf[1].BufferType = SECBUFFER_EMPTY;
+	buf[2].BufferType = SECBUFFER_EMPTY;
+	buf[3].BufferType = SECBUFFER_EMPTY;
+
+	SECURITY_STATUS st = DecryptMessage(&scd->context, &msg, 0, NULL);
+
+	if (st != SEC_E_OK) {
+		/* FIXME */
+		return -1;
+	}
+
+	for (i = 0; i < 4; i++) {
+		if (buf[i].BufferType == SECBUFFER_DATA) {
+			memcpy(retdata, buf[i].pvBuffer, len);
+			ret = len;
+		}	
+	}
+
+	g_free(data);
+	return -1;
+}
+
+int ssl_write(void *conn, const char *userdata, int len)
+{
+	struct scd *scd = conn;
+	SecBuffer buf[4];
+	SecBufferDesc msg;
+	char *data;
+	int ret;
+
+	msg.ulVersion = SECBUFFER_VERSION;
+	msg.cBuffers = 4;
+	msg.pBuffers = buf;
+
+	data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer);
+	memcpy(data + scd->sizes.cbHeader, userdata, len);
+
+	buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+	buf[0].cbBuffer = scd->sizes.cbHeader;
+	buf[0].pvBuffer = data;
+
+	buf[1].BufferType = SECBUFFER_DATA;
+	buf[1].cbBuffer = len;
+	buf[1].pvBuffer = data + scd->sizes.cbHeader;
+
+	buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+	buf[2].cbBuffer = scd->sizes.cbTrailer;
+	buf[2].pvBuffer = data + scd->sizes.cbHeader + len;
+	buf[3].BufferType = SECBUFFER_EMPTY;
+
+	SECURITY_STATUS st = EncryptMessage(&scd->context, 0, &msg, 0);
+
+	ret = send(scd->fd, data, 
+				buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer, 0);
+
+	g_free(data);
+
+	return ret;
+}
+
+void ssl_disconnect(void *conn)
+{
+	struct scd *scd = conn;
+
+	SecBufferDesc msg;
+	SecBuffer buf;
+	DWORD dw;
+
+	dw = SCHANNEL_SHUTDOWN;
+	buf.cbBuffer = sizeof(dw);
+	buf.BufferType = SECBUFFER_TOKEN;
+	buf.pvBuffer = &dw;
+	
+	msg.ulVersion = SECBUFFER_VERSION;
+	msg.cBuffers = 1;
+	msg.pBuffers = &buf;
+
+	SECURITY_STATUS st = ApplyControlToken(&scd->context, &msg);
+
+	if (st != SEC_E_OK) {
+		/* FIXME */
+	}
+	
+	/* FIXME: call InitializeSecurityContext(Schannel), passing 
+	 * in empty buffers*/
+
+	DeleteSecurityContext(&scd->context);
+
+	FreeCredentialsHandle(&scd->cred);
+
+	closesocket(scd->fd);
+	g_free(scd->host);
+	g_free(scd);
+}
+
+int ssl_getfd(void *conn)
+{
+	return ((struct scd*)conn)->fd;
+}
+
+GaimInputCondition ssl_getdirection( void *conn )
+{
+	return GAIM_INPUT_WRITE; /* FIXME: or GAIM_INPUT_READ */
+}
Index: lib/url.c
===================================================================
--- lib/url.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/url.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -26,11 +26,14 @@
 #include "url.h"
 
-/* Convert an URL to a url_t structure					*/
+/* Convert an URL to a url_t structure */
 int url_set( url_t *url, char *set_url )
 {
-	char s[MAX_STRING];
+	char s[MAX_STRING+1];
 	char *i;
 	
-	/* protocol://							*/
+	memset( url, 0, sizeof( url_t ) );
+	memset( s, 0, sizeof( s ) );
+	
+	/* protocol:// */
 	if( ( i = strstr( set_url, "://" ) ) == NULL )
 	{
@@ -49,11 +52,10 @@
 			url->proto = PROTO_SOCKS5;
 		else
-		{
-			return( 0 );
-		}
+			return 0;
+		
 		strncpy( s, i + 3, MAX_STRING );
 	}
 	
-	/* Split							*/
+	/* Split */
 	if( ( i = strchr( s, '/' ) ) == NULL )
 	{
@@ -67,5 +69,5 @@
 	strncpy( url->host, s, MAX_STRING );
 	
-	/* Check for username in host field				*/
+	/* Check for username in host field */
 	if( strrchr( url->host, '@' ) != NULL )
 	{
@@ -76,5 +78,5 @@
 		*url->pass = 0;
 	}
-	/* If not: Fill in defaults					*/
+	/* If not: Fill in defaults */
 	else
 	{
@@ -82,5 +84,5 @@
 	}
 	
-	/* Password?							*/
+	/* Password? */
 	if( ( i = strchr( url->user, ':' ) ) != NULL )
 	{
@@ -88,5 +90,5 @@
 		strcpy( url->pass, i + 1 );
 	}
-	/* Port number?							*/
+	/* Port number? */
 	if( ( i = strchr( url->host, ':' ) ) != NULL )
 	{
Index: lib/url.h
===================================================================
--- lib/url.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ lib/url.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -26,9 +26,9 @@
 #include "bitlbee.h"
 
-#define PROTO_HTTP		2
-#define PROTO_HTTPS		5
-#define PROTO_SOCKS4	3
-#define PROTO_SOCKS5	4
-#define PROTO_DEFAULT	PROTO_HTTP
+#define PROTO_HTTP      2
+#define PROTO_HTTPS     5
+#define PROTO_SOCKS4    3
+#define PROTO_SOCKS5    4
+#define PROTO_DEFAULT   PROTO_HTTP
 
 typedef struct url
@@ -36,8 +36,8 @@
 	int proto;
 	int port;
-	char host[MAX_STRING];
-	char file[MAX_STRING];
-	char user[MAX_STRING];
-	char pass[MAX_STRING];
+	char host[MAX_STRING+1];
+	char file[MAX_STRING+1];
+	char user[MAX_STRING+1];
+	char pass[MAX_STRING+1];
 } url_t;
 
Index: lib/xmltree.c
===================================================================
--- lib/xmltree.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ lib/xmltree.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,590 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
+*                                                                           *
+*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "xmltree.h"
+
+static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
+{
+	struct xt_parser *xt = data;
+	struct xt_node *node = g_new0( struct xt_node, 1 ), *nt;
+	int i;
+	
+	node->parent = xt->cur;
+	node->name = g_strdup( element_name );
+	
+	/* First count the number of attributes */
+	for( i = 0; attr_names[i]; i ++ );
+	
+	/* Then allocate a NULL-terminated array. */
+	node->attr = g_new0( struct xt_attr, i + 1 );
+	
+	/* And fill it, saving one variable by starting at the end. */
+	for( i --; i >= 0; i -- )
+	{
+		node->attr[i].key = g_strdup( attr_names[i] );
+		node->attr[i].value = g_strdup( attr_values[i] );
+	}
+	
+	/* Add it to the linked list of children nodes, if we have a current
+	   node yet. */
+	if( xt->cur )
+	{
+		if( xt->cur->children )
+		{
+			for( nt = xt->cur->children; nt->next; nt = nt->next );
+			nt->next = node;
+		}
+		else
+		{
+			xt->cur->children = node;
+		}
+	}
+	else if( xt->root )
+	{
+		/* ERROR situation: A second root-element??? */
+	}
+	
+	/* Now this node will be the new current node. */
+	xt->cur = node;
+	/* And maybe this is the root? */
+	if( xt->root == NULL )
+		xt->root = node;
+}
+
+static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
+{
+	struct xt_parser *xt = data;
+	struct xt_node *node = xt->cur;
+	
+	if( node == NULL )
+		return;
+	
+	/* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */
+	node->text = g_renew( char, node->text, node->text_len + text_len + 1 );
+	memcpy( node->text + node->text_len, text, text_len );
+	node->text_len += text_len;
+	/* Zero termination is always nice to have. */
+	node->text[node->text_len] = 0;
+}
+
+static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
+{
+	struct xt_parser *xt = data;
+	
+	xt->cur->flags |= XT_COMPLETE;
+	xt->cur = xt->cur->parent;
+}
+
+GMarkupParser xt_parser_funcs =
+{
+	xt_start_element,
+	xt_end_element,
+	xt_text,
+	NULL,
+	NULL
+};
+
+struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data )
+{
+	struct xt_parser *xt = g_new0( struct xt_parser, 1 );
+	
+	xt->data = data;
+	xt->handlers = handlers;
+	xt_reset( xt );
+	
+	return xt;
+}
+
+/* Reset the parser, flush everything we have so far. For example, we need
+   this for XMPP when doing TLS/SASL to restart the stream. */
+void xt_reset( struct xt_parser *xt )
+{
+	if( xt->parser )
+		g_markup_parse_context_free( xt->parser );
+	
+	xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL );
+	
+	if( xt->root )
+	{
+		xt_free_node( xt->root );
+		xt->root = NULL;
+		xt->cur = NULL;
+	}
+}
+
+/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
+   end-of-stream and 1 otherwise. */
+int xt_feed( struct xt_parser *xt, char *text, int text_len )
+{
+	if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
+	{
+		return -1;
+	}
+	
+	return !( xt->root && xt->root->flags & XT_COMPLETE );
+}
+
+/* Find completed nodes and see if a handler has to be called. Passing
+   a node isn't necessary if you want to start at the root, just pass
+   NULL. This second argument is needed for recursive calls. */
+int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
+{
+	struct xt_node *c;
+	xt_status st;
+	int i;
+	
+	/* Just in case someone likes infinite loops... */
+	if( xt->root == NULL )
+		return 0;
+	
+	if( node == NULL )
+		return xt_handle( xt, xt->root, depth );
+	
+	if( depth != 0 )
+		for( c = node->children; c; c = c->next )
+			if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) )
+				return 0;
+	
+	if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
+	{
+		for( i = 0; xt->handlers[i].func; i ++ )
+		{
+			/* This one is fun! \o/ */
+			
+						/* If handler.name == NULL it means it should always match. */
+			if( ( xt->handlers[i].name == NULL || 
+						/* If it's not, compare. There should always be a name. */
+			      g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
+						/* If handler.parent == NULL, it's a match. */
+			    ( xt->handlers[i].parent == NULL ||
+						/* If there's a parent node, see if the name matches. */
+			      ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
+						/* If there's no parent, the handler should mention <root> as a parent. */
+			                       g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
+			{
+				st = xt->handlers[i].func( node, xt->data );
+				
+				if( st == XT_ABORT )
+					return 0;
+				else if( st != XT_NEXT )
+					break;
+			}
+		}
+		
+		node->flags |= XT_SEEN;
+	}
+	
+	return 1;
+}
+
+/* Garbage collection: Cleans up all nodes that are handled. Useful for
+   streams because there's no reason to keep a complete packet history
+   in memory. */
+void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
+{
+	struct xt_node *c, *prev;
+	
+	if( !xt || !xt->root )
+		return;
+	
+	if( node == NULL )
+		return xt_cleanup( xt, xt->root, depth );
+	
+	if( node->flags & XT_SEEN && node == xt->root )
+	{
+		xt_free_node( xt->root );
+		xt->root = xt->cur = NULL;
+		/* xt->cur should be NULL already, BTW... */
+		
+		return;
+	}
+	
+	/* c contains the current node, prev the previous node (or NULL).
+	   I admit, this one's pretty horrible. */
+	for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
+	{
+		if( c->flags & XT_SEEN )
+		{
+			/* Remove the node from the linked list. */
+			if( prev )
+				prev->next = c->next;
+			else
+				node->children = c->next;
+			
+			xt_free_node( c );
+			
+			/* Since the for loop wants to get c->next, make sure
+			   c points at something that exists (and that c->next
+			   will actually be the next item we should check). c
+			   can be NULL now, if we just removed the first item.
+			   That explains the ? thing in for(). */
+			c = prev;
+		}
+		else
+		{
+			/* This node can't be cleaned up yet, but maybe a
+			   subnode can. */
+			if( depth != 0 )
+				xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
+		}
+	}
+}
+
+static void xt_to_string_real( struct xt_node *node, GString *str )
+{
+	char *buf;
+	struct xt_node *c;
+	int i;
+	
+	g_string_append_printf( str, "<%s", node->name );
+	
+	for( i = 0; node->attr[i].key; i ++ )
+	{
+		buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
+		g_string_append( str, buf );
+		g_free( buf );
+	}
+	
+	if( node->text == NULL && node->children == NULL )
+	{
+		g_string_append( str, "/>" );
+		return;
+	}
+	
+	g_string_append( str, ">" );
+	if( node->text_len > 0 )
+	{
+		buf = g_markup_escape_text( node->text, node->text_len );
+		g_string_append( str, buf );
+		g_free( buf );
+	}
+	
+	for( c = node->children; c; c = c->next )
+		xt_to_string_real( c, str );
+	
+	g_string_append_printf( str, "</%s>", node->name );
+}
+
+char *xt_to_string( struct xt_node *node )
+{
+	GString *ret;
+	char *real;
+	
+	ret = g_string_new( "" );
+	xt_to_string_real( node, ret );
+	
+	real = ret->str;
+	g_string_free( ret, FALSE );
+	
+	return real;
+}
+
+#ifdef DEBUG
+void xt_print( struct xt_node *node )
+{
+	int i;
+	struct xt_node *c;
+	
+	/* Indentation */
+	for( c = node; c->parent; c = c->parent )
+		printf( "\t" );
+	
+	/* Start the tag */
+	printf( "<%s", node->name );
+	
+	/* Print the attributes */
+	for( i = 0; node->attr[i].key; i ++ )
+		printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) );
+	
+	/* /> 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 )
+	{
+		printf( "/>\n" );
+		return;
+		/* Then we're finished! */
+	}
+	
+	/* Otherwise... */
+	printf( ">" );
+	
+	/* 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] )
+			printf( "%s", g_markup_escape_text( node->text, -1 ) );
+	}
+	
+	if( node->children )
+		printf( "\n" );
+	
+	for( c = node->children; c; c = c->next )
+		xt_print( c );
+	
+	if( node->children )
+		for( c = node; c->parent; c = c->parent )
+			printf( "\t" );
+	
+	/* Non-empty tag is now finished. */
+	printf( "</%s>\n", node->name );
+}
+#endif
+
+struct xt_node *xt_dup( struct xt_node *node )
+{
+	struct xt_node *dup = g_new0( struct xt_node, 1 );
+	struct xt_node *c, *dc = NULL;
+	int i;
+	
+	/* Let's NOT copy the parent element here BTW! Only do it for children. */
+	
+	dup->name = g_strdup( node->name );
+	dup->flags = node->flags;
+	if( node->text )
+	{
+		dup->text = g_memdup( node->text, node->text_len + 1 );
+		dup->text_len = node->text_len;
+	}
+	
+	/* Count the number of attributes and allocate the new array. */
+	for( i = 0; node->attr[i].key; i ++ );
+	dup->attr = g_new0( struct xt_attr, i + 1 );
+	
+	/* Copy them all! */
+	for( i --; i >= 0; i -- )
+	{
+		dup->attr[i].key = g_strdup( node->attr[i].key );
+		dup->attr[i].value = g_strdup( node->attr[i].value );
+	}
+	
+	/* This nice mysterious loop takes care of the children. */
+	for( c = node->children; c; c = c->next )
+	{
+		if( dc == NULL )
+			dc = dup->children = xt_dup( c );
+		else
+			dc = ( dc->next = xt_dup( c ) );
+		
+		dc->parent = dup;
+	}
+	
+	return dup;
+}
+
+/* Frees a node. This doesn't clean up references to itself from parents! */
+void xt_free_node( struct xt_node *node )
+{
+	int i;
+	
+	if( !node )
+		return;
+	
+	g_free( node->name );
+	g_free( node->text );
+	
+	for( i = 0; node->attr[i].key; i ++ )
+	{
+		g_free( node->attr[i].key );
+		g_free( node->attr[i].value );
+	}
+	g_free( node->attr );
+	
+	while( node->children )
+	{
+		struct xt_node *next = node->children->next;
+		
+		xt_free_node( node->children );
+		node->children = next;
+	}
+	
+	g_free( node );
+}
+
+void xt_free( struct xt_parser *xt )
+{
+	if( !xt )
+		return;
+	
+	if( xt->root )
+		xt_free_node( xt->root );
+	
+	g_markup_parse_context_free( xt->parser );
+	
+	g_free( xt );
+}
+
+/* To find a node's child with a specific name, pass the node's children
+   list, not the node itself! The reason you have to do this by hand: So
+   that you can also use this function as a find-next. */
+struct xt_node *xt_find_node( struct xt_node *node, const char *name )
+{
+	while( node )
+	{
+		if( g_strcasecmp( node->name, name ) == 0 )
+			break;
+		
+		node = node->next;
+	}
+	
+	return node;
+}
+
+char *xt_find_attr( struct xt_node *node, const char *key )
+{
+	int i;
+	
+	if( !node )
+		return NULL;
+	
+	for( i = 0; node->attr[i].key; i ++ )
+		if( g_strcasecmp( node->attr[i].key, key ) == 0 )
+			break;
+	
+	return node->attr[i].value;
+}
+
+struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children )
+{
+	struct xt_node *node, *c;
+	
+	node = g_new0( struct xt_node, 1 );
+	node->name = g_strdup( name );
+	node->children = children;
+	node->attr = g_new0( struct xt_attr, 1 );
+	
+	if( text )
+	{
+		node->text_len = strlen( text );
+		node->text = g_memdup( text, node->text_len + 1 );
+	}
+	
+	for( c = children; c; c = c->next )
+	{
+		if( c->parent != NULL )
+		{
+			/* ERROR CONDITION: They seem to have a parent already??? */
+		}
+		
+		c->parent = node;
+	}
+	
+	return node;
+}
+
+void xt_add_child( struct xt_node *parent, struct xt_node *child )
+{
+	struct xt_node *node;
+	
+	/* This function can actually be used to add more than one child, so
+	   do handle this properly. */
+	for( node = child; node; node = node->next )
+	{
+		if( node->parent != NULL )
+		{
+			/* ERROR CONDITION: They seem to have a parent already??? */
+		}
+		
+		node->parent = parent;
+	}
+	
+	if( parent->children == NULL )
+	{
+		parent->children = child;
+	}
+	else
+	{
+		for( node = parent->children; node->next; node = node->next );
+		node->next = child;
+	}
+}
+
+void xt_add_attr( struct xt_node *node, const char *key, const char *value )
+{
+	int i;
+	
+	/* Now actually it'd be nice if we can also change existing attributes
+	   (which actually means this function doesn't have the right name).
+	   So let's find out if we have this attribute already... */
+	for( i = 0; node->attr[i].key; i ++ )
+		if( strcmp( node->attr[i].key, key ) == 0 )
+			break;
+	
+	if( node->attr[i].key == NULL )
+	{
+		/* If not, allocate space for a new attribute. */
+		node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
+		node->attr[i].key = g_strdup( key );
+		node->attr[i+1].key = NULL;
+	}
+	else
+	{
+		/* Otherwise, free the old value before setting the new one. */
+		g_free( node->attr[i].value );
+	}
+	
+	node->attr[i].value = g_strdup( value );
+}
+
+int xt_remove_attr( struct xt_node *node, const char *key )
+{
+	int i, last;
+	
+	for( i = 0; node->attr[i].key; i ++ )
+		if( strcmp( node->attr[i].key, key ) == 0 )
+			break;
+	
+	/* If we didn't find the attribute... */
+	if( node->attr[i].key == NULL )
+		return 0;
+	
+	g_free( node->attr[i].key );
+	g_free( node->attr[i].value );
+	
+	/* If it's the last, this is easy: */
+	if( node->attr[i+1].key == NULL )
+	{
+		node->attr[i].key = node->attr[i].value = NULL;
+	}
+	else /* It's also pretty easy, actually. */
+	{
+		/* Find the last item. */
+		for( last = i + 1; node->attr[last+1].key; last ++ );
+		
+		node->attr[i] = node->attr[last];
+		node->attr[last].key = NULL;
+		node->attr[last].value = NULL;
+	}
+	
+	/* Let's not bother with reallocating memory here. It takes time and
+	   most packets don't stay in memory for long anyway. */
+	
+	return 1;
+}
Index: lib/xmltree.h
===================================================================
--- lib/xmltree.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ lib/xmltree.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,97 @@
+/***************************************************************************\
+*                                                                           *
+*  BitlBee - An IRC to IM gateway                                           *
+*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
+*                                                                           *
+*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
+*                                                                           *
+*  This library is free software; you can redistribute it and/or            *
+*  modify it under the terms of the GNU Lesser General Public               *
+*  License as published by the Free Software Foundation, version            *
+*  2.1.                                                                     *
+*                                                                           *
+*  This library 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        *
+*  Lesser General Public License for more details.                          *
+*                                                                           *
+*  You should have received a copy of the GNU Lesser General Public License *
+*  along with this library; if not, write to the Free Software Foundation,  *
+*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
+*                                                                           *
+****************************************************************************/
+
+#ifndef _XMLTREE_H
+#define _XMLTREE_H
+
+typedef enum
+{
+	XT_COMPLETE	= 1,	/* </tag> reached */
+	XT_SEEN		= 2,	/* Handler called (or not defined) */
+} xt_flags;
+
+typedef enum
+{
+	XT_ABORT,		/* Abort, don't handle the rest anymore */
+	XT_HANDLED,		/* Handled this tag properly, go to the next one */
+	XT_NEXT			/* Try if there's another matching handler */
+} xt_status;
+
+struct xt_attr
+{
+	char *key, *value;
+};
+
+struct xt_node
+{
+	struct xt_node *parent;
+	struct xt_node *children;
+	
+	char *name;
+	struct xt_attr *attr;
+	char *text;
+	int text_len;
+	
+	struct xt_node *next;
+	xt_flags flags;
+};
+
+typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data );
+
+struct xt_handler_entry
+{
+	char *name, *parent;
+	xt_handler_func func;
+};
+
+struct xt_parser
+{
+	GMarkupParseContext *parser;
+	struct xt_node *root;
+	struct xt_node *cur;
+	
+	const struct xt_handler_entry *handlers;
+	gpointer data;
+	
+	GError *gerr;
+};
+
+struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data );
+void xt_reset( struct xt_parser *xt );
+int xt_feed( struct xt_parser *xt, char *text, int text_len );
+int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth );
+void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth );
+char *xt_to_string( struct xt_node *node );
+void xt_print( struct xt_node *node );
+struct xt_node *xt_dup( struct xt_node *node );
+void xt_free_node( struct xt_node *node );
+void xt_free( struct xt_parser *xt );
+struct xt_node *xt_find_node( struct xt_node *node, const char *name );
+char *xt_find_attr( struct xt_node *node, const char *key );
+
+struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children );
+void xt_add_child( struct xt_node *parent, struct xt_node *child );
+void xt_add_attr( struct xt_node *node, const char *key, const char *value );
+int xt_remove_attr( struct xt_node *node, const char *key );
+
+#endif
Index: nick.c
===================================================================
--- nick.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ nick.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -47,4 +47,5 @@
 	
 	store_handle = clean_handle( handle );
+	store_nick[MAX_NICK_LENGTH] = 0;
 	strncpy( store_nick, nick, MAX_NICK_LENGTH );
 	nick_strip( store_nick );
Index: otr.c
===================================================================
--- otr.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ otr.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -46,4 +46,5 @@
 #include <unistd.h>
 #include <assert.h>
+#include <signal.h>
 
 
@@ -102,4 +103,9 @@
 };
 
+typedef struct {
+	void *fst;
+	void *snd;
+} pair_t;	
+
 
 /** misc. helpers/subroutines: **/
@@ -124,8 +130,8 @@
 
 /* some yes/no handlers */
-void yes_keygen(gpointer w, void *data);
-void yes_forget_fingerprint(gpointer w, void *data);
-void yes_forget_context(gpointer w, void *data);
-void yes_forget_key(gpointer w, void *data);
+void yes_keygen(void *data);
+void yes_forget_fingerprint(void *data);
+void yes_forget_context(void *data);
+void yes_forget_key(void *data);
 
 /* helper to make sure accountname and protocol match the incoming "opdata" */
@@ -841,8 +847,11 @@
 }
 
-void yes_forget_fingerprint(gpointer w, void *data)
-{
-	irc_t *irc = (irc_t *)w;
-	Fingerprint *fp = (Fingerprint *)data;
+void yes_forget_fingerprint(void *data)
+{
+	pair_t *p = (pair_t *)data;
+	irc_t *irc = (irc_t *)p->fst;
+	Fingerprint *fp = (Fingerprint *)p->snd;
+
+	g_free(p);
 	
 	if(fp == fp->context->active_fingerprint) {
@@ -854,8 +863,11 @@
 }
 
-void yes_forget_context(gpointer w, void *data)
-{
-	irc_t *irc = (irc_t *)w;
-	ConnContext *ctx = (ConnContext *)data;
+void yes_forget_context(void *data)
+{
+	pair_t *p = (pair_t *)data;
+	irc_t *irc = (irc_t *)p->fst;
+	ConnContext *ctx = (ConnContext *)p->snd;
+
+	g_free(p);
 	
 	if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
@@ -870,5 +882,5 @@
 }
 
-void yes_forget_key(gpointer w, void *data)
+void yes_forget_key(void *data)
 {
 	OtrlPrivKey *key = (OtrlPrivKey *)data;
@@ -889,4 +901,5 @@
 		char human[54];
 		char *s;
+		pair_t *p;
 		
 		if(!args[3]) {
@@ -922,5 +935,10 @@
 		otrl_privkey_hash_to_human(human, fp->fingerprint);
 		s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human);
-		query_add(irc, NULL, s, yes_forget_fingerprint, NULL, fp);
+		p = g_malloc(sizeof(pair_t));
+		if(!p)
+			return;
+		p->fst = irc;
+		p->snd = fp;
+		query_add(irc, NULL, s, yes_forget_fingerprint, NULL, p);
 		g_free(s);
 	}
@@ -931,4 +949,5 @@
 		ConnContext *ctx;
 		char *s;
+		pair_t *p;
 		
 		/* TODO: allow context specs ("user/proto/account") in 'otr forget contex'? */
@@ -952,5 +971,10 @@
 		
 		s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]);
-		query_add(irc, NULL, s, yes_forget_context, NULL, ctx);
+		p = g_malloc(sizeof(pair_t));
+		if(!p)
+			return;
+		p->fst = irc;
+		p->snd = ctx;
+		query_add(irc, NULL, s, yes_forget_context, NULL, p);
 		g_free(s);
 	}
@@ -1660,5 +1684,5 @@
 }
 
-void yes_keygen(gpointer w, void *data)
+void yes_keygen(void *data)
 {
 	account_t *acc = (account_t *)data;
Index: protocols/jabber/Makefile
===================================================================
--- protocols/jabber/Makefile	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/jabber/Makefile	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -10,5 +10,5 @@
 
 # [SH] Program variables
-objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o
+objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o
 
 CFLAGS += -Wall
Index: protocols/jabber/io.c
===================================================================
--- protocols/jabber/io.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/jabber/io.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -241,6 +241,11 @@
 	}
 	
-	/* EAGAIN/etc or a successful read. */
-	return TRUE;
+	if( ssl_pending( jd->ssl ) )
+		/* OpenSSL empties the TCP buffers completely but may keep some
+		   data in its internap buffers. select() won't see that, but
+		   ssl_pending() does. */
+		return jabber_read_callback( data, fd, cond );
+	else
+		return TRUE;
 }
 
@@ -521,6 +526,5 @@
 	   from the server too. */
 	xt_free( jd->xt );	/* In case we're RE-starting. */
-	jd->xt = xt_new( ic );
-	jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
+	jd->xt = xt_new( jabber_handlers, ic );
 	
 	if( jd->r_inpa <= 0 )
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/jabber/jabber.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -33,13 +33,31 @@
 #include "jabber.h"
 #include "md5.h"
-#include "base64.h"
 
 GSList *jabber_connections;
 
+/* First enty is the default */
+static const int jabber_port_list[] = {
+	5222,
+	5223,
+	5220,
+	5221,
+	5224,
+	5225,
+	5226,
+	5227,
+	5228,
+	5229,
+	80,
+	443,
+	0
+};
+
 static void jabber_init( account_t *acc )
 {
 	set_t *s;
-	
-	s = set_add( &acc->set, "port", JABBER_PORT_DEFAULT, set_eval_int, acc );
+	char str[16];
+	
+	g_snprintf( str, sizeof( str ), "%d", jabber_port_list[0] );
+	s = set_add( &acc->set, "port", str, set_eval_int, acc );
 	s->flags |= ACC_SET_OFFLINE_ONLY;
 	
@@ -72,4 +90,5 @@
 	struct ns_srv_reply *srv = NULL;
 	char *connect_to, *s;
+	int i;
 	
 	/* For now this is needed in the _connected() handlers if using
@@ -177,9 +196,11 @@
 	imcb_log( ic, "Connecting" );
 	
-	if( set_getint( &acc->set, "port" ) < JABBER_PORT_MIN ||
-	    set_getint( &acc->set, "port" ) > JABBER_PORT_MAX )
-	{
-		imcb_log( ic, "Incorrect port number, must be in the %d-%d range",
-		               JABBER_PORT_MIN, JABBER_PORT_MAX );
+	for( i = 0; jabber_port_list[i] > 0; i ++ )
+		if( set_getint( &acc->set, "port" ) == jabber_port_list[i] )
+			break;
+
+	if( jabber_port_list[i] == 0 )
+	{
+		imcb_log( ic, "Illegal port number" );
 		imc_logout( ic, FALSE );
 		return;
@@ -219,22 +240,18 @@
 }
 
+/* This generates an unfinished md5_state_t variable. Every time we generate
+   an ID, we finish the state by adding a sequence number and take the hash. */
 static void jabber_generate_id_hash( struct jabber_data *jd )
 {
-	md5_state_t id_hash;
-	md5_byte_t binbuf[16];
+	md5_byte_t binbuf[4];
 	char *s;
 	
-	md5_init( &id_hash );
-	md5_append( &id_hash, (unsigned char *) jd->username, strlen( jd->username ) );
-	md5_append( &id_hash, (unsigned char *) jd->server, strlen( jd->server ) );
+	md5_init( &jd->cached_id_prefix );
+	md5_append( &jd->cached_id_prefix, (unsigned char *) jd->username, strlen( jd->username ) );
+	md5_append( &jd->cached_id_prefix, (unsigned char *) jd->server, strlen( jd->server ) );
 	s = set_getstr( &jd->ic->acc->set, "resource" );
-	md5_append( &id_hash, (unsigned char *) s, strlen( s ) );
-	random_bytes( binbuf, 16 );
-	md5_append( &id_hash, binbuf, 16 );
-	md5_finish( &id_hash, binbuf );
-	
-	s = base64_encode( binbuf, 9 );
-	jd->cached_id_prefix = g_strdup_printf( "%s%s", JABBER_CACHED_ID, s );
-	g_free( s );
+	md5_append( &jd->cached_id_prefix, (unsigned char *) s, strlen( s ) );
+	random_bytes( binbuf, 4 );
+	md5_append( &jd->cached_id_prefix, binbuf, 4 );
 }
 
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/jabber/jabber.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -86,5 +86,5 @@
 	char *away_message;
 	
-	char *cached_id_prefix;
+	md5_state_t cached_id_prefix;
 	GHashTable *node_cache;
 	GHashTable *buddies;
@@ -134,8 +134,4 @@
 
 #define JABBER_XMLCONSOLE_HANDLE "xmlconsole"
-
-#define JABBER_PORT_DEFAULT "5222"
-#define JABBER_PORT_MIN 5220
-#define JABBER_PORT_MAX 5229
 
 /* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/jabber/jabber_util.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -23,4 +23,6 @@
 
 #include "jabber.h"
+#include "md5.h"
+#include "base64.h"
 
 static unsigned int next_id = 1;
@@ -134,9 +136,19 @@
 	struct jabber_data *jd = ic->proto_data;
 	struct jabber_cache_entry *entry = g_new0( struct jabber_cache_entry, 1 );
-	char *id;
-	
-	id = g_strdup_printf( "%s%05x", jd->cached_id_prefix, ( next_id++ ) & 0xfffff );
+	md5_state_t id_hash;
+	md5_byte_t id_sum[16];
+	char *id, *asc_hash;
+	
+	next_id ++;
+	
+	id_hash = jd->cached_id_prefix;
+	md5_append( &id_hash, (md5_byte_t*) &next_id, sizeof( next_id ) );
+	md5_finish( &id_hash, id_sum );
+	asc_hash = base64_encode( id_sum, 12 );
+	
+	id = g_strdup_printf( "%s%s", JABBER_CACHED_ID, asc_hash );
 	xt_add_attr( node, "id", id );
 	g_free( id );
+	g_free( asc_hash );
 	
 	entry->node = node;
@@ -184,5 +196,5 @@
 	
 	if( ( s = xt_find_attr( node, "id" ) ) == NULL ||
-	    strncmp( s, jd->cached_id_prefix, strlen( jd->cached_id_prefix ) ) != 0 )
+	    strncmp( s, JABBER_CACHED_ID, strlen( JABBER_CACHED_ID ) ) != 0 )
 	{
 		/* Silently ignore it, without an ID (or a non-cache
@@ -196,6 +208,12 @@
 	if( entry == NULL )
 	{
+		/*
+		There's no longer an easy way to see if we generated this
+		one or someone else, and there's a ten-minute timeout anyway,
+		so meh.
+		
 		imcb_log( ic, "Warning: Received %s-%s packet with unknown/expired ID %s!",
 		              node->name, xt_find_attr( node, "type" ) ? : "(no type)", s );
+		*/
 	}
 	else if( entry->func )
@@ -246,6 +264,8 @@
 };
 
-static void jabber_buddy_ask_yes( gpointer w, struct jabber_buddy_ask_data *bla )
-{
+static void jabber_buddy_ask_yes( void *data )
+{
+	struct jabber_buddy_ask_data *bla = data;
+	
 	presence_send_request( bla->ic, bla->handle, "subscribed" );
 	
@@ -257,6 +277,8 @@
 }
 
-static void jabber_buddy_ask_no( gpointer w, struct jabber_buddy_ask_data *bla )
-{
+static void jabber_buddy_ask_no( void *data )
+{
+	struct jabber_buddy_ask_data *bla = data;
+	
 	presence_send_request( bla->ic, bla->handle, "subscribed" );
 	
@@ -286,6 +308,11 @@
 	len = strlen( orig );
 	new = g_new( char, len + 1 );
-	for( i = 0; i < len; i ++ )
+	
+	/* So it turns out the /resource part is case sensitive. Yeah, and
+	   it's Unicode but feck Unicode. :-P So stop once we see a slash. */
+	for( i = 0; i < len && orig[i] != '/' ; i ++ )
 		new[i] = tolower( orig[i] );
+	for( ; orig[i]; i ++ )
+		new[i] = orig[i];
 	
 	new[i] = 0;
@@ -330,5 +357,5 @@
 		{
 			/* Check for dupes. */
-			if( g_strcasecmp( bi->resource, s + 1 ) == 0 )
+			if( strcmp( bi->resource, s + 1 ) == 0 )
 			{
 				*s = '/';
@@ -383,5 +410,5 @@
 	if( ( s = strchr( jid, '/' ) ) )
 	{
-		int none_found = 0;
+		int bare_exists = 0;
 		
 		*s = 0;
@@ -406,19 +433,17 @@
 			/* See if there's an exact match. */
 			for( ; bud; bud = bud->next )
-				if( g_strcasecmp( bud->resource, s + 1 ) == 0 )
+				if( strcmp( bud->resource, s + 1 ) == 0 )
 					break;
 		}
 		else
 		{
-			/* This hack is there to make sure that O_CREAT will
-			   work if there's already another resouce present
-			   for this JID, even if it's an unknown buddy. This
-			   is done to handle conferences properly. */
-			none_found = 1;
-			/* TODO(wilmer): Find out what I was thinking when I
-			   wrote this??? And then fix it. This makes me sad... */
-		}
-		
-		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && ( imcb_find_buddy( ic, jid ) || !none_found ) )
+			/* This variable tells the if down here that the bare
+			   JID already exists and we should feel free to add
+			   more resources, if the caller asked for that. */
+			bare_exists = 1;
+		}
+		
+		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) &&
+		    ( !bare_exists || imcb_find_buddy( ic, jid ) ) )
 		{
 			*s = '/';
@@ -445,5 +470,5 @@
 			/* We want an exact match, so in thise case there shouldn't be a /resource. */
 			return NULL;
-		else if( ( bud->resource == NULL || bud->next == NULL ) )
+		else if( bud->resource == NULL || bud->next == NULL )
 			/* No need for selection if there's only one option. */
 			return bud;
@@ -521,5 +546,7 @@
 		   matches), removing it is simple. (And the hash reference
 		   should be removed too!) */
-		if( bud->next == NULL && ( ( s == NULL || bud->resource == NULL ) || g_strcasecmp( bud->resource, s + 1 ) == 0 ) )
+		if( bud->next == NULL &&
+		    ( ( s == NULL && bud->resource == NULL ) ||
+		      ( bud->resource && s && strcmp( bud->resource, s + 1 ) == 0 ) ) )
 		{
 			g_hash_table_remove( jd->buddies, bud->bare_jid );
@@ -544,5 +571,5 @@
 		{
 			for( bi = bud, prev = NULL; bi; bi = (prev=bi)->next )
-				if( g_strcasecmp( bi->resource, s + 1 ) == 0 )
+				if( strcmp( bi->resource, s + 1 ) == 0 )
 					break;
 			
Index: protocols/jabber/message.c
===================================================================
--- protocols/jabber/message.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/jabber/message.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -49,4 +49,21 @@
 	{
 		GString *fullmsg = g_string_new( "" );
+
+		for( c = node->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
+		{
+			char *ns = xt_find_attr( c, "xmlns" ), *room;
+			struct xt_node *inv, *reason;
+			
+			if( strcmp( ns, XMLNS_MUC_USER ) == 0 &&
+			    ( inv = xt_find_node( c->children, "invite" ) ) )
+			{
+				room = from;
+				from = xt_find_attr( inv, "from" ) ? : from;
+
+				g_string_append_printf( fullmsg, "<< \002BitlBee\002 - Invitation to chatroom %s >>\n", room );
+				if( ( reason = xt_find_node( inv->children, "reason" ) ) && reason->text_len > 0 )
+					g_string_append( fullmsg, reason->text );
+			}
+		}
 		
 		if( ( s = strchr( from, '/' ) ) )
Index: otocols/jabber/xmltree.c
===================================================================
--- protocols/jabber/xmltree.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ 	(revision )
@@ -1,589 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
-*                                                                           *
-*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library 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        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#include <glib.h>
-#include <string.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <stdio.h>
-
-#include "xmltree.h"
-
-static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
-{
-	struct xt_parser *xt = data;
-	struct xt_node *node = g_new0( struct xt_node, 1 ), *nt;
-	int i;
-	
-	node->parent = xt->cur;
-	node->name = g_strdup( element_name );
-	
-	/* First count the number of attributes */
-	for( i = 0; attr_names[i]; i ++ );
-	
-	/* Then allocate a NULL-terminated array. */
-	node->attr = g_new0( struct xt_attr, i + 1 );
-	
-	/* And fill it, saving one variable by starting at the end. */
-	for( i --; i >= 0; i -- )
-	{
-		node->attr[i].key = g_strdup( attr_names[i] );
-		node->attr[i].value = g_strdup( attr_values[i] );
-	}
-	
-	/* Add it to the linked list of children nodes, if we have a current
-	   node yet. */
-	if( xt->cur )
-	{
-		if( xt->cur->children )
-		{
-			for( nt = xt->cur->children; nt->next; nt = nt->next );
-			nt->next = node;
-		}
-		else
-		{
-			xt->cur->children = node;
-		}
-	}
-	else if( xt->root )
-	{
-		/* ERROR situation: A second root-element??? */
-	}
-	
-	/* Now this node will be the new current node. */
-	xt->cur = node;
-	/* And maybe this is the root? */
-	if( xt->root == NULL )
-		xt->root = node;
-}
-
-static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
-{
-	struct xt_parser *xt = data;
-	struct xt_node *node = xt->cur;
-	
-	if( node == NULL )
-		return;
-	
-	/* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */
-	node->text = g_renew( char, node->text, node->text_len + text_len + 1 );
-	memcpy( node->text + node->text_len, text, text_len );
-	node->text_len += text_len;
-	/* Zero termination is always nice to have. */
-	node->text[node->text_len] = 0;
-}
-
-static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
-{
-	struct xt_parser *xt = data;
-	
-	xt->cur->flags |= XT_COMPLETE;
-	xt->cur = xt->cur->parent;
-}
-
-GMarkupParser xt_parser_funcs =
-{
-	xt_start_element,
-	xt_end_element,
-	xt_text,
-	NULL,
-	NULL
-};
-
-struct xt_parser *xt_new( gpointer data )
-{
-	struct xt_parser *xt = g_new0( struct xt_parser, 1 );
-	
-	xt->data = data;
-	xt_reset( xt );
-	
-	return xt;
-}
-
-/* Reset the parser, flush everything we have so far. For example, we need
-   this for XMPP when doing TLS/SASL to restart the stream. */
-void xt_reset( struct xt_parser *xt )
-{
-	if( xt->parser )
-		g_markup_parse_context_free( xt->parser );
-	
-	xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL );
-	
-	if( xt->root )
-	{
-		xt_free_node( xt->root );
-		xt->root = NULL;
-		xt->cur = NULL;
-	}
-}
-
-/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
-   end-of-stream and 1 otherwise. */
-int xt_feed( struct xt_parser *xt, char *text, int text_len )
-{
-	if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
-	{
-		return -1;
-	}
-	
-	return !( xt->root && xt->root->flags & XT_COMPLETE );
-}
-
-/* Find completed nodes and see if a handler has to be called. Passing
-   a node isn't necessary if you want to start at the root, just pass
-   NULL. This second argument is needed for recursive calls. */
-int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
-{
-	struct xt_node *c;
-	xt_status st;
-	int i;
-	
-	/* Just in case someone likes infinite loops... */
-	if( xt->root == NULL )
-		return 0;
-	
-	if( node == NULL )
-		return xt_handle( xt, xt->root, depth );
-	
-	if( depth != 0 )
-		for( c = node->children; c; c = c->next )
-			if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) )
-				return 0;
-	
-	if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
-	{
-		for( i = 0; xt->handlers[i].func; i ++ )
-		{
-			/* This one is fun! \o/ */
-			
-						/* If handler.name == NULL it means it should always match. */
-			if( ( xt->handlers[i].name == NULL || 
-						/* If it's not, compare. There should always be a name. */
-			      g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
-						/* If handler.parent == NULL, it's a match. */
-			    ( xt->handlers[i].parent == NULL ||
-						/* If there's a parent node, see if the name matches. */
-			      ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
-						/* If there's no parent, the handler should mention <root> as a parent. */
-			                       g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
-			{
-				st = xt->handlers[i].func( node, xt->data );
-				
-				if( st == XT_ABORT )
-					return 0;
-				else if( st != XT_NEXT )
-					break;
-			}
-		}
-		
-		node->flags |= XT_SEEN;
-	}
-	
-	return 1;
-}
-
-/* Garbage collection: Cleans up all nodes that are handled. Useful for
-   streams because there's no reason to keep a complete packet history
-   in memory. */
-void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
-{
-	struct xt_node *c, *prev;
-	
-	if( !xt || !xt->root )
-		return;
-	
-	if( node == NULL )
-		return xt_cleanup( xt, xt->root, depth );
-	
-	if( node->flags & XT_SEEN && node == xt->root )
-	{
-		xt_free_node( xt->root );
-		xt->root = xt->cur = NULL;
-		/* xt->cur should be NULL already, BTW... */
-		
-		return;
-	}
-	
-	/* c contains the current node, prev the previous node (or NULL).
-	   I admit, this one's pretty horrible. */
-	for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
-	{
-		if( c->flags & XT_SEEN )
-		{
-			/* Remove the node from the linked list. */
-			if( prev )
-				prev->next = c->next;
-			else
-				node->children = c->next;
-			
-			xt_free_node( c );
-			
-			/* Since the for loop wants to get c->next, make sure
-			   c points at something that exists (and that c->next
-			   will actually be the next item we should check). c
-			   can be NULL now, if we just removed the first item.
-			   That explains the ? thing in for(). */
-			c = prev;
-		}
-		else
-		{
-			/* This node can't be cleaned up yet, but maybe a
-			   subnode can. */
-			if( depth != 0 )
-				xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
-		}
-	}
-}
-
-static void xt_to_string_real( struct xt_node *node, GString *str )
-{
-	char *buf;
-	struct xt_node *c;
-	int i;
-	
-	g_string_append_printf( str, "<%s", node->name );
-	
-	for( i = 0; node->attr[i].key; i ++ )
-	{
-		buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
-		g_string_append( str, buf );
-		g_free( buf );
-	}
-	
-	if( node->text == NULL && node->children == NULL )
-	{
-		g_string_append( str, "/>" );
-		return;
-	}
-	
-	g_string_append( str, ">" );
-	if( node->text_len > 0 )
-	{
-		buf = g_markup_escape_text( node->text, node->text_len );
-		g_string_append( str, buf );
-		g_free( buf );
-	}
-	
-	for( c = node->children; c; c = c->next )
-		xt_to_string_real( c, str );
-	
-	g_string_append_printf( str, "</%s>", node->name );
-}
-
-char *xt_to_string( struct xt_node *node )
-{
-	GString *ret;
-	char *real;
-	
-	ret = g_string_new( "" );
-	xt_to_string_real( node, ret );
-	
-	real = ret->str;
-	g_string_free( ret, FALSE );
-	
-	return real;
-}
-
-#ifdef DEBUG
-void xt_print( struct xt_node *node )
-{
-	int i;
-	struct xt_node *c;
-	
-	/* Indentation */
-	for( c = node; c->parent; c = c->parent )
-		printf( "\t" );
-	
-	/* Start the tag */
-	printf( "<%s", node->name );
-	
-	/* Print the attributes */
-	for( i = 0; node->attr[i].key; i ++ )
-		printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) );
-	
-	/* /> 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 )
-	{
-		printf( "/>\n" );
-		return;
-		/* Then we're finished! */
-	}
-	
-	/* Otherwise... */
-	printf( ">" );
-	
-	/* 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] )
-			printf( "%s", g_markup_escape_text( node->text, -1 ) );
-	}
-	
-	if( node->children )
-		printf( "\n" );
-	
-	for( c = node->children; c; c = c->next )
-		xt_print( c );
-	
-	if( node->children )
-		for( c = node; c->parent; c = c->parent )
-			printf( "\t" );
-	
-	/* Non-empty tag is now finished. */
-	printf( "</%s>\n", node->name );
-}
-#endif
-
-struct xt_node *xt_dup( struct xt_node *node )
-{
-	struct xt_node *dup = g_new0( struct xt_node, 1 );
-	struct xt_node *c, *dc = NULL;
-	int i;
-	
-	/* Let's NOT copy the parent element here BTW! Only do it for children. */
-	
-	dup->name = g_strdup( node->name );
-	dup->flags = node->flags;
-	if( node->text )
-	{
-		dup->text = g_memdup( node->text, node->text_len + 1 );
-		dup->text_len = node->text_len;
-	}
-	
-	/* Count the number of attributes and allocate the new array. */
-	for( i = 0; node->attr[i].key; i ++ );
-	dup->attr = g_new0( struct xt_attr, i + 1 );
-	
-	/* Copy them all! */
-	for( i --; i >= 0; i -- )
-	{
-		dup->attr[i].key = g_strdup( node->attr[i].key );
-		dup->attr[i].value = g_strdup( node->attr[i].value );
-	}
-	
-	/* This nice mysterious loop takes care of the children. */
-	for( c = node->children; c; c = c->next )
-	{
-		if( dc == NULL )
-			dc = dup->children = xt_dup( c );
-		else
-			dc = ( dc->next = xt_dup( c ) );
-		
-		dc->parent = dup;
-	}
-	
-	return dup;
-}
-
-/* Frees a node. This doesn't clean up references to itself from parents! */
-void xt_free_node( struct xt_node *node )
-{
-	int i;
-	
-	if( !node )
-		return;
-	
-	g_free( node->name );
-	g_free( node->text );
-	
-	for( i = 0; node->attr[i].key; i ++ )
-	{
-		g_free( node->attr[i].key );
-		g_free( node->attr[i].value );
-	}
-	g_free( node->attr );
-	
-	while( node->children )
-	{
-		struct xt_node *next = node->children->next;
-		
-		xt_free_node( node->children );
-		node->children = next;
-	}
-	
-	g_free( node );
-}
-
-void xt_free( struct xt_parser *xt )
-{
-	if( !xt )
-		return;
-	
-	if( xt->root )
-		xt_free_node( xt->root );
-	
-	g_markup_parse_context_free( xt->parser );
-	
-	g_free( xt );
-}
-
-/* To find a node's child with a specific name, pass the node's children
-   list, not the node itself! The reason you have to do this by hand: So
-   that you can also use this function as a find-next. */
-struct xt_node *xt_find_node( struct xt_node *node, const char *name )
-{
-	while( node )
-	{
-		if( g_strcasecmp( node->name, name ) == 0 )
-			break;
-		
-		node = node->next;
-	}
-	
-	return node;
-}
-
-char *xt_find_attr( struct xt_node *node, const char *key )
-{
-	int i;
-	
-	if( !node )
-		return NULL;
-	
-	for( i = 0; node->attr[i].key; i ++ )
-		if( g_strcasecmp( node->attr[i].key, key ) == 0 )
-			break;
-	
-	return node->attr[i].value;
-}
-
-struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children )
-{
-	struct xt_node *node, *c;
-	
-	node = g_new0( struct xt_node, 1 );
-	node->name = g_strdup( name );
-	node->children = children;
-	node->attr = g_new0( struct xt_attr, 1 );
-	
-	if( text )
-	{
-		node->text_len = strlen( text );
-		node->text = g_memdup( text, node->text_len + 1 );
-	}
-	
-	for( c = children; c; c = c->next )
-	{
-		if( c->parent != NULL )
-		{
-			/* ERROR CONDITION: They seem to have a parent already??? */
-		}
-		
-		c->parent = node;
-	}
-	
-	return node;
-}
-
-void xt_add_child( struct xt_node *parent, struct xt_node *child )
-{
-	struct xt_node *node;
-	
-	/* This function can actually be used to add more than one child, so
-	   do handle this properly. */
-	for( node = child; node; node = node->next )
-	{
-		if( node->parent != NULL )
-		{
-			/* ERROR CONDITION: They seem to have a parent already??? */
-		}
-		
-		node->parent = parent;
-	}
-	
-	if( parent->children == NULL )
-	{
-		parent->children = child;
-	}
-	else
-	{
-		for( node = parent->children; node->next; node = node->next );
-		node->next = child;
-	}
-}
-
-void xt_add_attr( struct xt_node *node, const char *key, const char *value )
-{
-	int i;
-	
-	/* Now actually it'd be nice if we can also change existing attributes
-	   (which actually means this function doesn't have the right name).
-	   So let's find out if we have this attribute already... */
-	for( i = 0; node->attr[i].key; i ++ )
-		if( strcmp( node->attr[i].key, key ) == 0 )
-			break;
-	
-	if( node->attr[i].key == NULL )
-	{
-		/* If not, allocate space for a new attribute. */
-		node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
-		node->attr[i].key = g_strdup( key );
-		node->attr[i+1].key = NULL;
-	}
-	else
-	{
-		/* Otherwise, free the old value before setting the new one. */
-		g_free( node->attr[i].value );
-	}
-	
-	node->attr[i].value = g_strdup( value );
-}
-
-int xt_remove_attr( struct xt_node *node, const char *key )
-{
-	int i, last;
-	
-	for( i = 0; node->attr[i].key; i ++ )
-		if( strcmp( node->attr[i].key, key ) == 0 )
-			break;
-	
-	/* If we didn't find the attribute... */
-	if( node->attr[i].key == NULL )
-		return 0;
-	
-	g_free( node->attr[i].key );
-	g_free( node->attr[i].value );
-	
-	/* If it's the last, this is easy: */
-	if( node->attr[i+1].key == NULL )
-	{
-		node->attr[i].key = node->attr[i].value = NULL;
-	}
-	else /* It's also pretty easy, actually. */
-	{
-		/* Find the last item. */
-		for( last = i + 1; node->attr[last+1].key; last ++ );
-		
-		node->attr[i] = node->attr[last];
-		node->attr[last].key = NULL;
-		node->attr[last].value = NULL;
-	}
-	
-	/* Let's not bother with reallocating memory here. It takes time and
-	   most packets don't stay in memory for long anyway. */
-	
-	return 1;
-}
Index: otocols/jabber/xmltree.h
===================================================================
--- protocols/jabber/xmltree.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ 	(revision )
@@ -1,97 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
-*                                                                           *
-*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library 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        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#ifndef _XMLTREE_H
-#define _XMLTREE_H
-
-typedef enum
-{
-	XT_COMPLETE	= 1,	/* </tag> reached */
-	XT_SEEN		= 2,	/* Handler called (or not defined) */
-} xt_flags;
-
-typedef enum
-{
-	XT_ABORT,		/* Abort, don't handle the rest anymore */
-	XT_HANDLED,		/* Handled this tag properly, go to the next one */
-	XT_NEXT			/* Try if there's another matching handler */
-} xt_status;
-
-struct xt_attr
-{
-	char *key, *value;
-};
-
-struct xt_node
-{
-	struct xt_node *parent;
-	struct xt_node *children;
-	
-	char *name;
-	struct xt_attr *attr;
-	char *text;
-	int text_len;
-	
-	struct xt_node *next;
-	xt_flags flags;
-};
-
-typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data );
-
-struct xt_handler_entry
-{
-	char *name, *parent;
-	xt_handler_func func;
-};
-
-struct xt_parser
-{
-	GMarkupParseContext *parser;
-	struct xt_node *root;
-	struct xt_node *cur;
-	
-	struct xt_handler_entry *handlers;
-	gpointer data;
-	
-	GError *gerr;
-};
-
-struct xt_parser *xt_new( gpointer data );
-void xt_reset( struct xt_parser *xt );
-int xt_feed( struct xt_parser *xt, char *text, int text_len );
-int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth );
-void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth );
-char *xt_to_string( struct xt_node *node );
-void xt_print( struct xt_node *node );
-struct xt_node *xt_dup( struct xt_node *node );
-void xt_free_node( struct xt_node *node );
-void xt_free( struct xt_parser *xt );
-struct xt_node *xt_find_node( struct xt_node *node, const char *name );
-char *xt_find_attr( struct xt_node *node, const char *key );
-
-struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children );
-void xt_add_child( struct xt_node *parent, struct xt_node *child );
-void xt_add_attr( struct xt_node *node, const char *key, const char *value );
-int xt_remove_attr( struct xt_node *node, const char *key );
-
-#endif
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/msn/msn.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -29,5 +29,5 @@
 #define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r"
 
-#ifdef DEBUG
+#ifdef DEBUG_MSN
 #define debug( text... ) imcb_log( ic, text );
 #else
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/msn/msn_util.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -90,6 +90,8 @@
 };
 
-static void msn_buddy_ask_yes( gpointer w, struct msn_buddy_ask_data *bla )
-{
+static void msn_buddy_ask_yes( void *data )
+{
+	struct msn_buddy_ask_data *bla = data;
+	
 	msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname );
 	
@@ -102,6 +104,8 @@
 }
 
-static void msn_buddy_ask_no( gpointer w, struct msn_buddy_ask_data *bla )
-{
+static void msn_buddy_ask_no( void *data )
+{
+	struct msn_buddy_ask_data *bla = data;
+	
 	msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname );
 	
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/msn/ns.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -34,5 +34,5 @@
 static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
 
-static void msn_auth_got_passport_id( struct passport_reply *rep );
+static void msn_auth_got_passport_token( struct msn_auth_data *mad );
 
 gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )
@@ -178,5 +178,13 @@
 			
 			debug( "Connecting to a new switchboard with key %s", cmd[5] );
-			sb = msn_sb_create( ic, server, port, cmd[5], MSN_SB_NEW );
+
+			if( ( sb = msn_sb_create( ic, server, port, cmd[5], MSN_SB_NEW ) ) == NULL )
+			{
+				/* Although this isn't strictly fatal for the NS connection, it's
+				   definitely something serious (we ran out of file descriptors?). */
+				imcb_error( ic, "Could not create new switchboard" );
+				imc_logout( ic, TRUE );
+				return( 0 );
+			}
 			
 			if( md->msgq )
@@ -214,5 +222,5 @@
 		{
 			/* Time for some Passport black magic... */
-			if( !passport_get_id( msn_auth_got_passport_id, ic, ic->acc->user, ic->acc->pass, cmd[4] ) )
+			if( !passport_get_token( msn_auth_got_passport_token, ic, ic->acc->user, ic->acc->pass, cmd[4] ) )
 			{
 				imcb_error( ic, "Error while contacting Passport server" );
@@ -270,9 +278,23 @@
 		if( num_parts == 5 )
 		{
+			int i, groupcount;
+			
+			groupcount = atoi( cmd[4] );
+			if( groupcount > 0 )
+			{
+				/* valgrind says this is leaking memory, I'm guessing
+				   that this happens during server redirects. */
+				if( md->grouplist )
+				{
+					for( i = 0; i < md->groupcount; i ++ )
+						g_free( md->grouplist[i] );
+					g_free( md->grouplist );
+				}
+				
+				md->groupcount = groupcount;
+				md->grouplist = g_new0( char *, md->groupcount );
+			}
+			
 			md->buddycount = atoi( cmd[3] );
-			md->groupcount = atoi( cmd[4] );
-			if( md->groupcount > 0 )
-				md->grouplist = g_new0( char *, md->groupcount );
-			
 			if( !*cmd[3] || md->buddycount == 0 )
 				msn_logged_in( ic );
@@ -468,6 +490,16 @@
 		debug( "Got a call from %s (session %d). Key = %s", cmd[5], session, cmd[4] );
 		
-		sb = msn_sb_create( ic, server, port, cmd[4], session );
-		sb->who = g_strdup( cmd[5] );
+		if( ( sb = msn_sb_create( ic, server, port, cmd[4], session ) ) == NULL )
+		{
+			/* Although this isn't strictly fatal for the NS connection, it's
+			   definitely something serious (we ran out of file descriptors?). */
+			imcb_error( ic, "Could not create new switchboard" );
+			imc_logout( ic, TRUE );
+			return( 0 );
+		}
+		else
+		{
+			sb->who = g_strdup( cmd[5] );
+		}
 	}
 	else if( strcmp( cmd[0], "ADD" ) == 0 )
@@ -647,4 +679,7 @@
 					imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
 				}
+				
+				g_free( inbox );
+				g_free( folders );
 			}
 			else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 )
@@ -674,21 +709,25 @@
 }
 
-static void msn_auth_got_passport_id( struct passport_reply *rep )
+static void msn_auth_got_passport_token( struct msn_auth_data *mad )
 {
-	struct im_connection *ic = rep->data;
-	struct msn_data *md = ic->proto_data;
-	char *key = rep->result;
-	char buf[1024];
-	
-	if( key == NULL )
-	{
-		imcb_error( ic, "Error during Passport authentication (%s)",
-		               rep->error_string ? rep->error_string : "Unknown error" );
+	struct im_connection *ic = mad->data;
+	struct msn_data *md;
+	
+	/* Dead connection? */
+	if( g_slist_find( msn_connections, ic ) == NULL )
+		return;
+	
+	md = ic->proto_data;
+	if( mad->token )
+	{
+		char buf[1024];
+		
+		g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token );
+		msn_write( ic, buf, strlen( buf ) );
+	}
+	else
+	{
+		imcb_error( ic, "Error during Passport authentication: %s", mad->error );
 		imc_logout( ic, TRUE );
 	}
-	else
-	{
-		g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, key );
-		msn_write( ic, buf, strlen( buf ) );
-	}
 }
Index: protocols/msn/passport.c
===================================================================
--- protocols/msn/passport.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/msn/passport.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -1,7 +1,6 @@
-/* passport.c
+/** passport.c
  *
- * Functions to login to microsoft passport service for Messenger
- * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>
- * Copyright (C) 2004 Wilmer van der Gaast <wilmer@gaast.net>
+ * Functions to login to Microsoft Passport service for Messenger
+ * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net>
  *
  * This program is free software; you can redistribute it and/or modify             
@@ -24,207 +23,148 @@
 #include "msn.h"
 #include "bitlbee.h"
+#include "url.h"
+#include "misc.h"
+#include "xmltree.h"
 #include <ctype.h>
 #include <errno.h>
 
-#define MSN_BUF_LEN 8192
+static int passport_get_token_real( struct msn_auth_data *mad );
+static void passport_get_token_ready( struct http_request *req );
 
-static char *prd_cached = NULL;
-
-static int passport_get_id_real( gpointer func, gpointer data, char *header );
-static void passport_get_id_ready( struct http_request *req );
-
-static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header );
-static void passport_retrieve_dalogin_ready( struct http_request *req );
-
-static char *passport_create_header( char *cookie, char *email, char *pwd );
-static void destroy_reply( struct passport_reply *rep );
-
-int passport_get_id( gpointer func, gpointer data, char *username, char *password, char *cookie )
+int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie )
 {
-	char *header = passport_create_header( cookie, username, password );
+	struct msn_auth_data *mad = g_new0( struct msn_auth_data, 1 );
+	int i;
 	
-	if( prd_cached == NULL )
-		return passport_retrieve_dalogin( func, data, header );
-	else
-		return passport_get_id_real( func, data, header );
+	mad->username = g_strdup( username );
+	mad->password = g_strdup( password );
+	mad->cookie = g_strdup( cookie );
+	
+	mad->callback = func;
+	mad->data = data;
+	
+	mad->url = g_strdup( SOAP_AUTHENTICATION_URL );
+	mad->ttl = 3; /* Max. # of redirects. */
+	
+	/* HTTP-escape stuff and s/,/&/ */
+	http_decode( mad->cookie );
+	for( i = 0; mad->cookie[i]; i ++ )
+		if( mad->cookie[i] == ',' )
+			mad->cookie[i] = '&';
+	
+	/* Microsoft doesn't allow password longer than 16 chars and silently
+	   fails authentication if you give the "full version" of your passwd. */
+	if( strlen( mad->password ) > MAX_PASSPORT_PWLEN )
+		mad->password[MAX_PASSPORT_PWLEN] = 0;
+	
+	return passport_get_token_real( mad );
 }
 
-static int passport_get_id_real( gpointer func, gpointer data, char *header )
+static int passport_get_token_real( struct msn_auth_data *mad )
 {
-	struct passport_reply *rep;
-	char *server, *dummy, *reqs;
+	char *post_payload, *post_request;
 	struct http_request *req;
+	url_t url;
 	
-	rep = g_new0( struct passport_reply, 1 );
-	rep->data = data;
-	rep->func = func;
-	rep->header = header;
+	url_set( &url, mad->url );
 	
-	server = g_strdup( prd_cached );
-	dummy = strchr( server, '/' );
+	post_payload = g_markup_printf_escaped( SOAP_AUTHENTICATION_PAYLOAD,
+	                                        mad->username,
+	                                        mad->password,
+	                                        mad->cookie );
 	
-	if( dummy == NULL )
-	{
-		destroy_reply( rep );
-		return( 0 );
-	}
+	post_request = g_strdup_printf( SOAP_AUTHENTICATION_REQUEST,
+	                                url.file, url.host,
+	                                (int) strlen( post_payload ),
+	                                post_payload );
+	                                
+	req = http_dorequest( url.host, url.port, 1, post_request,
+	                      passport_get_token_ready, mad );
 	
-	reqs = g_strdup_printf( "GET %s HTTP/1.0\r\n%s\r\n\r\n", dummy, header );
+	g_free( post_request );
+	g_free( post_payload );
 	
-	*dummy = 0;
-	req = http_dorequest( server, 443, 1, reqs, passport_get_id_ready, rep );
-	
-	g_free( server );
-	g_free( reqs );
-	
-	if( req == NULL )
-		destroy_reply( rep );
-	
-	return( req != NULL );
+	return req != NULL;
 }
 
-static void passport_get_id_ready( struct http_request *req )
+static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data );
+static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data );
+
+static const struct xt_handler_entry passport_xt_handlers[] = {
+	{ "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", passport_xt_extract_token },
+	{ "S:Fault",                  "S:Envelope",                 passport_xt_handle_fault  },
+	{ NULL,                       NULL,                         NULL                      }
+};
+
+static void passport_get_token_ready( struct http_request *req )
 {
-	struct passport_reply *rep = req->data;
+	struct msn_auth_data *mad = req->data;
+	struct xt_parser *parser;
 	
-	if( !g_slist_find( msn_connections, rep->data ) )
+	g_free( mad->url );
+	g_free( mad->error );
+	mad->url = mad->error = NULL;
+	
+	if( req->status_code == 200 )
 	{
-		destroy_reply( rep );
-		return;
-	}
-	
-	if( req->finished && req->reply_headers && req->status_code == 200 )
-	{
-		char *dummy;
-		
-		if( ( dummy = strstr( req->reply_headers, "from-PP='" ) ) )
-		{
-			char *responseend;
-			
-			dummy += strlen( "from-PP='" );
-			responseend = strchr( dummy, '\'' );
-			if( responseend )
-				*responseend = 0;
-			
-			rep->result = g_strdup( dummy );
-		}
-		else
-		{
-			rep->error_string = g_strdup( "Could not parse Passport server response" );
-		}
+		parser = xt_new( passport_xt_handlers, mad );
+		xt_feed( parser, req->reply_body, req->body_size );
+		xt_handle( parser, NULL, -1 );
+		xt_free( parser );
 	}
 	else
 	{
-		rep->error_string = g_strdup_printf( "HTTP error: %s",
-		                      req->status_string ? req->status_string : "Unknown error" );
+		mad->error = g_strdup_printf( "HTTP error %d (%s)", req->status_code,
+		                              req->status_string ? req->status_string : "unknown" );
 	}
 	
-	rep->func( rep );
-	destroy_reply( rep );
+	if( mad->error == NULL && mad->token == NULL )
+		mad->error = g_strdup( "Could not parse Passport server response" );
+	
+	if( mad->url && mad->token == NULL )
+	{
+		passport_get_token_real( mad );
+	}
+	else
+	{
+		mad->callback( mad );
+		
+		g_free( mad->url );
+		g_free( mad->username );
+		g_free( mad->password );
+		g_free( mad->cookie );
+		g_free( mad->token );
+		g_free( mad->error );
+		g_free( mad );
+	}
 }
 
-static char *passport_create_header( char *cookie, char *email, char *pwd )
+static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data )
 {
-	char *buffer;
-	char *currenttoken;
-	char *email_enc, *pwd_enc;
+	struct msn_auth_data *mad = data;
+	char *s;
 	
-	currenttoken = strstr( cookie, "lc=" );
-	if( currenttoken == NULL )
-		return NULL;
+	if( ( s = xt_find_attr( node, "Id" ) ) && strcmp( s, "PPToken1" ) == 0 )
+		mad->token = g_memdup( node->text, node->text_len + 1 );
 	
-	email_enc = g_new0( char, strlen( email ) * 3 + 1 );
-	strcpy( email_enc, email );
-	http_encode( email_enc );
-	
-	pwd_enc = g_new0( char, strlen( pwd ) * 3 + 1 );
-	strcpy( pwd_enc, pwd );
-	http_encode( pwd_enc );
-	
-	buffer = g_strdup_printf( "Authorization: Passport1.4 OrgVerb=GET,"
-	                          "OrgURL=http%%3A%%2F%%2Fmessenger%%2Emsn%%2Ecom,"
-	                          "sign-in=%s,pwd=%s,%s", email_enc, pwd_enc,
-	                          currenttoken );
-	
-	g_free( email_enc );
-	g_free( pwd_enc );
-	
-	return buffer;
+	return XT_HANDLED;
 }
 
-static int passport_retrieve_dalogin( gpointer func, gpointer data, char *header )
+static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data )
 {
-	struct passport_reply *rep = g_new0( struct passport_reply, 1 );
-	struct http_request *req;
+	struct msn_auth_data *mad = data;
+	struct xt_node *code = xt_find_node( node->children, "faultcode" );
+	struct xt_node *string = xt_find_node( node->children, "faultstring" );
+	struct xt_node *redirect = xt_find_node( node->children, "psf:redirectUrl" );
 	
-	rep->data = data;
-	rep->func = func;
-	rep->header = header;
+	if( redirect && redirect->text_len && mad->ttl-- > 0 )
+		mad->url = g_memdup( redirect->text, redirect->text_len + 1 );
 	
-	req = http_dorequest_url( "https://nexus.passport.com/rdr/pprdr.asp", passport_retrieve_dalogin_ready, rep );
+	if( code == NULL || code->text_len == 0 )
+		mad->error = g_strdup( "Unknown error" );
+	else
+		mad->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ?
+		                              string->text : "no description available" );
 	
-	if( !req )
-		destroy_reply( rep );
-	
-	return( req != NULL );
+	return XT_HANDLED;
 }
-
-static void passport_retrieve_dalogin_ready( struct http_request *req )
-{
-	struct passport_reply *rep = req->data;
-	char *dalogin;
-	char *urlend;
-	
-	if( !g_slist_find( msn_connections, rep->data ) )
-	{
-		destroy_reply( rep );
-		return;
-	}
-	
-	if( !req->finished || !req->reply_headers || req->status_code != 200 )
-	{
-		rep->error_string = g_strdup_printf( "HTTP error while fetching DALogin: %s",
-		                        req->status_string ? req->status_string : "Unknown error" );
-		goto failure;
-	}
-	
-	dalogin = strstr( req->reply_headers, "DALogin=" );	
-	
-	if( !dalogin )
-	{
-		rep->error_string = g_strdup( "Parse error while fetching DALogin" );
-		goto failure;
-	}
-	
-	dalogin += strlen( "DALogin=" );
-	urlend = strchr( dalogin, ',' );
-	if( urlend )
-		*urlend = 0;
-	
-	/* strip the http(s):// part from the url */
-	urlend = strstr( urlend, "://" );
-	if( urlend )
-		dalogin = urlend + strlen( "://" );
-	
-	if( prd_cached == NULL )
-		prd_cached = g_strdup( dalogin );
-	
-	if( passport_get_id_real( rep->func, rep->data, rep->header ) )
-	{
-		rep->header = NULL;
-		destroy_reply( rep );
-		return;
-	}
-	
-failure:	
-	rep->func( rep );
-	destroy_reply( rep );
-}
-
-static void destroy_reply( struct passport_reply *rep )
-{
-	g_free( rep->result );
-	g_free( rep->header );
-	g_free( rep->error_string );
-	g_free( rep );
-}
Index: protocols/msn/passport.h
===================================================================
--- protocols/msn/passport.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/msn/passport.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -1,9 +1,6 @@
-#ifndef __PASSPORT_H__
-#define __PASSPORT_H__
 /* passport.h
  *
- * Functions to login to Microsoft Passport Service for Messenger
- * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>,
- *                    Wilmer van der Gaast <wilmer@gaast.net>
+ * Functions to login to Microsoft Passport service for Messenger
+ * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net>
  *
  * This program is free software; you can redistribute it and/or modify             
@@ -18,6 +15,12 @@
  * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA          
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA          
  */
+
+/* Thanks to http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
+   for the specs! */
+
+#ifndef __PASSPORT_H__
+#define __PASSPORT_H__
 
 #include <stdio.h>
@@ -33,14 +36,78 @@
 #include "nogaim.h"
 
-struct passport_reply
+#define MAX_PASSPORT_PWLEN 16
+
+struct msn_auth_data
 {
-	void (*func)( struct passport_reply * );
-	void *data;
-	char *result;
-	char *header;
-	char *error_string;
+	char *url;
+	int ttl;
+	
+	char *username;
+	char *password;
+	char *cookie;
+	
+	/* The end result, the only thing we'll really be interested in
+	   once finished. */
+	char *token;
+	char *error; /* Yeah, or that... */
+	
+	void (*callback)( struct msn_auth_data *mad );
+	gpointer data;
 };
 
-int passport_get_id( gpointer func, gpointer data, char *username, char *password, char *cookie );
+#define SOAP_AUTHENTICATION_URL "https://loginnet.passport.com/RST.srf"
+
+#define SOAP_AUTHENTICATION_REQUEST \
+"POST %s HTTP/1.0\r\n" \
+"Accept: text/*\r\n" \
+"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \
+"Host: %s\r\n" \
+"Content-Length: %d\r\n" \
+"Cache-Control: no-cache\r\n" \
+"\r\n" \
+"%s"
+
+#define SOAP_AUTHENTICATION_PAYLOAD \
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \
+"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \
+  "<Header>" \
+    "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">" \
+      "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \
+      "<ps:BinaryVersion>4</ps:BinaryVersion>" \
+      "<ps:UIVersion>1</ps:UIVersion>" \
+      "<ps:Cookies></ps:Cookies>" \
+      "<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>" \
+    "</ps:AuthInfo>" \
+    "<wsse:Security>" \
+       "<wsse:UsernameToken Id=\"user\">" \
+         "<wsse:Username>%s</wsse:Username>" \
+         "<wsse:Password>%s</wsse:Password>" \
+       "</wsse:UsernameToken>" \
+    "</wsse:Security>" \
+  "</Header>" \
+  "<Body>" \
+    "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">" \
+      "<wst:RequestSecurityToken Id=\"RST0\">" \
+        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+        "<wsp:AppliesTo>" \
+          "<wsa:EndpointReference>" \
+            "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \
+          "</wsa:EndpointReference>" \
+        "</wsp:AppliesTo>" \
+      "</wst:RequestSecurityToken>" \
+      "<wst:RequestSecurityToken Id=\"RST1\">" \
+       "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+        "<wsp:AppliesTo>" \
+          "<wsa:EndpointReference>" \
+            "<wsa:Address>messenger.msn.com</wsa:Address>" \
+          "</wsa:EndpointReference>" \
+        "</wsp:AppliesTo>" \
+        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>" \
+      "</wst:RequestSecurityToken>" \
+    "</ps:RequestMultipleSecurityTokens>" \
+  "</Body>" \
+"</Envelope>"
+
+int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie );
 
 #endif /* __PASSPORT_H__ */
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/nogaim.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -343,5 +343,6 @@
 /* dialogs.c */
 
-void imcb_ask( struct im_connection *ic, char *msg, void *data, void *doit, void *dont )
+void imcb_ask( struct im_connection *ic, char *msg, void *data,
+               query_callback doit, query_callback dont )
 {
 	query_add( ic->irc, ic, msg, doit, dont, data );
@@ -495,16 +496,18 @@
 };
 
-void show_got_added_no( gpointer w, struct show_got_added_data *data )
-{
-	g_free( data->handle );
+void show_got_added_no( void *data )
+{
+	g_free( ((struct show_got_added_data*)data)->handle );
 	g_free( data );
 }
 
-void show_got_added_yes( gpointer w, struct show_got_added_data *data )
-{
-	data->ic->acc->prpl->add_buddy( data->ic, data->handle, NULL );
-	/* imcb_add_buddy( data->ic, NULL, data->handle, data->handle ); */
-	
-	return show_got_added_no( w, data );
+void show_got_added_yes( void *data )
+{
+	struct show_got_added_data *sga = data;
+	
+	sga->ic->acc->prpl->add_buddy( sga->ic, sga->handle, NULL );
+	/* imcb_add_buddy( sga->ic, NULL, sga->handle, sga->handle ); */
+	
+	return show_got_added_no( data );
 }
 
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/nogaim.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -42,10 +42,6 @@
 #include "account.h"
 #include "proxy.h"
+#include "query.h"
 #include "md5.h"
-
-#define BUF_LEN MSG_LEN
-#define BUF_LONG ( BUF_LEN * 2 )
-#define MSG_LEN 2048
-#define BUF_LEN MSG_LEN
 
 #define BUDDY_ALIAS_MAXLEN 388   /* because MSN names can be 387 characters */
@@ -265,5 +261,5 @@
  * - 'doit' or 'dont' will be called depending of the answer of the user.
  */
-G_MODULE_EXPORT void imcb_ask( struct im_connection *ic, char *msg, void *data, void *doit, void *dont );
+G_MODULE_EXPORT void imcb_ask( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont );
 G_MODULE_EXPORT void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname );
 
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/oscar/oscar.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -60,4 +60,7 @@
 
 #define OSCAR_GROUP "Friends"
+
+#define BUF_LEN 2048
+#define BUF_LONG ( BUF_LEN * 2 )
 
 /* Don't know if support for UTF8 is really working. For now it's UTF16 here.
@@ -240,4 +243,30 @@
 };
 static int msgerrreasonlen = 25;
+
+/* Hurray, this function is NOT thread-safe \o/ */
+static char *normalize(const char *s)
+{
+	static char buf[BUF_LEN];
+	char *t, *u;
+	int x = 0;
+
+	g_return_val_if_fail((s != NULL), NULL);
+
+	u = t = g_strdup(s);
+
+	strcpy(t, s);
+	g_strdown(t);
+
+	while (*t && (x < BUF_LEN - 1)) {
+		if (*t != ' ' && *t != '!') {
+			buf[x] = *t;
+			x++;
+		}
+		t++;
+	}
+	buf[x] = '\0';
+	g_free(u);
+	return buf;
+}
 
 static gboolean oscar_callback(gpointer data, gint source,
@@ -1002,11 +1031,11 @@
 	}
 
-	tmp = g_strdup(normalize(ic->acc->user));
-	if (!strcmp(tmp, normalize(info->sn)))
+	if (!aim_sncmp(ic->acc->user, info->sn))
 		g_snprintf(ic->displayname, sizeof(ic->displayname), "%s", info->sn);
-	g_free(tmp);
-
-	imcb_buddy_status(ic, info->sn, flags, state_string, NULL);
-	/* imcb_buddy_times(ic, info->sn, signon, time_idle); */
+
+	tmp = normalize(info->sn);
+	imcb_buddy_status(ic, tmp, flags, state_string, NULL);
+	/* imcb_buddy_times(ic, tmp, signon, time_idle); */
+
 
 	return 1;
@@ -1022,5 +1051,5 @@
 	va_end(ap);
 
-	imcb_buddy_status(ic, info->sn, 0, NULL, NULL );
+	imcb_buddy_status(ic, normalize(info->sn), 0, NULL, NULL );
 
 	return 1;
@@ -1078,5 +1107,5 @@
 	
 	strip_linefeed(tmp);
-	imcb_buddy_msg(ic, userinfo->sn, tmp, flags, 0);
+	imcb_buddy_msg(ic, normalize(userinfo->sn), tmp, flags, 0);
 	g_free(tmp);
 	
@@ -1084,6 +1113,6 @@
 }
 
-void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv);
-void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv);
+void oscar_accept_chat(void *data);
+void oscar_reject_chat(void *data);
 	
 static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
@@ -1119,5 +1148,6 @@
 }
 
-static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
+static void gaim_icq_authgrant(void *data_) {
+	struct icq_auth *data = data_;
 	char *uin, message;
 	struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
@@ -1134,5 +1164,6 @@
 }
 
-static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
+static void gaim_icq_authdeny(void *data_) {
+	struct icq_auth *data = data_;
 	char *uin, *message;
 	struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
@@ -1175,5 +1206,5 @@
 			message = g_strdup(args->msg);
 			strip_linefeed(message);
-			imcb_buddy_msg(ic, uin, message, 0, 0);
+			imcb_buddy_msg(ic, normalize(uin), message, 0, 0);
 			g_free(uin);
 			g_free(message);
@@ -1194,5 +1225,5 @@
 
 			strip_linefeed(message);
-			imcb_buddy_msg(ic, uin, message, 0, 0);
+			imcb_buddy_msg(ic, normalize(uin), message, 0, 0);
 			g_free(uin);
 			g_free(m);
@@ -1469,5 +1500,5 @@
 
 	for (i = 0; i < count; i++)
-		imcb_chat_add_buddy(c->cnv, info[i].sn);
+		imcb_chat_add_buddy(c->cnv, normalize(info[i].sn));
 
 	return 1;
@@ -1492,5 +1523,5 @@
 
 	for (i = 0; i < count; i++)
-		imcb_chat_remove_buddy(c->cnv, info[i].sn, NULL);
+		imcb_chat_remove_buddy(c->cnv, normalize(info[i].sn), NULL);
 
 	return 1;
@@ -1543,5 +1574,5 @@
 	tmp = g_malloc(BUF_LONG);
 	g_snprintf(tmp, BUF_LONG, "%s", msg);
-	imcb_chat_msg(ccon->cnv, info->sn, tmp, 0, 0);
+	imcb_chat_msg(ccon->cnv, normalize(info->sn), tmp, 0, 0);
 	g_free(tmp);
 
@@ -1756,5 +1787,5 @@
 			g_snprintf(sender, sizeof(sender), "%u", msg->sender);
 			strip_linefeed(dialog_msg);
-			imcb_buddy_msg(ic, sender, dialog_msg, 0, t);
+			imcb_buddy_msg(ic, normalize(sender), dialog_msg, 0, t);
 			g_free(dialog_msg);
 		} break;
@@ -1777,5 +1808,5 @@
 
 			strip_linefeed(dialog_msg);
-			imcb_buddy_msg(ic, sender, dialog_msg, 0, t);
+			imcb_buddy_msg(ic, normalize(sender), dialog_msg, 0, t);
 			g_free(dialog_msg);
 			g_free(m);
@@ -2015,11 +2046,14 @@
 	struct aim_ssi_item *curitem;
 	int tmp;
+	char *nrm;
 
 	/* Add from server list to local list */
 	tmp = 0;
 	for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
+		nrm = curitem->name ? normalize(curitem->name) : NULL;
+		
 		switch (curitem->type) {
 			case 0x0000: /* Buddy */
-				if ((curitem->name) && (!imcb_find_buddy(ic, curitem->name))) {
+				if ((curitem->name) && (!imcb_find_buddy(ic, nrm))) {
 					char *realname = NULL;
 
@@ -2027,9 +2061,9 @@
 						    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
 						
-					imcb_add_buddy(ic, curitem->name, NULL);
+					imcb_add_buddy(ic, nrm, NULL);
 					
 					if (realname) {
-						imcb_buddy_nick_hint(ic, curitem->name, realname);
-						imcb_rename_buddy(ic, curitem->name, realname);
+						imcb_buddy_nick_hint(ic, nrm, realname);
+						imcb_rename_buddy(ic, nrm, realname);
 						g_free(realname);
 					}
@@ -2043,5 +2077,5 @@
 					if (!list) {
 						char *name;
-						name = g_strdup(normalize(curitem->name));
+						name = g_strdup(nrm);
 						ic->permit = g_slist_append(ic->permit, name);
 						tmp++;
@@ -2056,5 +2090,5 @@
 					if (!list) {
 						char *name;
-						name = g_strdup(normalize(curitem->name));
+						name = g_strdup(nrm);
 						ic->deny = g_slist_append(ic->deny, name);
 						tmp++;
@@ -2118,5 +2152,5 @@
 			if( st == 0x00 )
 			{
-				imcb_add_buddy( sess->aux_data, list, NULL );
+				imcb_add_buddy( sess->aux_data, normalize(list), NULL );
 			}
 			else if( st == 0x0E )
@@ -2448,13 +2482,13 @@
 	if(type2 == 0x0002) {
 		/* User is typing */
-		imcb_buddy_typing(ic, sn, OPT_TYPING);
+		imcb_buddy_typing(ic, normalize(sn), OPT_TYPING);
 	} 
 	else if (type2 == 0x0001) {
 		/* User has typed something, but is not actively typing (stale) */
-		imcb_buddy_typing(ic, sn, OPT_THINKING);
+		imcb_buddy_typing(ic, normalize(sn), OPT_THINKING);
 	}
 	else {
 		/* User has stopped typing */
-		imcb_buddy_typing(ic, sn, 0);
+		imcb_buddy_typing(ic, normalize(sn), 0);
 	}
 	
@@ -2588,6 +2622,8 @@
 }
 
-void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv)
-{
+void oscar_accept_chat(void *data)
+{
+	struct aim_chat_invitation * inv = data;
+	
 	oscar_chat_join(inv->ic, inv->name, NULL, NULL);
 	g_free(inv->name);
@@ -2595,6 +2631,8 @@
 }
 
-void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv)
-{
+void oscar_reject_chat(void *data)
+{
+	struct aim_chat_invitation * inv = data;
+	
 	g_free(inv->name);
 	g_free(inv);
Index: protocols/yahoo/libyahoo2.c
===================================================================
--- protocols/yahoo/libyahoo2.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/yahoo/libyahoo2.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -69,6 +69,4 @@
 #ifdef __MINGW32__
 # include <winsock2.h>
-# define write(a,b,c) send(a,b,c,0)
-# define read(a,b,c)  recv(a,b,c,0)
 #endif
 
@@ -381,5 +379,4 @@
 
 /* call repeatedly to get the next one */
-/*
 static struct yahoo_input_data * find_input_by_id(int id)
 {
@@ -392,5 +389,4 @@
 	return NULL;
 }
-*/
 
 static struct yahoo_input_data * find_input_by_id_and_webcam_user(int id, const char * who)
@@ -737,5 +733,5 @@
 
 	memcpy(data + pos, "YMSG", 4); pos += 4;
-	pos += yahoo_put16(data + pos, 0x0a00);
+	pos += yahoo_put16(data + pos, 0x000c);
 	pos += yahoo_put16(data + pos, 0x0000);
 	pos += yahoo_put16(data + pos, pktlen + extra_pad);
@@ -797,4 +793,5 @@
 {
 	struct yahoo_data *yd = find_conn_by_id(id);
+	
 	if(!yd)
 		return;
@@ -3166,5 +3163,5 @@
 
 	LOG(("write callback: id=%d fd=%d data=%p", id, fd, data));
-	if(!yid || !yid->txqueues)
+	if(!yid || !yid->txqueues || !find_conn_by_id(id))
 		return -2;
 	
@@ -3351,5 +3348,5 @@
 					break;
 				case 5: 
-					if(cp != "\005")
+					if(strcmp(cp, "5") != 0)
 						yct->location = cp;
 					k = 0;
@@ -3842,9 +3839,7 @@
 	}
 
-	
-/*	do {
+	do {
 		yahoo_input_close(yid);
-	} while((yid = find_input_by_id(id)));*/
-	
+	} while((yid = find_input_by_id(id)));
 }
 
Index: protocols/yahoo/yahoo.c
===================================================================
--- protocols/yahoo/yahoo.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/yahoo/yahoo.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -163,8 +163,5 @@
 	g_slist_free( yd->buddygroups );
 	
-	if( yd->logged_in )
-		yahoo_logoff( yd->y2_id );
-	else
-		yahoo_close( yd->y2_id );
+	yahoo_logoff( yd->y2_id );
 	
 	g_free( yd );
@@ -315,5 +312,5 @@
 	struct byahoo_data *yd = (struct byahoo_data *) c->ic->proto_data;
 	
-	yahoo_conference_invite( yd->y2_id, NULL, c->data, c->title, msg );
+	yahoo_conference_invite( yd->y2_id, NULL, c->data, c->title, msg ? msg : "" );
 }
 
@@ -454,8 +451,4 @@
 {
 	struct byahoo_write_ready_data *d = data;
-	
-	if( !byahoo_get_ic_by_id( d->id ) )
-		/* WTF doesn't libyahoo clean this up? */
-		return FALSE;
 	
 	yahoo_write_ready( d->id, d->fd, d->data );
@@ -798,6 +791,8 @@
 }
 
-static void byahoo_accept_conf( gpointer w, struct byahoo_conf_invitation *inv )
-{
+static void byahoo_accept_conf( void *data )
+{
+	struct byahoo_conf_invitation *inv = data;
+	
 	yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name );
 	imcb_chat_add_buddy( inv->c, inv->ic->acc->user );
@@ -806,6 +801,8 @@
 }
 
-static void byahoo_reject_conf( gpointer w, struct byahoo_conf_invitation *inv )
-{
+static void byahoo_reject_conf( void *data )
+{
+	struct byahoo_conf_invitation *inv = data;
+	
 	yahoo_conference_decline( inv->yid, NULL, inv->members, inv->name, "User rejected groupchat" );
 	imcb_chat_free( inv->c );
Index: protocols/yahoo/yahoo_httplib.c
===================================================================
--- protocols/yahoo/yahoo_httplib.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ protocols/yahoo/yahoo_httplib.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -51,6 +51,4 @@
 #ifdef __MINGW32__
 # include <winsock2.h>
-# define write(a,b,c) send(a,b,c,0)
-# define read(a,b,c)  recv(a,b,c,0)
 # define snprintf _snprintf
 #endif
Index: query.c
===================================================================
--- query.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ query.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -30,5 +30,6 @@
 static query_t *query_default( irc_t *irc );
 
-query_t *query_add( irc_t *irc, struct im_connection *ic, char *question, void *yes, void *no, void *data )
+query_t *query_add( irc_t *irc, struct im_connection *ic, char *question,
+                    query_callback yes, query_callback no, void *data )
 {
 	query_t *q = g_new0( query_t, 1 );
@@ -143,19 +144,19 @@
 	if( ans )
 	{
-		if(q->ic)
+		if( q->ic )
 			imcb_log( q->ic, "Accepted: %s", q->question );
 		else
 			irc_usermsg( irc, "Accepted: %s", q->question );
-		if(q->yes)
-			q->yes( q->ic ? (gpointer)q->ic : (gpointer)irc, q->data );
+		if( q->yes )
+			q->yes( q->data );
 	}
 	else
 	{
-		if(q->ic)
+		if( q->ic )
 			imcb_log( q->ic, "Rejected: %s", q->question );
 		else
 			irc_usermsg( irc, "Rejected: %s", q->question );
-		if(q->no)
-			q->no( q->ic ? (gpointer)q->ic : (gpointer)irc, q->data );
+		if( q->no )
+			q->no( q->data );
 	}
 	q->data = NULL;
Index: query.h
===================================================================
--- query.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ query.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -27,15 +27,17 @@
 #define _QUERY_H
 
+typedef void (*query_callback) ( void *data );
+
 typedef struct query
 {
 	struct im_connection *ic;
 	char *question;
-	void (* yes) ( gpointer w, void *data );
-	void (* no) ( gpointer w, void *data );
+	query_callback yes, no;
 	void *data;
 	struct query *next;
 } query_t;
 
-query_t *query_add( irc_t *irc, struct im_connection *ic, char *question, void *yes, void *no, void *data );
+query_t *query_add( irc_t *irc, struct im_connection *ic, char *question,
+                    query_callback yes, query_callback no, void *data );
 void query_del( irc_t *irc, query_t *q );
 void query_del_by_conn( irc_t *irc, struct im_connection *ic );
Index: root_commands.c
===================================================================
--- root_commands.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ root_commands.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -205,4 +205,38 @@
 }
 
+struct cmd_account_del_data
+{
+	account_t *a;
+	irc_t *irc;
+};
+
+void cmd_account_del_yes( void *data )
+{
+	struct cmd_account_del_data *cad = data;
+	account_t *a;
+	
+	for( a = cad->irc->accounts; a && a != cad->a; a = a->next );
+	
+	if( a == NULL )
+	{
+		irc_usermsg( cad->irc, "Account already deleted" );
+	}
+	else if( a->ic )
+	{
+		irc_usermsg( cad->irc, "Account is still logged in, can't delete" );
+	}
+	else
+	{
+		account_del( cad->irc, a );
+		irc_usermsg( cad->irc, "Account deleted" );
+	}
+	g_free( data );
+}
+
+void cmd_account_del_no( void *data )
+{
+	g_free( data );
+}
+
 static void cmd_account( irc_t *irc, char **cmd )
 {
@@ -263,6 +297,18 @@
 		else
 		{
-			account_del( irc, a );
-			irc_usermsg( irc, "Account deleted" );
+			struct cmd_account_del_data *cad;
+			char *msg;
+			
+			cad = g_malloc( sizeof( struct cmd_account_del_data ) );
+			cad->a = a;
+			cad->irc = irc;
+			
+			msg = g_strdup_printf( "If you remove this account (%s(%s)), BitlBee will "
+			                       "also forget all your saved nicknames. If you want "
+			                       "to change your username/password, use the `account "
+			                       "set' command. Are you sure you want to delete this "
+			                       "account?", a->prpl->name, a->user );
+			query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, cad );
+			g_free( msg );
 		}
 	}
@@ -382,4 +428,10 @@
 			acc_handle = g_strdup( cmd[2] );
 		
+		if( !acc_handle )
+		{
+			irc_usermsg( irc, "Not enough parameters given (need %d)", 3 );
+			return;
+		}
+		
 		if( ( tmp = strchr( acc_handle, '/' ) ) )
 		{
@@ -562,4 +614,7 @@
 			g_free( irc->mynick );
 			irc->mynick = g_strdup( cmd[2] );
+			
+			if( strcmp( cmd[0], "set_rename" ) != 0 )
+				set_setstr( &irc->set, "root_nick", cmd[2] );
 		}
 		else if( u->send_handler == buddy_send_handler )
@@ -570,4 +625,18 @@
 		irc_usermsg( irc, "Nick successfully changed" );
 	}
+}
+
+char *set_eval_root_nick( set_t *set, char *new_nick )
+{
+	irc_t *irc = set->data;
+	
+	if( strcmp( irc->mynick, new_nick ) != 0 )
+	{
+		char *cmd[] = { "set_rename", irc->mynick, new_nick, NULL };
+		
+		cmd_rename( irc, cmd );
+	}
+	
+	return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : NULL;
 }
 
@@ -774,4 +843,7 @@
 		else
 			irc_usermsg( irc, "%s is empty", set_name );
+
+		if( strchr( set_name, '/' ) )
+			irc_usermsg( irc, "Warning: / found in setting name, you're probably looking for the `account set' command." );
 	}
 	else
Index: set.c
===================================================================
--- set.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ set.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -357,19 +357,4 @@
 }
 
-char *set_eval_charset( set_t *set, char *value )
-{
-	GIConv cd;
-
-	if ( g_strncasecmp( value, "none", 4 ) == 0 )
-		return value;
-
-	cd = g_iconv_open( "UTF-8", value );
-	if( cd == (GIConv) -1 )
-		return NULL;
-
-	g_iconv_close( cd );
-	return value;
-}
-
 /* possible values: never, opportunistic, manual, always */
 char *set_eval_otr_policy( set_t *set, char *value )
Index: set.h
===================================================================
--- set.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ set.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -101,5 +101,4 @@
 char *set_eval_halfop_buddies( set_t *set, char *value );
 char *set_eval_voice_buddies( set_t *set, char *value );
-char *set_eval_charset( set_t *set, char *value );
 char *set_eval_otr_policy( set_t *set, char *value );
 
Index: sock.h
===================================================================
--- sock.h	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ sock.h	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -16,15 +16,9 @@
 #else
 # include <winsock2.h>
-# ifndef _MSC_VER
-#  include <ws2tcpip.h>
-# endif
+# include <ws2tcpip.h>
 # if !defined(BITLBEE_CORE) && defined(_MSC_VER)
 #   pragma comment(lib,"bitlbee.lib")
 # endif
 # include <io.h>
-# define read(a,b,c) recv(a,b,c,0)
-# define write(a,b,c) send(a,b,c,0)
-# define umask _umask
-# define mode_t int
 # define sock_make_nonblocking(fd) { int non_block = 1; ioctlsocket(fd, FIONBIO, &non_block); }
 # define sock_make_blocking(fd) { int non_block = 0; ioctlsocket(fd, FIONBIO, &non_block); }
Index: storage_text.c
===================================================================
--- storage_text.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ storage_text.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -27,4 +27,12 @@
 #include "bitlbee.h"
 #include "crypting.h"
+#ifdef _WIN32
+# define umask _umask
+# define mode_t int
+#endif
+
+#ifndef F_OK
+#define F_OK 0
+#endif
 
 static void text_init (void)
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ storage_xml.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -29,4 +29,10 @@
 #include "arc.h"
 #include "md5.h"
+#include <glib/gstdio.h>
+
+#if !GLIB_CHECK_VERSION(2,8,0)
+/* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */
+#define g_access access
+#endif
 
 typedef enum
@@ -80,5 +86,5 @@
 		char *nick = xml_attr( attr_names, attr_values, "nick" );
 		char *pass = xml_attr( attr_names, attr_values, "password" );
-		md5_byte_t *pass_dec = NULL;
+		int st;
 		
 		if( !nick || !pass )
@@ -87,40 +93,21 @@
 			             "Missing attributes for %s element", element_name );
 		}
-		else if( base64_decode( pass, &pass_dec ) != 21 )
-		{
+		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
 		{
-			md5_byte_t pass_md5[16];
-			md5_state_t md5_state;
-			int i;
-			
-			md5_init( &md5_state );
-			md5_append( &md5_state, (md5_byte_t*) xd->given_pass, strlen( xd->given_pass ) );
-			md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
-			md5_finish( &md5_state, pass_md5 );
-			
-			for( i = 0; i < 16; i ++ )
-			{
-				if( pass_dec[i] != pass_md5[i] )
-				{
-					xd->pass_st = XML_PASS_WRONG;
-					g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
-					             "Password mismatch" );
-					break;
-				}
-			}
-			
-			/* If we reached the end of the loop, it was a match! */
-			if( i == 16 )
-			{
-				if( xd->pass_st != XML_PASS_CHECK_ONLY )
-					xd->pass_st = XML_PASS_OK;
-			}
-		}
-		
-		g_free( pass_dec );
+			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 )
@@ -262,7 +249,8 @@
 static void xml_init( void )
 {
-	if( access( global.conf->configdir, F_OK ) != 0 )
+	if( g_access( global.conf->configdir, F_OK ) != 0 )
 		log_message( LOGLVL_WARNING, "The configuration directory `%s' does not exist. Configuration won't be saved.", global.conf->configdir );
-	else if( access( global.conf->configdir, R_OK ) != 0 || access( global.conf->configdir, W_OK ) != 0 )
+	else if( g_access( global.conf->configdir, F_OK ) != 0 || 
+	         g_access( global.conf->configdir, W_OK ) != 0 )
 		log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to `%s'.", global.conf->configdir );
 }
@@ -391,5 +379,5 @@
 	g_free( path2 );
 	
-	if( !overwrite && access( path, F_OK ) != -1 )
+	if( !overwrite && g_access( path, F_OK ) == 0 )
 		return STORAGE_ALREADY_EXISTS;
 	
@@ -428,5 +416,5 @@
 		int pass_len;
 		
-		pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password );
+		pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password, 12 );
 		pass_b64 = base64_encode( pass_cr, pass_len );
 		g_free( pass_cr );
@@ -499,5 +487,5 @@
 static storage_status_t xml_remove( const char *nick, const char *password )
 {
-	char s[512];
+	char s[512], *lc;
 	storage_status_t status;
 
@@ -506,5 +494,9 @@
 		return status;
 
-	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".xml" );
+	lc = g_strdup( nick );
+	nick_lc( lc );
+	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, lc, ".xml" );
+	g_free( lc );
+	
 	if( unlink( s ) == -1 )
 		return STORAGE_OTHER_ERROR;
Index: tests/Makefile
===================================================================
--- tests/Makefile	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ tests/Makefile	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -13,5 +13,5 @@
 main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o
 
-test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o check_jabber_sasl.o
+test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o check_jabber_sasl.o check_jabber_util.o
 
 check: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../lib/lib.o
Index: tests/check.c
===================================================================
--- tests/check.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ tests/check.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -69,4 +69,7 @@
 Suite *jabber_sasl_suite(void);
 
+/* From check_jabber_sasl.c */
+Suite *jabber_util_suite(void);
+
 int main (int argc, char **argv)
 {
@@ -115,4 +118,5 @@
 	srunner_add_suite(sr, set_suite());
 	srunner_add_suite(sr, jabber_sasl_suite());
+	srunner_add_suite(sr, jabber_util_suite());
 	if (no_fork)
 		srunner_set_fork_status(sr, CK_NOFORK);
Index: tests/check_arc.c
===================================================================
--- tests/check_arc.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ tests/check_arc.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -7,5 +7,5 @@
 #include "arc.h"
 
-char *password = "TotT";
+char *password = "ArcVier";
 
 char *clear_tests[] =
@@ -14,4 +14,5 @@
 	"ItllBeBitlBee",
 	"One more boring password",
+	"Hoi hoi",
 	NULL
 };
@@ -28,5 +29,5 @@
 		int len;
 		
-		len = arc_encode( clear_tests[i], 0, &crypted, password );
+		len = arc_encode( clear_tests[i], 0, &crypted, password, 12 );
 		len = arc_decode( crypted, len, &decrypted, password );
 		
@@ -41,25 +42,33 @@
 struct
 {
-	unsigned char crypted[24];
+	unsigned char crypted[30];
 	int len;
 	char *decrypted;
 } decrypt_tests[] = {
+	/* One block with padding. */
 	{
 		{
-			0xc3, 0x0d, 0x43, 0xc3, 0xee, 0x80, 0xe2, 0x8c, 0x0b, 0x29, 0x32, 0x7e,
-			0x38, 0x05, 0x82, 0x10, 0x21, 0x1c, 0x4a, 0x00, 0x2c
-		}, 21, "Debugging sucks"
+			0x3f, 0x79, 0xb0, 0xf5, 0x91, 0x56, 0xd2, 0x1b, 0xd1, 0x4b, 0x67, 0xac,
+			0xb1, 0x31, 0xc9, 0xdb, 0xf9, 0xaa
+		}, 18, "short pass"
 	},
+	
+	/* Two blocks with padding. */
 	{
 		{
-			0xb0, 0x00, 0x57, 0x0d, 0x0d, 0x0d, 0x70, 0xe1, 0xc0, 0x00, 0xa4, 0x25,
-			0x7d, 0xbe, 0x03, 0xcc, 0x24, 0xd1, 0x0c
-		}, 19, "Testing rocks"
+			0xf9, 0xa6, 0xec, 0x5d, 0xc7, 0x06, 0xb8, 0x6b, 0x63, 0x9f, 0x2d, 0xb5,
+			0x7d, 0xaa, 0x32, 0xbb, 0xd8, 0x08, 0xfd, 0x81, 0x2e, 0xca, 0xb4, 0xd7,
+			0x2f, 0x36, 0x9c, 0xac, 0xa0, 0xbc
+		}, 30, "longer password"
 	},
+
+	/* This string is exactly two "blocks" long, to make sure unpadded strings also decrypt
+	   properly. */
 	{
 		{
-			0xb6, 0x92, 0x59, 0xe4, 0xf9, 0xc1, 0x7a, 0xf6, 0xf3, 0x18, 0xea, 0x28,
-			0x73, 0x6d, 0xb3, 0x0a, 0x6f, 0x0a, 0x2b, 0x43, 0x57, 0xe9, 0x3e, 0x63
-		}, 24, "OSCAR is creepy..."
+			0x95, 0x4d, 0xcf, 0x4d, 0x5e, 0x6c, 0xcf, 0xef, 0xb9, 0x80, 0x00, 0xef,
+			0x25, 0xe9, 0x17, 0xf6, 0x29, 0x6a, 0x82, 0x79, 0x1c, 0xca, 0x68, 0xb5,
+			0x4e, 0xd0, 0xc1, 0x41, 0x8e, 0xe6
+		}, 30, "OSCAR is really creepy.."
 	},
 	{ "", 0, NULL }
@@ -80,5 +89,5 @@
 		
 		fail_if( strcmp( decrypt_tests[i].decrypted, decrypted ) != 0,
-		         "%s didn't decrypt properly", clear_tests[i] );
+		         "`%s' didn't decrypt properly", decrypt_tests[i].decrypted );
 		
 		g_free( decrypted );
Index: tests/check_irc.c
===================================================================
--- tests/check_irc.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ tests/check_irc.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -37,6 +37,6 @@
 	irc = irc_new(g_io_channel_unix_get_fd(ch1));
 
-	fail_unless(g_io_channel_write_chars(ch2, "NICK bla\r\n"
-			"USER a a a a\r\n", -1, NULL, NULL) == G_IO_STATUS_NORMAL);
+	fail_unless(g_io_channel_write_chars(ch2, "NICK bla\r\r\n"
+			"USER a a a a\n", -1, NULL, NULL) == G_IO_STATUS_NORMAL);
 	fail_unless(g_io_channel_flush(ch2, NULL) == G_IO_STATUS_NORMAL);
 
Index: tests/check_jabber_sasl.c
===================================================================
--- tests/check_jabber_sasl.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ tests/check_jabber_sasl.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -5,5 +5,4 @@
 #include <string.h>
 #include <stdio.h>
-#include "arc.h"
 
 char *sasl_get_part( char *data, char *field );
Index: tests/check_jabber_util.c
===================================================================
--- tests/check_jabber_util.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ tests/check_jabber_util.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,91 @@
+#include <stdlib.h>
+#include <glib.h>
+#include <gmodule.h>
+#include <check.h>
+#include <string.h>
+#include <stdio.h>
+#include "jabber/jabber.h"
+
+static struct im_connection *ic;
+
+static void check_buddy_add(int l)
+{
+	struct jabber_buddy *budw1, *budw2, *budw3, *budn, *bud;
+	
+	budw1 = jabber_buddy_add( ic, "wilmer@gaast.net/BitlBee" );
+	budw1->last_act = time( NULL ) - 100;
+	budw2 = jabber_buddy_add( ic, "WILMER@gaast.net/Telepathy" );
+	budw2->priority = 2;
+	budw2->last_act = time( NULL );
+	budw3 = jabber_buddy_add( ic, "wilmer@GAAST.NET/bitlbee" );
+	budw3->last_act = time( NULL ) - 200;
+	budw3->priority = 4;
+	/* TODO(wilmer): Shouldn't this just return budw3? */
+	fail_if( jabber_buddy_add( ic, "wilmer@gaast.net/Telepathy" ) != NULL );
+	
+	budn = jabber_buddy_add( ic, "nekkid@lamejab.net" );
+	/* Shouldn't be allowed if there's already a bare JID. */
+	fail_if( jabber_buddy_add( ic, "nekkid@lamejab.net/Illegal" ) );
+	
+	/* Case sensitivity: Case only matters after the / */
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@gaast.net/BitlBee", 0 ) ==
+	         jabber_buddy_by_jid( ic, "wilmer@gaast.net/bitlbee", 0 ) );
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@gaast.net/telepathy", 0 ) );
+	
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@gaast.net/BitlBee", 0 ) == budw1 );
+	fail_unless( jabber_buddy_by_jid( ic, "WILMER@GAAST.NET/BitlBee", GET_BUDDY_EXACT ) == budw1 );
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@GAAST.NET/BitlBee", GET_BUDDY_CREAT ) == budw1 );
+
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@gaast.net", GET_BUDDY_EXACT ) );
+	fail_unless( jabber_buddy_by_jid( ic, "WILMER@gaast.net", 0 ) == budw3 );
+
+	/* Check O_FIRST and see if it's indeed the first item from the list. */
+	fail_unless( ( bud = jabber_buddy_by_jid( ic, "wilmer@gaast.net", GET_BUDDY_FIRST ) ) == budw1 );
+	fail_unless( bud->next == budw2 && bud->next->next == budw3 && bud->next->next->next == NULL );
+	
+	/* Change the resource_select setting, now we should get a different resource. */
+	set_setstr( &ic->acc->set, "resource_select", "activity" );
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@GAAST.NET", 0 ) == budw2 );
+	
+	/* Some testing of bare JID handling (which is horrible). */
+	fail_if( jabber_buddy_by_jid( ic, "nekkid@lamejab.net/Illegal", 0 ) );
+	fail_if( jabber_buddy_by_jid( ic, "NEKKID@LAMEJAB.NET/Illegal", GET_BUDDY_CREAT ) );
+	fail_unless( jabber_buddy_by_jid( ic, "nekkid@lamejab.net", 0 ) == budn );
+	fail_unless( jabber_buddy_by_jid( ic, "NEKKID@lamejab.net", GET_BUDDY_EXACT ) == budn );
+	fail_unless( jabber_buddy_by_jid( ic, "nekkid@LAMEJAB.NET", GET_BUDDY_CREAT ) == budn );
+	
+	/* More case sensitivity testing, and see if remove works properly. */
+	fail_if( jabber_buddy_remove( ic, "wilmer@gaast.net/telepathy" ) );
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@GAAST.NET/telepathy", GET_BUDDY_CREAT ) == budw2 );
+	fail_unless( jabber_buddy_remove( ic, "wilmer@gaast.net/Telepathy" ) );
+	fail_unless( jabber_buddy_remove( ic, "wilmer@gaast.net/telepathy" ) );
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@gaast.net", 0 ) == budw1 );
+	
+	fail_if( jabber_buddy_remove( ic, "wilmer@gaast.net" ) );
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@gaast.net", 0 ) == budw1 );
+	
+	/* Check if remove_bare() indeed gets rid of all. */
+	fail_unless( jabber_buddy_remove_bare( ic, "wilmer@gaast.net" ) );
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@gaast.net", 0 ) );
+
+	fail_if( jabber_buddy_remove( ic, "nekkid@lamejab.net/Illegal" ) );
+	fail_unless( jabber_buddy_remove( ic, "nekkid@lamejab.net" ) );
+	fail_if( jabber_buddy_by_jid( ic, "nekkid@lamejab.net", 0 ) );
+}
+
+Suite *jabber_util_suite (void)
+{
+	Suite *s = suite_create("jabber/util");
+	TCase *tc_core = tcase_create("Buddy");
+	struct jabber_data *jd;
+	
+	ic = g_new0( struct im_connection, 1 );
+	ic->acc = g_new0( account_t, 1 );
+	ic->proto_data = jd = g_new0( struct jabber_data, 1 );
+	jd->buddies = g_hash_table_new( g_str_hash, g_str_equal );
+	set_add( &ic->acc->set, "resource_select", "priority", NULL, ic->acc );
+	
+	suite_add_tcase (s, tc_core);
+	tcase_add_test (tc_core, check_buddy_add);
+	return s;
+}
Index: unix.c
===================================================================
--- unix.c	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ unix.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -42,5 +42,5 @@
 static void sighandler( int signal );
 
-int main( int argc, char *argv[], char **envp )
+int main( int argc, char *argv[] )
 {
 	int i = 0;
@@ -69,4 +69,7 @@
 	if( global.conf->runmode == RUNMODE_INETD )
 	{
+		log_link( LOGLVL_ERROR, LOGOUTPUT_IRC );
+		log_link( LOGLVL_WARNING, LOGOUTPUT_IRC );
+	
 		i = bitlbee_inetd_init();
 		log_message( LOGLVL_INFO, "Bitlbee %s starting in inetd mode.", BITLBEE_VERSION );
@@ -75,4 +78,7 @@
 	else if( global.conf->runmode == RUNMODE_DAEMON )
 	{
+		log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG );
+		log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG );
+
 		i = bitlbee_daemon_init();
 		log_message( LOGLVL_INFO, "Bitlbee %s starting in daemon mode.", BITLBEE_VERSION );
@@ -144,28 +150,17 @@
 	{
 		char *fn = ipc_master_save_state();
-		char **args;
-		int n, i;
 		
 		chdir( old_cwd );
 		
-		n = 0;
-		args = g_new0( char *, argc + 3 );
-		args[n++] = argv[0];
-		if( fn )
-		{
-			args[n++] = "-R";
-			args[n++] = fn;
-		}
-		for( i = 1; argv[i] && i < argc; i ++ )
-		{
-			if( strcmp( argv[i], "-R" ) == 0 )
-				i += 2;
-			
-			args[n++] = argv[i];
-		}
+		setenv( "_BITLBEE_RESTART_STATE", fn, 1 );
+		g_free( fn );
 		
 		close( global.listen_socket );
 		
-		execve( args[0], args, envp );
+		if( execv( argv[0], argv ) == -1 )
+			/* Apparently the execve() failed, so let's just
+			   jump back into our own/current main(). */
+			/* Need more cleanup code to make this work. */
+			return 1; /* main( argc, argv ); */
 	}
 	
@@ -228,2 +223,4 @@
 	return( (double) time->tv_sec + (double) time->tv_usec / 1000000 );
 }
+
+
Index: welcome.txt
===================================================================
--- welcome.txt	(revision eb0714d7be4f57b92231fcc1b0798c7fe644c000)
+++ welcome.txt	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -2,4 +2,4 @@
 
 If you've never used BitlBee before, please do read the help information using the help command. Lots of FAQs are answered there.
-
 OTR users please note: Private key files are owned by the user BitlBee is running as.
+If you already have an account on this server, just use the identify command to identify yourself.
Index: win32.c
===================================================================
--- win32.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
+++ win32.c	(revision 9b55485a6f9d72334b372e6fb6b60bbde943170d)
@@ -0,0 +1,332 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway					 *
+  *																	*
+  * Copyright 2002-2004 Wilmer van der Gaast and others				*
+  \********************************************************************/
+
+/* Main file (Windows specific part)								   */
+
+/*
+  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 "commands.h"
+#include "crypting.h"
+#include "protocols/nogaim.h"
+#include "help.h"
+#include <signal.h>
+#include <windows.h>
+
+global_t global;	/* Against global namespace pollution */
+
+static void WINAPI service_ctrl (DWORD dwControl)
+{
+	switch (dwControl)
+	{
+		case SERVICE_CONTROL_STOP:
+			/* FIXME */
+			break;
+
+		case SERVICE_CONTROL_INTERROGATE:
+			break;
+
+		default:
+			break;
+
+	}
+}
+
+static void bitlbee_init(int argc, char **argv)
+{
+	int i = -1;
+	memset( &global, 0, sizeof( global_t ) );
+
+	b_main_init();
+	
+	global.conf = conf_load( argc, argv );
+	if( global.conf == NULL )
+		return;
+	
+	if( global.conf->runmode == RUNMODE_INETD )
+	{
+		i = bitlbee_inetd_init();
+		log_message( LOGLVL_INFO, "Bitlbee %s starting in inetd mode.", BITLBEE_VERSION );
+
+	}
+	else if( global.conf->runmode == RUNMODE_DAEMON )
+	{
+		i = bitlbee_daemon_init();
+		log_message( LOGLVL_INFO, "Bitlbee %s starting in daemon mode.", BITLBEE_VERSION );
+	} 
+	else 
+	{
+		log_message( LOGLVL_INFO, "No bitlbee mode specified...");
+	}
+	
+	if( i != 0 )
+		return;
+ 	
+	if( access( global.conf->configdir, F_OK ) != 0 )
+		log_message( LOGLVL_WARNING, "The configuration directory %s does not exist. Configuration won't be saved.", global.conf->configdir );
+	else if( access( global.conf->configdir, 06 ) != 0 )
+		log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to %s.", global.conf->configdir );
+	if( help_init( &(global.help), HELP_FILE ) == NULL )
+		log_message( LOGLVL_WARNING, "Error opening helpfile %s.", global.helpfile );
+}
+
+void service_main (DWORD argc, LPTSTR *argv)
+{
+	SERVICE_STATUS_HANDLE handle;
+	SERVICE_STATUS status;
+
+	handle = RegisterServiceCtrlHandler("bitlbee", service_ctrl);
+
+	if (!handle)
+		return;
+
+	status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+	status.dwServiceSpecificExitCode = 0;
+
+	bitlbee_init(argc, argv);
+
+	SetServiceStatus(handle, &status);
+	
+	b_main_run( );
+}
+
+SERVICE_TABLE_ENTRY dispatch_table[] =
+{
+   { TEXT("bitlbee"), (LPSERVICE_MAIN_FUNCTION)service_main },
+   { NULL, NULL }
+};
+
+static int debug = 0;
+
+static void usage()
+{
+	printf("Options:\n");
+	printf("-h   Show this help message\n");
+	printf("-d   Debug mode (simple console program)\n");
+}
+
+int main( int argc, char **argv)
+{	
+	int i;
+	WSADATA WSAData;
+
+	nogaim_init( );
+
+	for (i = 1; i < argc; i++) {
+		if (!strcmp(argv[i], "-d")) debug = 1;
+		if (!strcmp(argv[i], "-h")) {
+			usage();
+			return 0;
+		}
+	}
+
+	WSAStartup(MAKEWORD(1,1), &WSAData);
+
+	if (!debug) {
+		if (!StartServiceCtrlDispatcher(dispatch_table))
+			log_message( LOGLVL_ERROR, "StartServiceCtrlDispatcher failed.");
+	} else {
+			bitlbee_init(argc, argv);
+ 			b_main_run();
+	}
+	
+	return 0;
+}
+
+double gettime()
+{
+	return (GetTickCount() / 1000);
+}
+
+void conf_get_string(HKEY section, const char *name, const char *def, char **dest)
+{
+	char buf[4096];
+	long x;
+	if (RegQueryValue(section, name, buf, &x) == ERROR_SUCCESS) {
+		*dest = g_strdup(buf);
+	} else if (!def) {
+		*dest = NULL;
+	} else {
+		*dest = g_strdup(def);
+	}
+}
+
+
+void conf_get_int(HKEY section, const char *name, int def, int *dest)
+{
+	char buf[20];
+	long x;
+	DWORD y;
+	if (RegQueryValue(section, name, buf, &x) == ERROR_SUCCESS) {
+		memcpy(&y, buf, sizeof(DWORD));
+		*dest = y;
+	} else {
+		*dest = def;
+	}
+}
+
+conf_t *conf_load( int argc, char *argv[] ) 
+{
+	conf_t *conf;
+	HKEY key, key_main, key_proxy;
+	char *tmp;
+
+	RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Bitlbee", &key);
+	RegOpenKey(key, "main", &key_main);
+	RegOpenKey(key, "proxy", &key_proxy);
+	
+	memset( &global, 0, sizeof( global_t ) );
+	b_main_init();
+
+	conf = g_new0( conf_t,1 );
+	global.conf = conf;
+	conf_get_string(key_main, "interface_in", "0.0.0.0", &global.conf->iface_in);
+	conf_get_string(key_main, "interface_out", "0.0.0.0", &global.conf->iface_out);
+	conf_get_string(key_main, "port", "6667", &global.conf->port);
+	conf_get_int(key_main, "verbose", 0, &global.conf->verbose);
+	conf_get_string(key_main, "auth_pass", "", &global.conf->auth_pass);
+	conf_get_string(key_main, "oper_pass", "", &global.conf->oper_pass);
+	conf_get_int(key_main, "ping_interval_timeout", 60, &global.conf->ping_interval);
+	conf_get_string(key_main, "hostname", "localhost", &global.conf->hostname);
+	conf_get_string(key_main, "configdir", NULL, &global.conf->configdir);
+	conf_get_string(key_main, "motdfile", NULL, &global.conf->motdfile);
+	conf_get_string(key_main, "helpfile", NULL, &global.helpfile);
+	global.conf->runmode = RUNMODE_DAEMON;
+	conf_get_int(key_main, "AuthMode", AUTHMODE_OPEN, (int *)&global.conf->authmode);
+	conf_get_string(key_proxy, "host", "", &tmp); strcpy(proxyhost, tmp);
+	conf_get_string(key_proxy, "user", "", &tmp); strcpy(proxyuser, tmp);
+	conf_get_string(key_proxy, "password", "", &tmp); strcpy(proxypass, tmp);
+	conf_get_int(key_proxy, "type", PROXY_NONE, &proxytype);
+	conf_get_int(key_proxy, "port", 3128, &proxyport);
+
+	RegCloseKey(key);
+	RegCloseKey(key_main);
+	RegCloseKey(key_proxy);
+
+	return conf;
+}
+
+void conf_loaddefaults( irc_t *irc )
+{
+	HKEY key_defaults;
+	int i;
+	char name[4096], data[4096];
+	DWORD namelen = sizeof(name), datalen = sizeof(data);
+	DWORD type;
+	if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Bitlbee\\defaults", &key_defaults) != ERROR_SUCCESS) {
+		return;
+	}
+
+	for (i = 0; RegEnumValue(key_defaults, i, name, &namelen, NULL, &type, data, &datalen) == ERROR_SUCCESS; i++) {
+		set_t *s = set_find( &irc->set, name );
+			
+		if( s )
+		{
+			if( s->def ) g_free( s->def );
+			s->def = g_strdup( data );
+		}
+
+		namelen = sizeof(name);
+		datalen = sizeof(data);
+	}
+
+	RegCloseKey(key_defaults);
+}
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+int
+inet_aton(const char *cp, struct in_addr *addr)
+{
+  addr->s_addr = inet_addr(cp);
+  return (addr->s_addr == INADDR_NONE) ? 0 : 1;
+}
+
+void log_error(char *msg)
+{
+	log_message(LOGLVL_ERROR, "%s", msg);
+}
+
+void log_message(int level, char *message, ...)
+{
+	HANDLE  hEventSource;
+	LPTSTR  lpszStrings[2];
+	WORD elevel;
+	va_list ap;
+
+	va_start(ap, message);
+
+	if (debug) {
+		vprintf(message, ap);
+		putchar('\n');
+		va_end(ap);
+		return;
+	}
+
+	hEventSource = RegisterEventSource(NULL, TEXT("bitlbee"));
+
+	lpszStrings[0] = TEXT("bitlbee");
+	lpszStrings[1] = g_strdup_vprintf(message, ap);
+	va_end(ap);
+
+	switch (level) {
+	case LOGLVL_ERROR: elevel = EVENTLOG_ERROR_TYPE; break;
+	case LOGLVL_WARNING: elevel = EVENTLOG_WARNING_TYPE; break;
+	case LOGLVL_INFO: elevel = EVENTLOG_INFORMATION_TYPE; break;
+#ifdef DEBUG
+	case LOGLVL_DEBUG: elevel = EVENTLOG_AUDIT_SUCCESS; break;
+#endif
+	}
+
+	if (hEventSource != NULL) {
+		ReportEvent(hEventSource, 
+		elevel,
+		0,					
+		0,					
+		NULL,				 
+		2,					
+		0,					
+		lpszStrings,		  
+		NULL);				
+
+		DeregisterEventSource(hEventSource);
+	}
+
+	g_free(lpszStrings[1]);
+}
+
+void log_link(int level, int output) { /* FIXME */ }
+
+struct tm *
+gmtime_r (const time_t *timer, struct tm *result)
+{
+	struct tm *local_result;
+	local_result = gmtime (timer);
+
+	if (local_result == NULL || result == NULL)
+		return NULL;
+
+	memcpy (result, local_result, sizeof (result));
+	return result;
+} 
