Index: bitlbee.c
===================================================================
--- bitlbee.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ bitlbee.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -48,5 +48,9 @@
 	hints.ai_family = PF_UNSPEC;
 	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
+	hints.ai_flags = AI_PASSIVE
+#ifdef AI_ADDRCONFIG
+	               | AI_ADDRCONFIG
+#endif
+	;
 
 	i = getaddrinfo( global.conf->iface, global.conf->port, &hints, &addrinfo_bind );
@@ -279,5 +283,5 @@
 			child_list = g_slist_append( child_list, child );
 			
-			log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", client_pid );
+			log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid );
 			
 			/* Close some things we don't need in the parent process. */
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ bitlbee.h	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -99,4 +99,10 @@
 #endif
 
+#ifndef G_GNUC_MALLOC
+/* Doesn't exist in GLib <=2.4 while everything else in BitlBee should
+   work with it, so let's fake this one. */
+#define G_GNUC_MALLOC
+#endif
+
 #define _( x ) x
 
@@ -115,6 +121,4 @@
 #define HELP_FILE VARDIR "help.txt"
 #define CONF_FILE_DEF ETCDIR "bitlbee.conf"
-
-extern char *CONF_FILE;
 
 #include "irc.h"
@@ -139,4 +143,5 @@
 	gint listen_watch_source_id;
 	help_t *help;
+	char *conf_file;
 	conf_t *conf;
 	GList *storage; /* The first backend in the list will be used for saving */
Index: conf.c
===================================================================
--- conf.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ conf.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -36,6 +36,4 @@
 #include "proxy.h"
 
-char *CONF_FILE;
-
 static int conf_loadini( conf_t *conf, char *file );
 
@@ -43,5 +41,5 @@
 {
 	conf_t *conf;
-	int opt, i;
+	int opt, i, config_missing = 0;
 	
 	conf = g_new0( conf_t, 1 );
@@ -66,13 +64,15 @@
 	proxytype = 0;
 	
-	i = conf_loadini( conf, CONF_FILE );
+	i = conf_loadini( conf, global.conf_file );
 	if( i == 0 )
 	{
-		fprintf( stderr, "Error: Syntax error in configuration file `%s'.\n", CONF_FILE );
-		return( NULL );
+		fprintf( stderr, "Error: Syntax error in configuration file `%s'.\n", global.conf_file );
+		return NULL;
 	}
 	else if( i == -1 )
 	{
-		fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", CONF_FILE );
+		config_missing ++;
+		/* Whine after parsing the options if there was no -c pointing
+		   at a *valid* configuration file. */
 	}
 	
@@ -106,8 +106,8 @@
 		else if( opt == 'c' )
 		{
-			if( strcmp( CONF_FILE, optarg ) != 0 )
-			{
-				g_free( CONF_FILE );
-				CONF_FILE = g_strdup( optarg );
+			if( strcmp( global.conf_file, optarg ) != 0 )
+			{
+				g_free( global.conf_file );
+				global.conf_file = g_strdup( optarg );
 				g_free( conf );
 				/* Re-evaluate arguments. Don't use this option twice, 
@@ -115,5 +115,5 @@
 				   works with all libcs BTW.. */
 				optind = 1;
-				return( conf_load( argc, argv ) );
+				return conf_load( argc, argv );
 			}
 		}
@@ -143,5 +143,5 @@
 			        "  -d  Specify alternative user configuration directory\n"
 			        "  -h  Show this help page.\n" );
-			return( NULL );
+			return NULL;
 		}
 		else if( opt == 'R' )
@@ -169,5 +169,8 @@
 	}
 	
-	return( conf );
+	if( config_missing )
+		fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", global.conf_file );
+	
+	return conf;
 }
 
@@ -178,5 +181,5 @@
 	
 	ini = ini_open( file );
-	if( ini == NULL ) return( -1 );
+	if( ini == NULL ) return -1;
 	while( ini_read( ini ) )
 	{
@@ -256,5 +259,5 @@
 				{
 					fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
-					return( 0 );
+					return 0;
 				}
 				conf->ping_interval = i;
@@ -265,5 +268,5 @@
 				{
 					fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
-					return( 0 );
+					return 0;
 				}
 				conf->ping_timeout = i;
@@ -277,5 +280,5 @@
 					fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
 					g_free( url );
-					return( 0 );
+					return 0;
 				}
 				
@@ -301,5 +304,5 @@
 			{
 				fprintf( stderr, "Error: Unknown setting `%s` in configuration file.\n", ini->key );
-				return( 0 );
+				return 0;
 				/* For now just ignore unknown keys... */
 			}
@@ -309,10 +312,10 @@
 			fprintf( stderr, "Error: Unknown section [%s] in configuration file. "
 			                 "BitlBee configuration must be put in a [settings] section!\n", ini->section );
-			return( 0 );
+			return 0;
 		}
 	}
 	ini_close( ini );
 	
-	return( 1 );
+	return 1;
 }
 
@@ -321,5 +324,5 @@
 	ini_t *ini;
 	
-	ini = ini_open( CONF_FILE );
+	ini = ini_open( global.conf_file );
 	if( ini == NULL ) return;
 	while( ini_read( ini ) )
Index: configure
===================================================================
--- configure	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ configure	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -189,5 +189,5 @@
 
 if [ "$events" = "libevent" ]; then
-	if ! [ -e "${libevent}include/event.h" ]; then
+	if ! [ -f "${libevent}include/event.h" ]; then
 		echo
 		echo 'Warning: Could not find event.h, you might have to install it and/or specify'
@@ -324,5 +324,5 @@
 
 for i in /lib /usr/lib /usr/local/lib; do
-	if [ -e $i/libresolv.a ]; then
+	if [ -f $i/libresolv.a ]; then
 		echo '#define HAVE_RESOLV_A' >> config.h
 		echo 'EFLAGS+='$i'/libresolv.a' >> Makefile.settings
Index: crypting.c
===================================================================
--- crypting.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ crypting.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -29,8 +29,5 @@
    the programs will be built. */
 
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <glib.h>
+#include <bitlbee.h>
 #include "md5.h"
 #include "crypting.h"
Index: doc/user-guide/quickstart.xml
===================================================================
--- doc/user-guide/quickstart.xml	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ doc/user-guide/quickstart.xml	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -34,5 +34,5 @@
 
 <para>
-For instance, suppose you have an ICQ account with UIN <emphasis>72696705</emphasis> with password <emphasis>QuickStart</emphasis>, you would:
+For instance, suppose you have a Jabber account at jabber.org with handle <emphasis>bitlbee@jabber.org</emphasis> with password <emphasis>QuickStart</emphasis>, you would:
 </para>
 
@@ -43,5 +43,5 @@
 
 <para>
-Other available IM protocols are msn, oscar, and yahoo. Oscar is the protocol used by ICQ and AOL.
+Other available IM protocols are msn, oscar, and yahoo. OSCAR is the protocol used by ICQ and AOL. For more information about the <emphasis>account add</emphasis> command, see <emphasis>help account add</emphasis>.
 </para>
 
Index: help.c
===================================================================
--- help.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ help.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -71,19 +71,18 @@
 		{
 			/* FIXME: Clean up */
-//			help_close( *help );
-			*help = NULL;
+			help_free( help );
 			g_free( s );
-			return( NULL );
+			return NULL;
 		}
 		i = strchr( s, '\n' ) - s;
 		
-		if( h->string )
+		if( h->title )
 		{
 			h = h->next = g_new0( help_t, 1 );
 		}
-		h->string = g_new ( char, i );
+		h->title = g_new ( char, i );
 		
-		strncpy( h->string, s + 1, i - 1 );
-		h->string[i-1] = 0;
+		strncpy( h->title, s + 1, i - 1 );
+		h->title[i-1] = 0;
 		h->fd = (*help)->fd;
 		h->offset.file_offset = lseek( h->fd, 0, SEEK_CUR ) - buflen + i + 1;
@@ -103,5 +102,29 @@
 }
 
