Index: Makefile
===================================================================
--- Makefile	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ Makefile	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -50,5 +50,4 @@
 	$(MAKE) -C tests
 
-lcov:
 gcov: check
 	gcov *.c
Index: bitlbee.c
===================================================================
--- bitlbee.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ bitlbee.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 );
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ bitlbee.h	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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"
@@ -140,4 +144,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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ conf.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 );
@@ -67,13 +65,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. */
 	}
 	
@@ -107,8 +107,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, 
@@ -116,5 +116,5 @@
 				   works with all libcs BTW.. */
 				optind = 1;
-				return( conf_load( argc, argv ) );
+				return conf_load( argc, argv );
 			}
 		}
@@ -144,5 +144,5 @@
 			        "  -d  Specify alternative user configuration directory\n"
 			        "  -h  Show this help page.\n" );
-			return( NULL );
+			return NULL;
 		}
 		else if( opt == 'R' )
@@ -170,5 +170,8 @@
 	}
 	
-	return( conf );
+	if( config_missing )
+		fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", global.conf_file );
+	
+	return conf;
 }
 
@@ -179,5 +182,5 @@
 	
 	ini = ini_open( file );
-	if( ini == NULL ) return( -1 );
+	if( ini == NULL ) return -1;
 	while( ini_read( ini ) )
 	{
@@ -262,5 +265,5 @@
 				{
 					fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
-					return( 0 );
+					return 0;
 				}
 				conf->ping_interval = i;
@@ -271,5 +274,5 @@
 				{
 					fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
-					return( 0 );
+					return 0;
 				}
 				conf->ping_timeout = i;
@@ -283,5 +286,5 @@
 					fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
 					g_free( url );
-					return( 0 );
+					return 0;
 				}
 				
@@ -307,5 +310,5 @@
 			{
 				fprintf( stderr, "Error: Unknown setting `%s` in configuration file.\n", ini->key );
-				return( 0 );
+				return 0;
 				/* For now just ignore unknown keys... */
 			}
@@ -315,10 +318,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;
 }
 
@@ -327,5 +330,5 @@
 	ini_t *ini;
 	
-	ini = ini_open( CONF_FILE );
+	ini = ini_open( global.conf_file );
 	if( ini == NULL ) return;
 	while( ini_read( ini ) )
Index: crypting.c
===================================================================
--- crypting.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ crypting.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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: debian/watch
===================================================================
--- debian/watch	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
+++ debian/watch	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -0,0 +1,2 @@
+version=2
+http://get.bitlbee.org/src/bitlbee-(.*).tar.gz
Index: help.c
===================================================================
--- help.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ help.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ help.h	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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: irc.c
===================================================================
--- irc.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ irc.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -77,5 +77,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 ) );
@@ -88,5 +88,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 ) );
@@ -199,5 +199,4 @@
 	account_t *account;
 	user_t *user, *usertmp;
-	help_t *helpnode, *helpnodetmp;
 	
 	log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
@@ -275,15 +274,4 @@
 	g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);
 	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);
-		}
-	}
 	
 	otr_free(irc->otr);
Index: irc.h
===================================================================
--- irc.h	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ irc.h	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -91,5 +91,4 @@
 	GHashTable *watches;
 	struct __NICK *nicks;
-	struct help *help;
 	struct set *set;
 
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ irc_commands.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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/Makefile
===================================================================
--- lib/Makefile	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ lib/Makefile	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -18,5 +18,5 @@
 all: lib.o
 check: all
-lcov:
+lcov: check
 gcov:
 	gcov *.c
Index: protocols/Makefile
===================================================================
--- protocols/Makefile	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/Makefile	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -27,5 +27,5 @@
 all: protocols.o
 check: all
-lcov:
+lcov: check
 gcov:
 	gcov *.c
Index: protocols/jabber/Makefile
===================================================================
--- protocols/jabber/Makefile	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/jabber/Makefile	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -18,5 +18,5 @@
 all: jabber_mod.o
 check: all
-lcov:
+lcov: check
 gcov: 
 	gcov *.c
Index: protocols/jabber/conference.c
===================================================================
--- protocols/jabber/conference.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/jabber/conference.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 );
 	
@@ -122,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 );
@@ -295,8 +299,9 @@
 	struct xt_node *subject = xt_find_node( node->children, "subject" );
 	struct xt_node *body = xt_find_node( node->children, "body" );
-	struct groupchat *chat = NULL;
+	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;
@@ -346,5 +351,5 @@
 		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/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/jabber/iq.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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.h
===================================================================
--- protocols/jabber/jabber.h	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/jabber/jabber.h	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -49,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
@@ -95,4 +101,5 @@
 struct jabber_cache_entry
 {
+	time_t saved_at;
 	struct xt_node *node;
 	jabber_cache_event func;
@@ -140,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 */
@@ -161,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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/jabber/jabber_util.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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;
 }
 
Index: protocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/jabber/presence.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -29,5 +29,5 @@
 	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 xt_node *c, *cap;
 	struct jabber_buddy *bud, *send_presence = NULL;
 	int is_chat = 0;
@@ -76,4 +76,24 @@
 		else
 			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 )
@@ -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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/jabber/sasl.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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/Makefile
===================================================================
--- protocols/msn/Makefile	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/msn/Makefile	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -18,5 +18,5 @@
 all: msn_mod.o
 check: all
-lcov:
+lcov: check
 gcov: 
 	gcov *.c
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/msn/ns.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/msn/sb.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -594,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/oscar/Makefile
===================================================================
--- protocols/oscar/Makefile	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/oscar/Makefile	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -18,5 +18,5 @@
 all: oscar_mod.o
 check: all
-lcov:
+lcov: check
 gcov:
 	gcov *.c
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/oscar/oscar.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -1066,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);
+			}
 		}
 	}
Index: protocols/yahoo/Makefile
===================================================================
--- protocols/yahoo/Makefile	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ protocols/yahoo/Makefile	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -18,5 +18,5 @@
 all: yahoo_mod.o
 check: all
-lcov:
+lcov: check
 gcov: 
 	gcov *.c
Index: root_commands.c
===================================================================
--- root_commands.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ root_commands.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -454,5 +454,5 @@
 	{
 		add_on_server = 0;
-		cmd ++;		/* So evil... :-D */
+		cmd ++;
 	}
 	
@@ -486,10 +486,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]  );
Index: storage_text.c
===================================================================
--- storage_text.c	(revision 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ storage_text.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ storage_xml.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ tests/Makefile	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ tests/check.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ tests/check_arc.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
+++ tests/check_jabber_sasl.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -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 896195002cc903ec4b1ef7e1468f73c1dc08df9e)
+++ unix.c	(revision 4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206)
@@ -48,5 +48,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 )
@@ -126,8 +126,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 )