-char *help_get( help_t **help, char *string )
+void help_free( help_t **help )
+{
+	help_t *h, *oh;
+	int last_fd = -1; /* Weak de-dupe */
+	
+	if( help == NULL || *help == NULL )
+		return;
+	
+	h = *help;
+	while( h )
+	{
+		if( h->fd != last_fd )
+		{
+			close( h->fd );
+			last_fd = h->fd;
+		}
+		g_free( h->title );
+		h = (oh=h)->next;
+		g_free( oh );
+	}
+	
+	*help = NULL;
+}
+
+char *help_get( help_t **help, char *title )
 {
 	time_t mtime;
@@ -111,6 +134,5 @@
 	for( h = *help; h; h = h->next )
 	{
-		if( h->string != NULL && 
-			g_strcasecmp( h->string, string ) == 0 ) 
+		if( h->title != NULL && g_strcasecmp( h->title, title ) == 0 )
 			break;
 	}
@@ -119,18 +141,20 @@
 		char *s = g_new( char, h->length + 1 );
 		
-		if( fstat( h->fd, stat ) != 0 )
-		{
-			g_free( h );
-			*help = NULL;
-			return NULL;
-		}
-		mtime = stat->st_mtime;
-		
-		if( mtime > h->mtime )
-			return NULL;
-		
 		s[h->length] = 0;
 		if( h->fd >= 0 )
 		{
+			if( fstat( h->fd, stat ) != 0 )
+			{
+				g_free( s );
+				return NULL;
+			}
+			mtime = stat->st_mtime;
+		
+			if( mtime > h->mtime )
+			{
+				g_free( s );
+				return NULL;
+			}
+			
 			lseek( h->fd, h->offset.file_offset, SEEK_SET );
 			read( h->fd, s, h->length );
Index: help.h
===================================================================
--- help.h	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ help.h	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -37,5 +37,5 @@
 	int fd;
 	time_t mtime;
-	char *string;
+	char *title;
 	help_off_t offset;
 	int length;
@@ -44,5 +44,6 @@
 
 G_GNUC_MALLOC help_t *help_init( help_t **help, const char *helpfile );
-char *help_get( help_t **help, char *string );
+void help_free( help_t **help );
+char *help_get( help_t **help, char *title );
 
 #endif
Index: ipc.c
===================================================================
--- ipc.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ ipc.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -52,5 +52,5 @@
 	if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 )
 		ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
-		                     child ? child->pid : -1, cmd[2], cmd[1], cmd[3] );
+		                     (int) ( child ? child->pid : -1 ), cmd[2], cmd[1], cmd[3] );
 }
 
@@ -446,5 +446,5 @@
 	
 	for( l = child_list; l; l = l->next )
-		fprintf( fp, "%d %d\n", ((struct bitlbee_child*)l->data)->pid,
+		fprintf( fp, "%d %d\n", (int) ((struct bitlbee_child*)l->data)->pid,
 		                        ((struct bitlbee_child*)l->data)->ipc_fd );
 	
@@ -551,5 +551,5 @@
 		child = g_new0( struct bitlbee_child, 1 );
 		
-		if( fscanf( fp, "%d %d", &child->pid, &child->ipc_fd ) != 2 )
+		if( fscanf( fp, "%d %d", (int *) &child->pid, &child->ipc_fd ) != 2 )
 		{
 			log_message( LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i );
Index: irc.c
===================================================================
--- irc.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ irc.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -74,5 +74,5 @@
 
 		if( getnameinfo( (struct sockaddr *) &sock, socklen, buf,
-		                 NI_MAXHOST, NULL, -1, 0 ) == 0 )
+		                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
 		{
 			irc->myhost = g_strdup( ipv6_unwrap( buf ) );
@@ -85,5 +85,5 @@
 
 		if( getnameinfo( (struct sockaddr *)&sock, socklen, buf,
-		                 NI_MAXHOST, NULL, -1, 0 ) == 0 )
+		                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
 		{
 			irc->host = g_strdup( ipv6_unwrap( buf ) );
@@ -189,5 +189,4 @@
 	account_t *account;
 	user_t *user, *usertmp;
-	help_t *helpnode, *helpnodetmp;
 	
 	log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
@@ -266,14 +265,4 @@
 	g_hash_table_destroy(irc->watches);
 	
-	if (irc->help != NULL) {
-		helpnode = irc->help;
-		while (helpnode != NULL) {
-			g_free(helpnode->string);
-			
-			helpnodetmp = helpnode;
-			helpnode = helpnode->next;
-			g_free(helpnodetmp);
-		}
-	}
 	g_free(irc);
 	
@@ -325,5 +314,8 @@
 				if( do_iconv( cs, "UTF-8", lines[i], conv, 0, IRC_MAX_LINE - 2 ) == -1 )
 				{
+					/* GLib can do strange things if things are not in the expected charset,
+					   so let's be a little bit paranoid here: */
 					if( irc->status & USTATUS_LOGGED_IN )
+					{
 						irc_usermsg( irc, "Error: Charset mismatch detected. The charset "
 						                  "setting is currently set to %s, so please make "
@@ -333,5 +325,16 @@
 						                  "`help set charset' for more information. Your "
 						                  "message was ignored.", cs );
-					*conv = 0;
+						*conv = 0;
+					}
+					else
+					{
+						irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
+						           "Warning: invalid (non-UTF8) characters received at login time." );
+						
+						strncpy( conv, lines[i], IRC_MAX_LINE );
+						for( temp = conv; *temp; temp ++ )
+							if( *temp & 0x80 )
+								*temp = '?';
+					}
 				}
 				lines[i] = conv;
Index: irc.h
===================================================================
--- irc.h	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ irc.h	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -89,5 +89,4 @@
 	GHashTable *watches;
 	struct __NICK *nicks;
-	struct help *help;
 	struct set *set;
 
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ irc_commands.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -556,5 +556,5 @@
 	
 	for( h = global.help; h; h = h->next )
-		irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->string );
+		irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->title );
 	
 	for( s = irc->set; s; s = s->next )
@@ -571,5 +571,5 @@
 		ipc_to_master( cmd );
 	
-	irc_reply( irc, 382, "%s :Rehashing", CONF_FILE );
+	irc_reply( irc, 382, "%s :Rehashing", global.conf_file );
 }
 
Index: lib/md5.c
===================================================================
--- lib/md5.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ lib/md5.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -26,5 +26,5 @@
 #include "md5.h"
 
-static void md5_transform(u_int32_t buf[4], u_int32_t const in[16]);
+static void md5_transform(uint32_t buf[4], uint32_t const in[16]);
 
 /*
@@ -34,4 +34,27 @@
  * 20021120
  */
+
+/* Turns out MD5 was designed for little-endian machines. If we're running
+   on a big-endian machines, we have to swap some bytes. Since detecting
+   endianness at compile time reliably seems pretty hard, let's do it at
+   run-time. It's not like we're going to checksum megabytes of data... */
+static uint32_t cvt32(uint32_t val)
+{
+	static int little_endian = -1;
+	
+	if (little_endian == -1)
+	{
+		little_endian = 1;
+		little_endian = *((char*) &little_endian);
+	}
+	
+	if (little_endian)
+		return val;
+	else
+		return (val >> 24) |
+		       ((val >> 8) & 0xff00) |
+		       ((val << 8) & 0xff0000) |
+		       (val << 24);
+}
 
 void md5_init(struct MD5Context *ctx)
@@ -53,10 +76,10 @@
 		unsigned int len)
 {
-	u_int32_t t;
+	uint32_t t;
 
 	/* Update bitcount */
 
 	t = ctx->bits[0];
-	if ((ctx->bits[0] = t + ((u_int32_t) len << 3)) < t)
+	if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
 		ctx->bits[1]++;	/* Carry from low to high */
 	ctx->bits[1] += len >> 29;
@@ -75,5 +98,5 @@
 		}
 		memcpy(p, buf, t);
-		md5_transform(ctx->buf, (u_int32_t *) ctx->in);
+		md5_transform(ctx->buf, (uint32_t *) ctx->in);
 		buf += t;
 		len -= t;
@@ -83,5 +106,5 @@
 	while (len >= 64) {
 		memcpy(ctx->in, buf, 64);
-		md5_transform(ctx->buf, (u_int32_t *) ctx->in);
+		md5_transform(ctx->buf, (uint32_t *) ctx->in);
 		buf += 64;
 		len -= 64;
@@ -117,5 +140,5 @@
 		/* Two lots of padding:  Pad the first block to 64 bytes */
 		memset(p, 0, count);
-		md5_transform(ctx->buf, (u_int32_t *) ctx->in);
+		md5_transform(ctx->buf, (uint32_t *) ctx->in);
 
 		/* Now fill the next block with 56 bytes */
@@ -127,8 +150,12 @@
 
 	/* Append length in bits and transform */
-	((u_int32_t *) ctx->in)[14] = ctx->bits[0];
-	((u_int32_t *) ctx->in)[15] = ctx->bits[1];
-
-	md5_transform(ctx->buf, (u_int32_t *) ctx->in);
+	((uint32_t *) ctx->in)[14] = cvt32(ctx->bits[0]);
+	((uint32_t *) ctx->in)[15] = cvt32(ctx->bits[1]);
+
+	md5_transform(ctx->buf, (uint32_t *) ctx->in);
+	ctx->buf[0] = cvt32(ctx->buf[0]);
+	ctx->buf[1] = cvt32(ctx->buf[1]);
+	ctx->buf[2] = cvt32(ctx->buf[2]);
+	ctx->buf[3] = cvt32(ctx->buf[3]);
 	memcpy(digest, ctx->buf, 16);
 	memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
@@ -152,7 +179,7 @@
  * the data and converts bytes into longwords for this routine.
  */
-static void md5_transform(u_int32_t buf[4], u_int32_t const in[16])
-{
-	register u_int32_t a, b, c, d;
+static void md5_transform(uint32_t buf[4], uint32_t const in[16])
+{
+	register uint32_t a, b, c, d;
 
 	a = buf[0];
@@ -161,71 +188,71 @@
 	d = buf[3];
 
-	MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
-	MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
-	MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
-	MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
-	MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
-	MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
-	MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
-	MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
-	MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
-	MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
-	MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
-	MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
-	MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
-	MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
-	MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
-	MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
-	MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
-	MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
-	MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
-	MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
-	MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
-	MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
-	MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
-	MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
-	MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
-	MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
-	MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
-	MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
-	MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
-	MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
-	MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
-	MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
-	MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
-	MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
-	MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
-	MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
-	MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
-	MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
-	MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
-	MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
-	MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
-	MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
-	MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
-	MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
-	MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
-	MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
-	MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
-	MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
-	MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
-	MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
-	MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
-	MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
-	MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
-	MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
-	MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
-	MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
-	MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
-	MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
-	MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
-	MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
-	MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
-	MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
-	MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
-	MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+	MD5STEP(F1, a, b, c, d, cvt32(in[0]) + 0xd76aa478, 7);
+	MD5STEP(F1, d, a, b, c, cvt32(in[1]) + 0xe8c7b756, 12);
+	MD5STEP(F1, c, d, a, b, cvt32(in[2]) + 0x242070db, 17);
+	MD5STEP(F1, b, c, d, a, cvt32(in[3]) + 0xc1bdceee, 22);
+	MD5STEP(F1, a, b, c, d, cvt32(in[4]) + 0xf57c0faf, 7);
+	MD5STEP(F1, d, a, b, c, cvt32(in[5]) + 0x4787c62a, 12);
+	MD5STEP(F1, c, d, a, b, cvt32(in[6]) + 0xa8304613, 17);
+	MD5STEP(F1, b, c, d, a, cvt32(in[7]) + 0xfd469501, 22);
+	MD5STEP(F1, a, b, c, d, cvt32(in[8]) + 0x698098d8, 7);
+	MD5STEP(F1, d, a, b, c, cvt32(in[9]) + 0x8b44f7af, 12);
+	MD5STEP(F1, c, d, a, b, cvt32(in[10]) + 0xffff5bb1, 17);
+	MD5STEP(F1, b, c, d, a, cvt32(in[11]) + 0x895cd7be, 22);
+	MD5STEP(F1, a, b, c, d, cvt32(in[12]) + 0x6b901122, 7);
+	MD5STEP(F1, d, a, b, c, cvt32(in[13]) + 0xfd987193, 12);
+	MD5STEP(F1, c, d, a, b, cvt32(in[14]) + 0xa679438e, 17);
+	MD5STEP(F1, b, c, d, a, cvt32(in[15]) + 0x49b40821, 22);
+
+	MD5STEP(F2, a, b, c, d, cvt32(in[1]) + 0xf61e2562, 5);
+	MD5STEP(F2, d, a, b, c, cvt32(in[6]) + 0xc040b340, 9);
+	MD5STEP(F2, c, d, a, b, cvt32(in[11]) + 0x265e5a51, 14);
+	MD5STEP(F2, b, c, d, a, cvt32(in[0]) + 0xe9b6c7aa, 20);
+	MD5STEP(F2, a, b, c, d, cvt32(in[5]) + 0xd62f105d, 5);
+	MD5STEP(F2, d, a, b, c, cvt32(in[10]) + 0x02441453, 9);
+	MD5STEP(F2, c, d, a, b, cvt32(in[15]) + 0xd8a1e681, 14);
+	MD5STEP(F2, b, c, d, a, cvt32(in[4]) + 0xe7d3fbc8, 20);
+	MD5STEP(F2, a, b, c, d, cvt32(in[9]) + 0x21e1cde6, 5);
+	MD5STEP(F2, d, a, b, c, cvt32(in[14]) + 0xc33707d6, 9);
+	MD5STEP(F2, c, d, a, b, cvt32(in[3]) + 0xf4d50d87, 14);
+	MD5STEP(F2, b, c, d, a, cvt32(in[8]) + 0x455a14ed, 20);
+	MD5STEP(F2, a, b, c, d, cvt32(in[13]) + 0xa9e3e905, 5);
+	MD5STEP(F2, d, a, b, c, cvt32(in[2]) + 0xfcefa3f8, 9);
+	MD5STEP(F2, c, d, a, b, cvt32(in[7]) + 0x676f02d9, 14);
+	MD5STEP(F2, b, c, d, a, cvt32(in[12]) + 0x8d2a4c8a, 20);
+
+	MD5STEP(F3, a, b, c, d, cvt32(in[5]) + 0xfffa3942, 4);
+	MD5STEP(F3, d, a, b, c, cvt32(in[8]) + 0x8771f681, 11);
+	MD5STEP(F3, c, d, a, b, cvt32(in[11]) + 0x6d9d6122, 16);
+	MD5STEP(F3, b, c, d, a, cvt32(in[14]) + 0xfde5380c, 23);
+	MD5STEP(F3, a, b, c, d, cvt32(in[1]) + 0xa4beea44, 4);
+	MD5STEP(F3, d, a, b, c, cvt32(in[4]) + 0x4bdecfa9, 11);
+	MD5STEP(F3, c, d, a, b, cvt32(in[7]) + 0xf6bb4b60, 16);
+	MD5STEP(F3, b, c, d, a, cvt32(in[10]) + 0xbebfbc70, 23);
+	MD5STEP(F3, a, b, c, d, cvt32(in[13]) + 0x289b7ec6, 4);
+	MD5STEP(F3, d, a, b, c, cvt32(in[0]) + 0xeaa127fa, 11);
+	MD5STEP(F3, c, d, a, b, cvt32(in[3]) + 0xd4ef3085, 16);
+	MD5STEP(F3, b, c, d, a, cvt32(in[6]) + 0x04881d05, 23);
+	MD5STEP(F3, a, b, c, d, cvt32(in[9]) + 0xd9d4d039, 4);
+	MD5STEP(F3, d, a, b, c, cvt32(in[12]) + 0xe6db99e5, 11);
+	MD5STEP(F3, c, d, a, b, cvt32(in[15]) + 0x1fa27cf8, 16);
+	MD5STEP(F3, b, c, d, a, cvt32(in[2]) + 0xc4ac5665, 23);
+
+	MD5STEP(F4, a, b, c, d, cvt32(in[0]) + 0xf4292244, 6);
+	MD5STEP(F4, d, a, b, c, cvt32(in[7]) + 0x432aff97, 10);
+	MD5STEP(F4, c, d, a, b, cvt32(in[14]) + 0xab9423a7, 15);
+	MD5STEP(F4, b, c, d, a, cvt32(in[5]) + 0xfc93a039, 21);
+	MD5STEP(F4, a, b, c, d, cvt32(in[12]) + 0x655b59c3, 6);
+	MD5STEP(F4, d, a, b, c, cvt32(in[3]) + 0x8f0ccc92, 10);
+	MD5STEP(F4, c, d, a, b, cvt32(in[10]) + 0xffeff47d, 15);
+	MD5STEP(F4, b, c, d, a, cvt32(in[1]) + 0x85845dd1, 21);
+	MD5STEP(F4, a, b, c, d, cvt32(in[8]) + 0x6fa87e4f, 6);
+	MD5STEP(F4, d, a, b, c, cvt32(in[15]) + 0xfe2ce6e0, 10);
+	MD5STEP(F4, c, d, a, b, cvt32(in[6]) + 0xa3014314, 15);
+	MD5STEP(F4, b, c, d, a, cvt32(in[13]) + 0x4e0811a1, 21);
+	MD5STEP(F4, a, b, c, d, cvt32(in[4]) + 0xf7537e82, 6);
+	MD5STEP(F4, d, a, b, c, cvt32(in[11]) + 0xbd3af235, 10);
+	MD5STEP(F4, c, d, a, b, cvt32(in[2]) + 0x2ad7d2bb, 15);
+	MD5STEP(F4, b, c, d, a, cvt32(in[9]) + 0xeb86d391, 21);
 
 	buf[0] += a;
Index: lib/md5.h
===================================================================
--- lib/md5.h	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ lib/md5.h	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -27,9 +27,10 @@
 #include <sys/types.h>
 #include <gmodule.h>
+#include <stdint.h>
 
-typedef u_int8_t md5_byte_t;
+typedef uint8_t md5_byte_t;
 typedef struct MD5Context {
-	u_int32_t buf[4];
-	u_int32_t bits[2];
+	uint32_t buf[4];
+	uint32_t bits[2];
 	unsigned char in[64];
 } md5_state_t;
Index: lib/misc.c
===================================================================
--- lib/misc.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ lib/misc.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -90,4 +90,5 @@
 	struct tm tm;
 
+	memset(&tm, 0, sizeof(struct tm));
 	tm.tm_year = year - 1900;
 	tm.tm_mon = month - 1;
@@ -96,4 +97,5 @@
 	tm.tm_min = min;
 	tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
+	
 	return mktime(&tm);
 }
@@ -245,10 +247,4 @@
 }
 
-void info_string_append(GString *str, char *newline, char *name, char *value)
-{
-	if( value && value[0] )
-		g_string_sprintfa( str, "%s%s: %s", newline, name, value );
-}
-
 /* Decode%20a%20file%20name						*/
 void http_decode( char *s )
Index: lib/misc.h
===================================================================
--- lib/misc.h	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ lib/misc.h	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -42,5 +42,4 @@
 G_MODULE_EXPORT char *strip_newlines(char *source);
 G_MODULE_EXPORT char *normalize( const char *s );
-G_MODULE_EXPORT void info_string_append( GString *str, char *newline, char *name, char *value );
 
 G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );
Index: protocols/jabber/conference.c
===================================================================
--- protocols/jabber/conference.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/jabber/conference.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -37,4 +37,6 @@
 	xt_add_attr( node, "xmlns", XMLNS_MUC );
 	node = jabber_make_packet( "presence", NULL, roomjid, node );
+	if( password )
+		xt_add_child( node, xt_new_node( "password", password, NULL ) );
 	jabber_cache_add( ic, node, jabber_chat_join_failed );
 	
@@ -73,15 +75,14 @@
 	
 	room = xt_find_attr( orig, "to" );
-	if( ( bud = jabber_buddy_by_jid( ic, room, 0 ) ) )
-		jabber_chat_free( jabber_chat_by_jid( ic, bud->bare_jid ) );
-	
+	bud = jabber_buddy_by_jid( ic, room, 0 );
 	err = jabber_error_parse( xt_find_node( node->children, "error" ), XMLNS_STANZA_ERROR );
 	if( err )
 	{
-		imcb_error( ic, "Error joining groupchat %s: %s%s%s",
-		            bud->bare_jid, err->code, err->text ? ": " : "",
-		            err->text ? err->text : "" );
+		imcb_error( ic, "Error joining groupchat %s: %s%s%s", room, err->code,
+		            err->text ? ": " : "", err->text ? err->text : "" );
 		jabber_error_free( err );
 	}
+	if( bud )
+		jabber_chat_free( jabber_chat_by_jid( ic, bud->bare_jid ) );
 	
 	return XT_HANDLED;
@@ -123,4 +124,6 @@
 	struct jabber_chat *jc = c->data;
 	struct xt_node *node;
+	
+	jc->flags |= JCFLAG_MESSAGE_SENT;
 	
 	node = xt_new_node( "body", message, NULL );
@@ -296,20 +299,57 @@
 	struct xt_node *subject = xt_find_node( node->children, "subject" );
 	struct xt_node *body = xt_find_node( node->children, "body" );
-	struct groupchat *chat;
+	struct groupchat *chat = bud ? jabber_chat_by_jid( ic, bud->bare_jid ) : NULL;
+	struct jabber_chat *jc = chat ? chat->data : NULL;
 	char *s;
 	
-	if( bud == NULL )
-	{
+	if( bud == NULL || ( jc && ~jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me ) )
+	{
+		char *nick;
+		
+		if( body == NULL || body->text_len == 0 )
+			/* Meh. Empty messages aren't very interesting, no matter
+			   how much some servers love to send them. */
+			return;
+		
 		s = xt_find_attr( node, "from" ); /* pkt_message() already NULL-checked this one. */
-		if( strchr( s, '/' ) == NULL )
+		nick = strchr( s, '/' );
+		if( nick )
+		{
+			/* If this message included a resource/nick we don't know,
+			   we might still know the groupchat itself. */
+			*nick = 0;
+			chat = jabber_chat_by_jid( ic, s );
+			*nick = '/';
+			
+			nick ++;
+		}
+		else
+		{
+			/* message.c uses the EXACT_JID option, so bud should
+			   always be NULL here for bare JIDs. */
+			chat = jabber_chat_by_jid( ic, s );
+		}
+		
+		if( nick == NULL )
+		{
 			/* This is fine, the groupchat itself isn't in jd->buddies. */
-			imcb_log( ic, "System message from groupchat %s: %s", s, body? body->text : "NULL" );
+			if( chat )
+				imcb_chat_log( chat, "From conference server: %s", body->text );
+			else
+				imcb_log( ic, "System message from unknown groupchat %s: %s", s, body->text );
+		}
 		else
-			/* This, however, isn't fine! */
-			imcb_log( ic, "Groupchat message from unknown participant %s: %s", s, body ? body->text : "NULL" );
+		{
+			/* This can happen too, at least when receiving a backlog when
+			   just joining a channel. */
+			if( chat )
+				imcb_chat_log( chat, "Message from unknown participant %s: %s", nick, body->text );
+			else
+				imcb_log( ic, "Groupchat message from unknown JID %s: %s", s, body->text );
+		}
 		
 		return;
 	}
-	else if( ( chat = jabber_chat_by_jid( ic, bud->bare_jid ) ) == NULL )
+	else if( chat == NULL )
 	{
 		/* How could this happen?? We could do kill( self, 11 )
Index: protocols/jabber/io.c
===================================================================
--- protocols/jabber/io.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/jabber/io.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -249,4 +249,7 @@
 	struct im_connection *ic = data;
 	
+	if( g_slist_find( jabber_connections, ic ) == NULL )
+		return FALSE;
+	
 	if( source == -1 )
 	{
@@ -264,5 +267,10 @@
 {
 	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
+	struct jabber_data *jd;
+	
+	if( g_slist_find( jabber_connections, ic ) == NULL )
+		return FALSE;
+	
+	jd = ic->proto_data;
 	
 	if( source == NULL )
Index: protocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/jabber/iq.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -92,5 +92,6 @@
 		else if( strcmp( s, XMLNS_DISCOVER ) == 0 )
 		{
-			const char *features[] = { XMLNS_VERSION,
+			const char *features[] = { XMLNS_DISCOVER,
+			                           XMLNS_VERSION,
 			                           XMLNS_TIME,
 			                           XMLNS_CHATSTATES,
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/jabber/jabber.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -35,4 +35,6 @@
 #include "base64.h"
 
+GSList *jabber_connections;
+
 static void jabber_init( account_t *acc )
 {
@@ -70,4 +72,9 @@
 	struct ns_srv_reply *srv = NULL;
 	char *connect_to, *s;
+	
+	/* For now this is needed in the _connected() handlers if using
+	   GLib event handling, to make sure we're not handling events
+	   on dead connections. */
+	jabber_connections = g_slist_prepend( jabber_connections, ic );
 	
 	jd->ic = ic;
@@ -263,4 +270,6 @@
 	g_free( jd->username );
 	g_free( jd );
+	
+	jabber_connections = g_slist_remove( jabber_connections, ic );
 }
 
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/jabber/jabber.h	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -29,4 +29,6 @@
 #include "xmltree.h"
 #include "bitlbee.h"
+
+extern GSList *jabber_connections;
 
 typedef enum
@@ -47,13 +49,19 @@
 typedef enum
 {
-	JBFLAG_PROBED_XEP85 = 1,	/* Set this when we sent our probe packet to make
+	JBFLAG_PROBED_XEP85 = 1,        /* Set this when we sent our probe packet to make
 	                                   sure it gets sent only once. */
-	JBFLAG_DOES_XEP85 = 2,		/* Set this when the resource seems to support
+	JBFLAG_DOES_XEP85 = 2,          /* Set this when the resource seems to support
 	                                   XEP85 (typing notification shite). */
-	JBFLAG_IS_CHATROOM = 4,		/* It's convenient to use this JID thingy for
+	JBFLAG_IS_CHATROOM = 4,         /* It's convenient to use this JID thingy for
 	                                   groupchat state info too. */
-	JBFLAG_IS_ANONYMOUS = 8,	/* For anonymous chatrooms, when we don't have
+	JBFLAG_IS_ANONYMOUS = 8,        /* For anonymous chatrooms, when we don't have
 	                                   have a real JID. */
 } jabber_buddy_flags_t;
+
+typedef enum
+{
+	JCFLAG_MESSAGE_SENT = 1,        /* Set this after sending the first message, so
+	                                   we can detect echoes/backlogs. */
+} jabber_chat_flags_t;
 
 struct jabber_data
@@ -93,4 +101,5 @@
 struct jabber_cache_entry
 {
+	time_t saved_at;
 	struct xt_node *node;
 	jabber_cache_event func;
@@ -138,4 +147,8 @@
 #define JABBER_PACKET_ID "BeeP"
 #define JABBER_CACHED_ID "BeeC"
+
+/* The number of seconds to keep cached packets before garbage collecting
+   them. This gc is done on every keepalive (every minute). */
+#define JABBER_CACHE_MAX_AGE 600
 
 /* RFC 392[01] stuff */
@@ -159,4 +172,5 @@
 #define XMLNS_MUC          "http://jabber.org/protocol/muc"     /* XEP-0045 */
 #define XMLNS_MUC_USER     "http://jabber.org/protocol/muc#user"/* XEP-0045 */
+#define XMLNS_CAPS         "http://jabber.org/protocol/caps"    /* XEP-0115 */
 
 /* iq.c */
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/jabber/jabber_util.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -142,4 +142,5 @@
 	entry->node = node;
 	entry->func = func;
+	entry->saved_at = time( NULL );
 	g_hash_table_insert( jd->node_cache, xt_find_attr( node, "id" ), entry );
 }
@@ -163,20 +164,15 @@
 {
 	struct jabber_data *jd = ic->proto_data;
-	
-	g_hash_table_foreach_remove( jd->node_cache, jabber_cache_clean_entry, NULL );
-}
-
-gboolean jabber_cache_clean_entry( gpointer key, gpointer entry_, gpointer nullpointer )
+	time_t threshold = time( NULL ) - JABBER_CACHE_MAX_AGE;
+	
+	g_hash_table_foreach_remove( jd->node_cache, jabber_cache_clean_entry, &threshold );
+}
+
+gboolean jabber_cache_clean_entry( gpointer key, gpointer entry_, gpointer threshold_ )
 {
 	struct jabber_cache_entry *entry = entry_;
-	struct xt_node *node = entry->node;
-	
-	if( node->flags & XT_SEEN )
-		return TRUE;
-	else
-	{
-		node->flags |= XT_SEEN;
-		return FALSE;
-	}
+	time_t *threshold = threshold_;
+	
+	return entry->saved_at < *threshold;
 }
 
@@ -392,17 +388,24 @@
 		if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )
 		{
+			/* Just return the first one for this bare JID. */
+			if( flags & GET_BUDDY_FIRST )
+			{
+				*s = '/';
+				g_free( jid );
+				return bud;
+			}
+			
 			/* Is this one of those no-resource buddies? */
 			if( bud->resource == NULL )
 			{
+				*s = '/';
 				g_free( jid );
 				return NULL;
 			}
-			else
-			{
-				/* See if there's an exact match. */
-				for( ; bud; bud = bud->next )
-					if( g_strcasecmp( bud->resource, s + 1 ) == 0 )
-						break;
-			}
+			
+			/* See if there's an exact match. */
+			for( ; bud; bud = bud->next )
+				if( g_strcasecmp( bud->resource, s + 1 ) == 0 )
+					break;
 		}
 		else
@@ -413,4 +416,6 @@
 			   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... */
 		}
 		
@@ -442,4 +447,7 @@
 		else if( ( bud->resource == NULL || bud->next == NULL ) )
 			/* No need for selection if there's only one option. */
+			return bud;
+		else if( flags & GET_BUDDY_FIRST )
+			/* Looks like the caller doesn't care about details. */
 			return bud;
 		
Index: protocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/jabber/presence.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -29,7 +29,7 @@
 	char *from = xt_find_attr( node, "from" );
 	char *type = xt_find_attr( node, "type" );	/* NULL should mean the person is online. */
-	struct xt_node *c;
-	struct jabber_buddy *bud;
-	int is_chat = 0, is_away = 0;
+	struct xt_node *c, *cap;
+	struct jabber_buddy *bud, *send_presence = NULL;
+	int is_chat = 0;
 	char *s;
 	
@@ -63,6 +63,4 @@
 		{
 			bud->away_state = (void*) jabber_away_state_by_code( c->text );
-			if( strcmp( c->text, "chat" ) != 0 )
-				is_away = OPT_AWAY;
 		}
 		else
@@ -79,10 +77,28 @@
 			bud->priority = 0;
 		
+		if( bud && ( cap = xt_find_node( node->children, "c" ) ) &&
+		    ( s = xt_find_attr( cap, "xmlns" ) ) && strcmp( s, XMLNS_CAPS ) == 0 )
+		{
+			/* This <presence> stanza includes an XEP-0115
+			   capabilities part. Not too interesting, but we can
+			   see if it has an ext= attribute. */
+			s = xt_find_attr( cap, "ext" );
+			if( s && ( strstr( s, "cstates" ) || strstr( s, "chatstate" ) ) )
+				bud->flags |= JBFLAG_DOES_XEP85;
+			
+			/* This field can contain more information like xhtml
+			   support, but we don't support that ourselves.
+			   Officially the ext= tag was deprecated, but enough
+			   clients do send it.
+			   
+			   (I'm aware that this is not the right way to use
+			   this field.) See for an explanation of ext=:
+			   http://www.xmpp.org/extensions/attic/xep-0115-1.3.html*/
+		}
+		
 		if( is_chat )
 			jabber_chat_pkt_presence( ic, bud, node );
-		else if( bud == jabber_buddy_by_jid( ic, bud->bare_jid, 0 ) )
-			imcb_buddy_status( ic, bud->bare_jid, OPT_LOGGED_IN | is_away,
-			                   ( is_away && bud->away_state ) ? bud->away_state->full_name : NULL,
-			                   bud->away_message );
+		else
+			send_presence = jabber_buddy_by_jid( ic, bud->bare_jid, 0 );
 	}
 	else if( strcmp( type, "unavailable" ) == 0 )
@@ -119,15 +135,5 @@
 			/* If another resource is still available, send its presence
 			   information. */
-			if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) )
-			{
-				if( bud->away_state && ( *bud->away_state->code == 0 ||
-				    strcmp( bud->away_state->code, "chat" ) == 0 ) )
-					is_away = OPT_AWAY;
-				
-				imcb_buddy_status( ic, bud->bare_jid, OPT_LOGGED_IN | is_away,
-				                   ( is_away && bud->away_state ) ? bud->away_state->full_name : NULL,
-				                   bud->away_message );
-			}
-			else
+			if( ( send_presence = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL )
 			{
 				/* Otherwise, count him/her as offline now. */
@@ -177,4 +183,18 @@
 		} */
 	}
+
+	if( send_presence )
+	{
+		int is_away = 0;
+
+		if( send_presence->away_state && !( *send_presence->away_state->code == 0 ||
+		    strcmp( send_presence->away_state->code, "chat" ) == 0 ) )
+			is_away = OPT_AWAY;
+
+		imcb_buddy_status( ic, send_presence->bare_jid, OPT_LOGGED_IN | is_away,
+		                   ( is_away && send_presence->away_state ) ?
+		                   send_presence->away_state->full_name : NULL,
+		                   send_presence->away_message );
+	}
 	
 	return XT_HANDLED;
@@ -186,5 +206,5 @@
 {
 	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *node;
+	struct xt_node *node, *cap;
 	char *show = jd->away_state->code;
 	char *status = jd->away_message;
@@ -199,4 +219,14 @@
 		xt_add_child( node, xt_new_node( "status", status, NULL ) );
 	
+	/* This makes the packet slightly bigger, but clients interested in
+	   capabilities can now cache the discovery info. This reduces the
+	   usual post-login iq-flood. See XEP-0115. At least libpurple and
+	   Trillian seem to do this right. */
+	cap = xt_new_node( "c", NULL, NULL );
+	xt_add_attr( cap, "xmlns", XMLNS_CAPS );
+	xt_add_attr( cap, "node", "http://bitlbee.org/xmpp/caps" );
+	xt_add_attr( cap, "ver", BITLBEE_VERSION ); /* The XEP wants this hashed, but nobody's doing that. */
+	xt_add_child( node, cap );
+	
 	st = jabber_write_packet( ic, node );
 	
Index: protocols/jabber/sasl.c
===================================================================
--- protocols/jabber/sasl.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/jabber/sasl.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -21,4 +21,6 @@
 *                                                                           *
 \***************************************************************************/
+
+#include <ctype.h>
 
 #include "jabber.h"
@@ -107,9 +109,14 @@
 }
 
-static char *sasl_get_part( char *data, char *field )
+/* Non-static function, but not mentioned in jabber.h because it's for internal
+   use, just that the unittest should be able to reach it... */
+char *sasl_get_part( char *data, char *field )
 {
 	int i, len;
 	
 	len = strlen( field );
+	
+	while( isspace( *data ) || *data == ',' )
+		data ++;
 	
 	if( g_strncasecmp( data, field, len ) == 0 && data[len] == '=' )
@@ -129,11 +136,17 @@
 			}
 			
-			/* If we got a comma, we got a new field. Check it. */
-			if( data[i] == ',' &&
-			    g_strncasecmp( data + i + 1, field, len ) == 0 &&
-			    data[i+len+1] == '=' )
+			/* If we got a comma, we got a new field. Check it,
+			   find the next key after it. */
+			if( data[i] == ',' )
 			{
-				i += len + 2;
-				break;
+				while( isspace( data[i] ) || data[i] == ',' )
+					i ++;
+				
+				if( g_strncasecmp( data + i, field, len ) == 0 &&
+				    data[i+len] == '=' )
+				{
+					i += len + 1;
+					break;
+				}
 			}
 		}
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/msn/msn.h	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -29,9 +29,7 @@
 #define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r"
 
-#ifdef _WIN32
-#define debug 
+#ifdef DEBUG
+#define debug( text... ) imcb_log( ic, text );
 #else
-#define debug( text... ) irc_usermsg( IRC, text );
-#undef debug
 #define debug( text... )
 #endif
@@ -66,6 +64,8 @@
 	GSList *msgq;
 	GSList *switchboards;
+	int sb_failures;
+	time_t first_sb_failure;
+	
 	const struct msn_away_state *away_state;
-	
 	int buddycount;
 	int groupcount;
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/msn/ns.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -584,5 +584,5 @@
 	else
 	{
-		debug( "Received unknown command from main server: %s", cmd[0] );
+		/* debug( "Received unknown command from main server: %s", cmd[0] ); */
 	}
 	
Index: protocols/msn/sb.c
===================================================================
--- protocols/msn/sb.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/msn/sb.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -277,8 +277,49 @@
 {
 	struct msn_switchboard *sb = data;
+	struct im_connection *ic = sb->ic;
+	struct msn_data *md = ic->proto_data;
 	
 	if( msn_handler( sb->handler ) == -1 )
 	{
+		time_t now = time( NULL );
+		
+		if( now - md->first_sb_failure > 600 )
+		{
+			/* It's not really the first one, but the start of this "series".
+			   With this, the warning below will be shown only if this happens
+			   at least three times in ten minutes. This algorithm isn't
+			   perfect, but for this purpose it will do. */
+			md->first_sb_failure = now;
+			md->sb_failures = 0;
+		}
+		
 		debug( "Error: Switchboard died" );
+		if( ++ md->sb_failures >= 3 )
+			imcb_log( ic, "Warning: Many switchboard failures on MSN connection. "
+			              "There might be problems delivering your messages." );
+		
+		if( sb->msgq != NULL )
+		{
+			char buf[1024];
+			
+			if( md->msgq == NULL )
+			{
+				md->msgq = sb->msgq;
+			}
+			else
+			{
+				GSList *l;
+				
+				for( l = md->msgq; l->next; l = l->next );
+				l->next = sb->msgq;
+			}
+			sb->msgq = NULL;
+			
+			debug( "Moved queued messages back to the main queue, creating a new switchboard to retry." );
+			g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
+			if( !msn_write( ic, buf, strlen( buf ) ) )
+				return FALSE;
+		}
+		
 		msn_sb_destroy( sb );
 		
@@ -286,5 +327,7 @@
 	}
 	else
+	{
 		return TRUE;
+	}
 }
 
@@ -551,5 +594,5 @@
 	else
 	{
-		debug( "Received unknown command from switchboard server: %s", cmd[0] );
+		/* debug( "Received unknown command from switchboard server: %s", cmd[0] ); */
 	}
 	
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/nogaim.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -625,5 +625,5 @@
 }
 
-void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_t flags, time_t sent_at )
+void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t flags, time_t sent_at )
 {
 	irc_t *irc = ic->irc;
@@ -676,5 +676,5 @@
 }
 
-void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags )
+void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags )
 {
 	user_t *u;
@@ -690,4 +690,29 @@
 		irc_privmsg( ic->irc, u, "PRIVMSG", ic->irc->nick, NULL, buf );
 	}
+}
+
+struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle )
+{
+	struct groupchat *c;
+	
+	/* This one just creates the conversation structure, user won't see anything yet */
+	
+	if( ic->groupchats )
+	{
+		for( c = ic->groupchats; c->next; c = c->next );
+		c = c->next = g_new0( struct groupchat, 1 );
+	}
+	else
+		ic->groupchats = c = g_new0( struct groupchat, 1 );
+	
+	c->ic = ic;
+	c->title = g_strdup( handle );
+	c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ );
+	c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title );
+	
+	if( set_getbool( &ic->irc->set, "debug" ) )
+		imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle );
+	
+	return c;
 }
 
@@ -728,9 +753,10 @@
 		g_free( c->channel );
 		g_free( c->title );
+		g_free( c->topic );
 		g_free( c );
 	}
 }
 
-void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags, time_t sent_at )
+void imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at )
 {
 	struct im_connection *ic = c->ic;
@@ -758,4 +784,22 @@
 	}
 	g_free( wrapped );
+}
+
+void imcb_chat_log( struct groupchat *c, char *format, ... )
+{
+	irc_t *irc = c->ic->irc;
+	va_list params;
+	char *text;
+	user_t *u;
+	
+	va_start( params, format );
+	text = g_strdup_vprintf( format, params );
+	va_end( params );
+	
+	u = user_find( irc, irc->mynick );
+	
+	irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text );
+	
+	g_free( text );
 }
 
@@ -781,29 +825,4 @@
 	if( c->joined && u )
 		irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic );
-}
-
-struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle )
-{
-	struct groupchat *c;
-	
-	/* This one just creates the conversation structure, user won't see anything yet */
-	
-	if( ic->groupchats )
-	{
-		for( c = ic->groupchats; c->next; c = c->next );
-		c = c->next = g_new0( struct groupchat, 1 );
-	}
-	else
-		ic->groupchats = c = g_new0( struct groupchat, 1 );
-	
-	c->ic = ic;
-	c->title = g_strdup( handle );
-	c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ );
-	c->topic = g_strdup_printf( "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->channel, c->title );
-	
-	if( set_getbool( &ic->irc->set, "debug" ) )
-		imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle );
-	
-	return c;
 }
 
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/nogaim.h	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -68,5 +68,5 @@
 {
 	account_t *acc;
-	u_int32_t flags;
+	uint32_t flags;
 	
 	/* each connection then can have its own protocol-specific data */
@@ -282,6 +282,6 @@
 /* Not implemented yet! */ G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle );
 /* Call when a handle says something. 'flags' and 'sent_at may be just 0. */
-G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_t flags, time_t sent_at );
-G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags );
+G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t flags, time_t sent_at );
+G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags );
 G_MODULE_EXPORT void imcb_clean_handle( struct im_connection *ic, char *handle );
 
@@ -299,5 +299,7 @@
 G_MODULE_EXPORT void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason );
 /* To tell BitlBee 'who' said 'msg' in 'c'. 'flags' and 'sent_at' can be 0. */
-G_MODULE_EXPORT void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags, time_t sent_at );
+G_MODULE_EXPORT void imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at );
+/* System messages specific to a groupchat, so they can be displayed in the right context. */
+G_MODULE_EXPORT void imcb_chat_log( struct groupchat *c, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
 /* To tell BitlBee 'who' changed the topic of 'c' to 'topic'. */
 G_MODULE_EXPORT void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at );
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/oscar/oscar.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -91,4 +91,6 @@
 	gboolean icq;
 	GSList *evilhack;
+	
+	GHashTable *ips;
 
 	struct {
@@ -356,7 +358,8 @@
 	struct oscar_data *odata = ic->proto_data = g_new0(struct oscar_data, 1);
 
-	if (!isdigit(acc->user[0])) {
+	if (isdigit(acc->user[0]))
+		odata->icq = TRUE;
+	else
 		ic->flags |= OPT_DOES_HTML;
-	}
 
 	sess = g_new0(aim_session_t, 1);
@@ -411,4 +414,6 @@
 		g_free(cr);
 	}
+	if (odata->ips)
+		g_hash_table_destroy(odata->ips);
 	if (odata->email)
 		g_free(odata->email);
@@ -987,4 +992,14 @@
 		signon = time(NULL) - info->sessionlen;
 
+	if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR) {
+		uint32_t *uin = g_new0(uint32_t, 1);
+		
+		if (od->ips == NULL)
+			od->ips = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL);
+		
+		if (sscanf(info->sn, "%d", uin) == 1)
+			g_hash_table_insert(od->ips, uin, (gpointer) (long) info->icqinfo.ipaddr);
+	}
+
 	tmp = g_strdup(normalize(ic->acc->user));
 	if (!strcmp(tmp, normalize(info->sn)))
@@ -1051,10 +1066,12 @@
 		g_snprintf(tmp, BUF_LONG, "%s", args->msg);
 	} else {
-		int i;
+		aim_mpmsg_section_t *part;
 		
 		*tmp = 0;
-		for (i = 0; i < args->mpmsg.numparts; i ++) {
-			g_strlcat(tmp, (char*) args->mpmsg.parts[i].data, BUF_LONG);
-			g_strlcat(tmp, "\n", BUF_LONG);
+		for (part = args->mpmsg.parts; part; part = part->next) {
+			if (part->data) {
+				g_strlcat(tmp, (char*) part->data, BUF_LONG);
+				g_strlcat(tmp, "\n", BUF_LONG);
+			}
 		}
 	}
@@ -2219,85 +2236,92 @@
 static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
 {
-        struct im_connection *ic = sess->aux_data;
-        gchar who[16];
-        GString *str;
-        va_list ap;
-        struct aim_icq_info *info;
-
-        va_start(ap, fr);
-        info = va_arg(ap, struct aim_icq_info *);
-        va_end(ap);
-
-        if (!info->uin)
-                return 0;
-
-        str = g_string_sized_new(100);
-        g_snprintf(who, sizeof(who), "%u", info->uin);
-
-        g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
-				info->nick ? info->nick : "-");
-        info_string_append(str, "\n", _("First Name"), info->first);
-        info_string_append(str, "\n", _("Last Name"), info->last);
-		info_string_append(str, "\n", _("Email Address"), info->email);
-        if (info->numaddresses && info->email2) {
-                int i;
-                for (i = 0; i < info->numaddresses; i++) {
-					info_string_append(str, "\n", _("Email Address"), info->email2[i]);
-                }
-        }
-        info_string_append(str, "\n", _("Mobile Phone"), info->mobile);
-        if (info->gender != 0)
-        	info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
-        if (info->birthyear || info->birthmonth || info->birthday) {
-                char date[30];
-                struct tm tm;
-                tm.tm_mday = (int)info->birthday;
-                tm.tm_mon = (int)info->birthmonth-1;
-                tm.tm_year = (int)info->birthyear%100;
-                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
-                info_string_append(str, "\n", _("Birthday"), date);
-        }
-        if (info->age) {
-                char age[5];
-                g_snprintf(age, sizeof(age), "%hhd", info->age);
-                info_string_append(str, "\n", _("Age"), age);
-        }
-		info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage);
-        if (info->info && info->info[0]) {
-                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
-						info->info, _("End of Additional Information"));
-        }
-        g_string_sprintfa(str, "\n");
-        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
-                g_string_sprintfa(str, "%s:", _("Home Address"));
-                info_string_append(str, "\n", _("Address"), info->homeaddr);
-                info_string_append(str, "\n", _("City"), info->homecity);
-                info_string_append(str, "\n", _("State"), info->homestate); 
-				info_string_append(str, "\n", _("Zip Code"), info->homezip);
-                g_string_sprintfa(str, "\n");
-        }
-        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
-                g_string_sprintfa(str, "%s:", _("Work Address"));
-                info_string_append(str, "\n", _("Address"), info->workaddr);
-                info_string_append(str, "\n", _("City"), info->workcity);
-                info_string_append(str, "\n", _("State"), info->workstate);
-				info_string_append(str, "\n", _("Zip Code"), info->workzip);
-                g_string_sprintfa(str, "\n");
-        }
-        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
-                g_string_sprintfa(str, "%s:", _("Work Information"));
-                info_string_append(str, "\n", _("Company"), info->workcompany);
-                info_string_append(str, "\n", _("Division"), info->workdivision);
-                info_string_append(str, "\n", _("Position"), info->workposition);
-                if (info->workwebpage && info->workwebpage[0]) {
-                        info_string_append(str, "\n", _("Web Page"), info->workwebpage);
-                }
-                g_string_sprintfa(str, "\n");
-        }
-
-		imcb_log(ic, "%s\n%s", _("User Info"), str->str);
-        g_string_free(str, TRUE);
-
-        return 1;
+	struct im_connection *ic = sess->aux_data;
+	struct oscar_data *od = ic->proto_data;
+	gchar who[16];
+	GString *str;
+	va_list ap;
+	struct aim_icq_info *info;
+	uint32_t ip;
+
+	va_start(ap, fr);
+	info = va_arg(ap, struct aim_icq_info *);
+	va_end(ap);
+
+	if (!info->uin)
+		return 0;
+
+	str = g_string_sized_new(512);
+	g_snprintf(who, sizeof(who), "%u", info->uin);
+
+	g_string_printf(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
+	info->nick ? info->nick : "-");
+	g_string_append_printf(str, "\n%s: %s", _("First Name"), info->first);
+	g_string_append_printf(str, "\n%s: %s", _("Last Name"), info->last);
+	g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email);
+	if (info->numaddresses && info->email2) {
+		int i;
+		for (i = 0; i < info->numaddresses; i++) {
+			g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email2[i]);
+		}
+	}
+	if ((ip = (long) g_hash_table_lookup(od->ips, &info->uin)) != 0) {
+		g_string_append_printf(str, "\n%s: %d.%d.%d.%d", _("Last used IP address"),
+		                       (ip >> 24), (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
+	}
+	g_string_append_printf(str, "\n%s: %s", _("Mobile Phone"), info->mobile);
+	if (info->gender != 0)
+		g_string_append_printf(str, "\n%s: %s", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
+	if (info->birthyear || info->birthmonth || info->birthday) {
+		char date[30];
+		struct tm tm;
+		memset(&tm, 0, sizeof(struct tm));
+		tm.tm_mday = (int)info->birthday;
+		tm.tm_mon = (int)info->birthmonth-1;
+		tm.tm_year = (int)info->birthyear%100;
+		strftime(date, sizeof(date), "%Y-%m-%d", &tm);
+		g_string_append_printf(str, "\n%s: %s", _("Birthday"), date);
+	}
+	if (info->age) {
+		char age[5];
+		g_snprintf(age, sizeof(age), "%hhd", info->age);
+		g_string_append_printf(str, "\n%s: %s", _("Age"), age);
+	}
+	g_string_append_printf(str, "\n%s: %s", _("Personal Web Page"), info->personalwebpage);
+	if (info->info && info->info[0]) {
+		g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
+		info->info, _("End of Additional Information"));
+	}
+	g_string_append_c(str, '\n');
+	if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
+		g_string_append_printf(str, "%s:", _("Home Address"));
+		g_string_append_printf(str, "\n%s: %s", _("Address"), info->homeaddr);
+		g_string_append_printf(str, "\n%s: %s", _("City"), info->homecity);
+		g_string_append_printf(str, "\n%s: %s", _("State"), info->homestate); 
+		g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->homezip);
+		g_string_append_c(str, '\n');
+	}
+	if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
+		g_string_append_printf(str, "%s:", _("Work Address"));
+		g_string_append_printf(str, "\n%s: %s", _("Address"), info->workaddr);
+		g_string_append_printf(str, "\n%s: %s", _("City"), info->workcity);
+		g_string_append_printf(str, "\n%s: %s", _("State"), info->workstate);
+		g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->workzip);
+		g_string_append_c(str, '\n');
+	}
+	if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
+		g_string_append_printf(str, "%s:", _("Work Information"));
+		g_string_append_printf(str, "\n%s: %s", _("Company"), info->workcompany);
+		g_string_append_printf(str, "\n%s: %s", _("Division"), info->workdivision);
+		g_string_append_printf(str, "\n%s: %s", _("Position"), info->workposition);
+		if (info->workwebpage && info->workwebpage[0]) {
+			g_string_append_printf(str, "\n%s: %s", _("Web Page"), info->workwebpage);
+		}
+		g_string_append_c(str, '\n');
+	}
+
+	imcb_log(ic, "%s\n%s", _("User Info"), str->str);
+	g_string_free(str, TRUE);
+
+	return 1;
 
 }
@@ -2433,5 +2457,5 @@
 		/* User has stopped typing */
 		imcb_buddy_typing(ic, sn, 0);
-	}        
+	}
 	
 	return 1;
Index: protocols/yahoo/yahoo.c
===================================================================
--- protocols/yahoo/yahoo.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ protocols/yahoo/yahoo.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -624,8 +624,12 @@
 {
 	struct im_connection *ic = byahoo_get_ic_by_id( id );
-	char *m = byahoo_strip( msg );
-	
-	imcb_buddy_msg( ic, (char*) who, (char*) m, 0, 0 );
-	g_free( m );
+	char *m;
+	
+	if( msg )
+	{
+		m = byahoo_strip( msg );
+		imcb_buddy_msg( ic, (char*) who, (char*) m, 0, 0 );
+		g_free( m );
+	}
 }
 
Index: root_commands.c
===================================================================
--- root_commands.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ root_commands.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -449,5 +449,5 @@
 	{
 		add_on_server = 0;
-		cmd ++;		/* So evil... :-D */
+		cmd ++;
 	}
 	
@@ -481,10 +481,11 @@
 	}
 	
-	/* By making this optional, you can talk to people without having to
-	   add them to your *real* (server-side) contact list. */
 	if( add_on_server )
 		a->ic->acc->prpl->add_buddy( a->ic, cmd[2], NULL );
-	
-	/* add_buddy( a->ic, NULL, cmd[2], cmd[2] ); */
+	else
+		/* Yeah, officially this is a call-*back*... So if we just
+		   called add_buddy, we'll wait for the IM server to respond
+		   before we do this. */
+		imcb_add_buddy( a->ic, cmd[2], NULL );
 	
 	irc_usermsg( irc, "Adding `%s' to your contact list", cmd[2]  );
@@ -821,5 +822,5 @@
 		if( online == 1 )
 		{
-			g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->ic->acc->prpl->name );
+			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );
 			irc_usermsg( irc, format, u->nick, s, "Online" );
 		}
@@ -832,5 +833,5 @@
 		if( away == 1 )
 		{
-			g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->ic->acc->prpl->name );
+			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );
 			irc_usermsg( irc, format, u->nick, s, u->away );
 		}
@@ -842,5 +843,5 @@
 		if( offline == 1 )
 		{
-			g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->ic->acc->prpl->name );
+			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );
 			irc_usermsg( irc, format, u->nick, s, "Offline" );
 		}
Index: storage_text.c
===================================================================
--- storage_text.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ storage_text.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -30,8 +30,8 @@
 static void text_init (void)
 {
-	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, R_OK ) != 0 || access( global.conf->configdir, W_OK ) != 0 )
-		log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to %s.", global.conf->configdir );
+	/* Don't complain about the configuration directory anymore, leave it
+	   up to the XML storage module, which uses the same directory for it
+	   anyway. Nobody should be using just the text plugin anymore since
+	   it's read only! */
 }
 
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ storage_xml.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -263,7 +263,7 @@
 {
 	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 );
+		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 )
-		log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to %s.", global.conf->configdir );
+		log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to `%s'.", global.conf->configdir );
 }
 
Index: tests/Makefile
===================================================================
--- tests/Makefile	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ tests/Makefile	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -11,7 +11,7 @@
 distclean: clean
 
-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 
+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
+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: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../lib/lib.o
Index: tests/check.c
===================================================================
--- tests/check.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ tests/check.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -66,4 +66,7 @@
 Suite *set_suite(void);
 
+/* From check_jabber_sasl.c */
+Suite *jabber_sasl_suite(void);
+
 int main (int argc, char **argv)
 {
@@ -111,4 +114,5 @@
 	srunner_add_suite(sr, crypting_suite());
 	srunner_add_suite(sr, set_suite());
+	srunner_add_suite(sr, jabber_sasl_suite());
 	if (no_fork)
 		srunner_set_fork_status(sr, CK_NOFORK);
Index: tests/check_arc.c
===================================================================
--- tests/check_arc.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ tests/check_arc.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -62,5 +62,6 @@
 			0x73, 0x6d, 0xb3, 0x0a, 0x6f, 0x0a, 0x2b, 0x43, 0x57, 0xe9, 0x3e, 0x63
 		}, 24, "OSCAR is creepy..."
-	}
+	},
+	{ "", 0, NULL }
 };
 
@@ -69,5 +70,5 @@
 	int i;
 	
-	for( i = 0; clear_tests[i]; i++ )
+	for( i = 0; decrypt_tests[i].len; i++ )
 	{
   		tcase_fn_start (decrypt_tests[i].decrypted, __FILE__, __LINE__);
Index: tests/check_jabber_sasl.c
===================================================================
--- tests/check_jabber_sasl.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
+++ tests/check_jabber_sasl.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -0,0 +1,117 @@
+#include <stdlib.h>
+#include <glib.h>
+#include <gmodule.h>
+#include <check.h>
+#include <string.h>
+#include <stdio.h>
+#include "arc.h"
+
+char *sasl_get_part( char *data, char *field );
+
+#define challenge1 "nonce=\"1669585310\",qop=\"auth\",charset=utf-8,algorithm=md5-sess," \
+                   "something=\"Not \\\"standardized\\\"\""
+#define challenge2 "realm=\"quadpoint.org\", nonce=\"NPotlQpQf9RNYodOwierkQ==\", " \
+                   "qop=\"auth, auth-int\", charset=utf-8, algorithm=md5-sess"
+#define challenge3 ", realm=\"localhost\", nonce=\"LlBV2txnO8RbB5hgs3KgiQ==\", " \
+                   "qop=\"auth, auth-int, \", ,\n, charset=utf-8, algorithm=md5-sess,"
+
+struct
+{
+	const char *challenge;
+	char *key;
+	char *value;
+} get_part_tests[] = {
+	{
+		challenge1,
+		"nonce",
+		"1669585310"
+	},
+	{
+		challenge1,
+		"charset",
+		"utf-8"
+	},
+	{
+		challenge1,
+		"harset",
+		NULL
+	},
+	{
+		challenge1,
+		"something",
+		"Not \"standardized\""
+	},
+	{
+		challenge1,
+		"something_else",
+		NULL
+	},
+	{
+		challenge2,
+		"realm",
+		"quadpoint.org",
+	},
+	{
+		challenge2,
+		"real",
+		NULL
+	},
+	{
+		challenge2,
+		"qop",
+		"auth, auth-int"
+	},
+	{
+		challenge3,
+		"realm",
+		"localhost"
+	},
+	{
+		challenge3,
+		"qop",
+		"auth, auth-int, "
+	},
+	{
+		challenge3,
+		"charset",
+		"utf-8"
+	},
+	{ NULL, NULL, NULL }
+};
+
+static void check_get_part(int l)
+{
+	int i;
+	
+	for( i = 0; get_part_tests[i].key; i++ )
+	{
+  		tcase_fn_start( get_part_tests[i].key, __FILE__, i );
+		char *res;
+		int len;
+		
+		res = sasl_get_part( get_part_tests[i].challenge,
+		                     get_part_tests[i].key );
+		
+		if( get_part_tests[i].value == NULL )
+			fail_if( res != NULL, "Found key %s in %s while it shouldn't be there!",
+			         get_part_tests[i].key, get_part_tests[i].challenge );
+		else if( res )
+			fail_unless( strcmp( res, get_part_tests[i].value ) == 0,
+			             "Incorrect value for key %s in %s: %s",
+			             get_part_tests[i].key, get_part_tests[i].challenge, res );
+		else
+			fail( "Could not find key %s in %s",
+			      get_part_tests[i].key, get_part_tests[i].challenge );
+		
+		g_free( res );
+	}
+}
+
+Suite *jabber_sasl_suite (void)
+{
+	Suite *s = suite_create("jabber/sasl");
+	TCase *tc_core = tcase_create("Core");
+	suite_add_tcase (s, tc_core);
+	tcase_add_test (tc_core, check_get_part);
+	return s;
+}
Index: unix.c
===================================================================
--- unix.c	(revision fc5cf88448d4337d1a5fde418df1c6206a9b0ea2)
+++ unix.c	(revision ca605509d0b49e6012d10ae5d1553ced007e6ce7)
@@ -47,5 +47,5 @@
 	
 	log_init();
-	CONF_FILE = g_strdup( CONF_FILE_DEF );
+	global.conf_file = g_strdup( CONF_FILE_DEF );
 	global.conf = conf_load( argc, argv );
 	if( global.conf == NULL )
@@ -124,8 +124,11 @@
 	if( !getuid() || !geteuid() )
 		log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" );
-	if( help_init( &(global.help), global.helpfile ) == NULL )
+	if( help_init( &global.help, global.helpfile ) == NULL )
 		log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE );
 	
 	b_main_run();
+	
+	/* Mainly good for restarting, to make sure we close the help.txt fd. */
+	help_free( &global.help );
 	
 	if( global.restart )
@@ -197,7 +200,7 @@
 		{
 			if( WIFSIGNALED( st ) )
-				log_message( LOGLVL_INFO, "Client %d terminated normally. (status = %d)", pid, WEXITSTATUS( st ) );
+				log_message( LOGLVL_INFO, "Client %d terminated normally. (status = %d)", (int) pid, WEXITSTATUS( st ) );
 			else if( WIFEXITED( st ) )
-				log_message( LOGLVL_INFO, "Client %d killed by signal %d.", pid, WTERMSIG( st ) );
+				log_message( LOGLVL_INFO, "Client %d killed by signal %d.", (int) pid, WTERMSIG( st ) );
 		}
 	}
