Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ bitlbee.h	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -33,5 +33,5 @@
 
 #define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "1.2"
+#define BITLBEE_VERSION "1.2.1"
 #define VERSION BITLBEE_VERSION
 
Index: debian/bitlbee.init
===================================================================
--- debian/bitlbee.init	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ debian/bitlbee.init	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -44,5 +44,4 @@
 	
 	start-stop-daemon --start --quiet --pidfile $PIDFILE \
-		-c bitlbee: \
 		--exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $BITLBEE_OPTS
 }
Index: debian/changelog
===================================================================
--- debian/changelog	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ debian/changelog	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -1,7 +1,16 @@
-bitlbee (1.2-5) UNRELEASED; urgency=low
+bitlbee (1.2-6) UNRELEASED; urgency=low
 
   * Add Homepage and Vcs-Bzr fields.
 
  -- Jelmer Vernooij <jelmer@samba.org>  Sun, 11 May 2008 14:18:16 +0200
+
+bitlbee (1.2-5) unstable; urgency=low
+
+  * Add myself to uploaders.
+  * Bump standards version to 3.8.0.
+  * Fix FSF address.
+  * Avoid changing uid from init script. (Closes: #474589)
+
+ -- Jelmer Vernooij <jelmer@samba.org>  Mon, 16 Jun 2008 00:53:20 +0200
 
 bitlbee (1.2-4) unstable; urgency=low
Index: debian/control
===================================================================
--- debian/control	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ debian/control	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -3,8 +3,10 @@
 Priority: optional
 Maintainer: Wilmer van der Gaast <wilmer@gaast.net>
-Standards-Version: 3.5.9
+Uploaders: Jelmer Vernooij <jelmer@samba.org>
+Standards-Version: 3.8.0
 Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf
 Homepage: http://www.bitlbee.org/
 Vcs-Bzr: http://code.bitlbee.org/bitlbee/
+DM-Upload-Allowed: yes
 
 Package: bitlbee
Index: debian/copyright
===================================================================
--- debian/copyright	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ debian/copyright	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -26,6 +26,6 @@
   You should have received a copy of the GNU General Public License with
   the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-  Suite 330, Boston, MA  02111-1307  USA
+  if not, write to the Free Software Foundation, Inc., 51 Franklin St, 
+  Fifth Floor, Boston, MA 02110-1301, USA.
 ============================================================================
 
@@ -40,5 +40,5 @@
 
  Copyright (C) 2000  Free Software Foundation, Inc.
-     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 	51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
Index: debian/rules
===================================================================
--- debian/rules	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ debian/rules	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -22,5 +22,5 @@
 	[ "`whoami`" = "root" -a -d debian ]
 	rm -rf build-arch-stamp debian/bitlbee debian/*.substvars debian/files debian/bitlbee-dev
-	-$(MAKE) distclean
+	$(MAKE) distclean
 #	-$(MAKE) -C doc/ clean
 				
Index: doc/CHANGES
===================================================================
--- doc/CHANGES	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ doc/CHANGES	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -1,2 +1,7 @@
+This ChangeLog mostly lists changes relevant to users. A full log can be
+found in the bzr commit logs, for example you can try:
+
+http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on
+
 Version 1.2.1:
 - Fixed proxy support.
@@ -10,6 +15,20 @@
   password in your IRC client.
 - Compatible with all crazy kinds of line endings that clients can send.
-
-Finished ...
+- Changed root nicknames are now saved.
+- Added ClientInterface setting to bind() outgoing connections to a specific
+  network interface.
+- Support for receiving Jabber chatroom invitations.
+- Relaxed port restriction of the Jabber module: added ports 80 and 443.
+- Preserving case in Jabber resources of buddies, since these should
+  officially be treated as case sensitive.
+- Fully stripping spaces from AIM screennames, this didn't happen completely
+  which severly breaks the IRC protocol.
+- Removed all the yellow tape around daemon mode, it's pretty mature by now:
+  testing.bitlbee.org serves all (~30) SSL users from one daemon mode
+  process without any serious stability issues.
+- Fixed GLib <2.6 compatibility issue.
+- Misc. memory leak/crash fixes.
+
+Finished 24 Jun 2008
 
 Version 1.2:
Index: ipc.c
===================================================================
--- ipc.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ ipc.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -209,10 +209,10 @@
 }
 
+/* Return just one line. Returns NULL if something broke, an empty string
+   on temporary "errors" (EAGAIN and friends). */
 static char *ipc_readline( int fd )
 {
-	char *buf, *eol;
+	char buf[513], *eol;
 	int size;
-	
-	buf = g_new0( char, 513 );
 	
 	/* Because this is internal communication, it should be pretty safe
@@ -221,5 +221,5 @@
 	   sockets and limites message length, messages should always be
 	   complete. Saves us quite a lot of code and buffering. */
-	size = recv( fd, buf, 512, MSG_PEEK );
+	size = recv( fd, buf, sizeof( buf ) - 1, MSG_PEEK );
 	if( size == 0 || ( size < 0 && !sockerr_again() ) )
 		return NULL;
@@ -229,19 +229,13 @@
 		buf[size] = 0;
 	
-	eol = strstr( buf, "\r\n" );
-	if( eol == NULL )
+	if( ( eol = strstr( buf, "\r\n" ) ) == NULL )
 		return NULL;
 	else
 		size = eol - buf + 2;
-	
-	g_free( buf );
-	buf = g_new0( char, size + 1 );
 	
 	if( recv( fd, buf, size, 0 ) != size )
 		return NULL;
 	else
-		buf[size-2] = 0;
-	
-	return buf;
+		return g_strndup( buf, size - 2 );
 }
 
@@ -254,5 +248,9 @@
 		cmd = irc_parse_line( buf );
 		if( cmd )
+		{
 			ipc_command_exec( data, cmd, ipc_master_commands );
+			g_free( cmd );
+		}
+		g_free( buf );
 	}
 	else
@@ -272,5 +270,9 @@
 		cmd = irc_parse_line( buf );
 		if( cmd )
+		{
 			ipc_command_exec( data, cmd, ipc_child_commands );
+			g_free( cmd );
+		}
+		g_free( buf );
 	}
 	else
Index: lib/misc.c
===================================================================
--- lib/misc.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ lib/misc.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -60,29 +60,4 @@
 	strcpy(text, text2);
 	g_free(text2);
-}
-
-char *normalize(const char *s)
-{
-	static char buf[BUF_LEN];
-	char *t, *u;
-	int x = 0;
-
-	g_return_val_if_fail((s != NULL), NULL);
-
-	u = t = g_strdup(s);
-
-	strcpy(t, s);
-	g_strdown(t);
-
-	while (*t && (x < BUF_LEN - 1)) {
-		if (*t != ' ') {
-			buf[x] = *t;
-			x++;
-		}
-		t++;
-	}
-	buf[x] = '\0';
-	g_free(u);
-	return buf;
 }
 
Index: lib/misc.h
===================================================================
--- lib/misc.h	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ lib/misc.h	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -41,5 +41,4 @@
 G_MODULE_EXPORT char *add_cr( char *text );
 G_MODULE_EXPORT char *strip_newlines(char *source);
-G_MODULE_EXPORT char *normalize( const char *s );
 
 G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );
Index: nick.c
===================================================================
--- nick.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ nick.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -47,4 +47,5 @@
 	
 	store_handle = clean_handle( handle );
+	store_nick[MAX_NICK_LENGTH] = 0;
 	strncpy( store_nick, nick, MAX_NICK_LENGTH );
 	nick_strip( store_nick );
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ protocols/jabber/jabber.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -33,13 +33,31 @@
 #include "jabber.h"
 #include "md5.h"
-#include "base64.h"
 
 GSList *jabber_connections;
 
+/* First enty is the default */
+static const int jabber_port_list[] = {
+	5222,
+	5223,
+	5220,
+	5221,
+	5224,
+	5225,
+	5226,
+	5227,
+	5228,
+	5229,
+	80,
+	443,
+	0
+};
+
 static void jabber_init( account_t *acc )
 {
 	set_t *s;
-	
-	s = set_add( &acc->set, "port", JABBER_PORT_DEFAULT, set_eval_int, acc );
+	char str[16];
+	
+	g_snprintf( str, sizeof( str ), "%d", jabber_port_list[0] );
+	s = set_add( &acc->set, "port", str, set_eval_int, acc );
 	s->flags |= ACC_SET_OFFLINE_ONLY;
 	
@@ -72,4 +90,5 @@
 	struct ns_srv_reply *srv = NULL;
 	char *connect_to, *s;
+	int i;
 	
 	/* For now this is needed in the _connected() handlers if using
@@ -177,9 +196,11 @@
 	imcb_log( ic, "Connecting" );
 	
-	if( set_getint( &acc->set, "port" ) < JABBER_PORT_MIN ||
-	    set_getint( &acc->set, "port" ) > JABBER_PORT_MAX )
-	{
-		imcb_log( ic, "Incorrect port number, must be in the %d-%d range",
-		               JABBER_PORT_MIN, JABBER_PORT_MAX );
+	for( i = 0; jabber_port_list[i] > 0; i ++ )
+		if( set_getint( &acc->set, "port" ) == jabber_port_list[i] )
+			break;
+
+	if( jabber_port_list[i] == 0 )
+	{
+		imcb_log( ic, "Illegal port number" );
 		imc_logout( ic, FALSE );
 		return;
@@ -219,22 +240,18 @@
 }
 
+/* This generates an unfinished md5_state_t variable. Every time we generate
+   an ID, we finish the state by adding a sequence number and take the hash. */
 static void jabber_generate_id_hash( struct jabber_data *jd )
 {
-	md5_state_t id_hash;
-	md5_byte_t binbuf[16];
+	md5_byte_t binbuf[4];
 	char *s;
 	
-	md5_init( &id_hash );
-	md5_append( &id_hash, (unsigned char *) jd->username, strlen( jd->username ) );
-	md5_append( &id_hash, (unsigned char *) jd->server, strlen( jd->server ) );
+	md5_init( &jd->cached_id_prefix );
+	md5_append( &jd->cached_id_prefix, (unsigned char *) jd->username, strlen( jd->username ) );
+	md5_append( &jd->cached_id_prefix, (unsigned char *) jd->server, strlen( jd->server ) );
 	s = set_getstr( &jd->ic->acc->set, "resource" );
-	md5_append( &id_hash, (unsigned char *) s, strlen( s ) );
-	random_bytes( binbuf, 16 );
-	md5_append( &id_hash, binbuf, 16 );
-	md5_finish( &id_hash, binbuf );
-	
-	s = base64_encode( binbuf, 9 );
-	jd->cached_id_prefix = g_strdup_printf( "%s%s", JABBER_CACHED_ID, s );
-	g_free( s );
+	md5_append( &jd->cached_id_prefix, (unsigned char *) s, strlen( s ) );
+	random_bytes( binbuf, 4 );
+	md5_append( &jd->cached_id_prefix, binbuf, 4 );
 }
 
@@ -267,5 +284,4 @@
 	xt_free( jd->xt );
 	
-	g_free( jd->cached_id_prefix );
 	g_free( jd->away_message );
 	g_free( jd->username );
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ protocols/jabber/jabber.h	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -86,5 +86,5 @@
 	char *away_message;
 	
-	char *cached_id_prefix;
+	md5_state_t cached_id_prefix;
 	GHashTable *node_cache;
 	GHashTable *buddies;
@@ -134,8 +134,4 @@
 
 #define JABBER_XMLCONSOLE_HANDLE "xmlconsole"
-
-#define JABBER_PORT_DEFAULT "5222"
-#define JABBER_PORT_MIN 5220
-#define JABBER_PORT_MAX 5229
 
 /* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ protocols/jabber/jabber_util.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -23,4 +23,6 @@
 
 #include "jabber.h"
+#include "md5.h"
+#include "base64.h"
 
 static unsigned int next_id = 1;
@@ -134,9 +136,19 @@
 	struct jabber_data *jd = ic->proto_data;
 	struct jabber_cache_entry *entry = g_new0( struct jabber_cache_entry, 1 );
-	char *id;
-	
-	id = g_strdup_printf( "%s%05x", jd->cached_id_prefix, ( next_id++ ) & 0xfffff );
+	md5_state_t id_hash;
+	md5_byte_t id_sum[16];
+	char *id, *asc_hash;
+	
+	next_id ++;
+	
+	id_hash = jd->cached_id_prefix;
+	md5_append( &id_hash, (md5_byte_t*) &next_id, sizeof( next_id ) );
+	md5_finish( &id_hash, id_sum );
+	asc_hash = base64_encode( id_sum, 12 );
+	
+	id = g_strdup_printf( "%s%s", JABBER_CACHED_ID, asc_hash );
 	xt_add_attr( node, "id", id );
 	g_free( id );
+	g_free( asc_hash );
 	
 	entry->node = node;
@@ -184,5 +196,5 @@
 	
 	if( ( s = xt_find_attr( node, "id" ) ) == NULL ||
-	    strncmp( s, jd->cached_id_prefix, strlen( jd->cached_id_prefix ) ) != 0 )
+	    strncmp( s, JABBER_CACHED_ID, strlen( JABBER_CACHED_ID ) ) != 0 )
 	{
 		/* Silently ignore it, without an ID (or a non-cache
@@ -196,6 +208,12 @@
 	if( entry == NULL )
 	{
+		/*
+		There's no longer an easy way to see if we generated this
+		one or someone else, and there's a ten-minute timeout anyway,
+		so meh.
+		
 		imcb_log( ic, "Warning: Received %s-%s packet with unknown/expired ID %s!",
 		              node->name, xt_find_attr( node, "type" ) ? : "(no type)", s );
+		*/
 	}
 	else if( entry->func )
@@ -290,6 +308,11 @@
 	len = strlen( orig );
 	new = g_new( char, len + 1 );
-	for( i = 0; i < len; i ++ )
+	
+	/* So it turns out the /resource part is case sensitive. Yeah, and
+	   it's Unicode but feck Unicode. :-P So stop once we see a slash. */
+	for( i = 0; i < len && orig[i] != '/' ; i ++ )
 		new[i] = tolower( orig[i] );
+	for( ; orig[i]; i ++ )
+		new[i] = orig[i];
 	
 	new[i] = 0;
@@ -334,5 +357,5 @@
 		{
 			/* Check for dupes. */
-			if( g_strcasecmp( bi->resource, s + 1 ) == 0 )
+			if( strcmp( bi->resource, s + 1 ) == 0 )
 			{
 				*s = '/';
@@ -387,5 +410,5 @@
 	if( ( s = strchr( jid, '/' ) ) )
 	{
-		int none_found = 0;
+		int bare_exists = 0;
 		
 		*s = 0;
@@ -410,19 +433,17 @@
 			/* See if there's an exact match. */
 			for( ; bud; bud = bud->next )
-				if( g_strcasecmp( bud->resource, s + 1 ) == 0 )
+				if( strcmp( bud->resource, s + 1 ) == 0 )
 					break;
 		}
 		else
 		{
-			/* This hack is there to make sure that O_CREAT will
-			   work if there's already another resouce present
-			   for this JID, even if it's an unknown buddy. This
-			   is done to handle conferences properly. */
-			none_found = 1;
-			/* TODO(wilmer): Find out what I was thinking when I
-			   wrote this??? And then fix it. This makes me sad... */
-		}
-		
-		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && ( imcb_find_buddy( ic, jid ) || !none_found ) )
+			/* This variable tells the if down here that the bare
+			   JID already exists and we should feel free to add
+			   more resources, if the caller asked for that. */
+			bare_exists = 1;
+		}
+		
+		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) &&
+		    ( !bare_exists || imcb_find_buddy( ic, jid ) ) )
 		{
 			*s = '/';
@@ -449,5 +470,5 @@
 			/* We want an exact match, so in thise case there shouldn't be a /resource. */
 			return NULL;
-		else if( ( bud->resource == NULL || bud->next == NULL ) )
+		else if( bud->resource == NULL || bud->next == NULL )
 			/* No need for selection if there's only one option. */
 			return bud;
@@ -525,5 +546,7 @@
 		   matches), removing it is simple. (And the hash reference
 		   should be removed too!) */
-		if( bud->next == NULL && ( ( s == NULL || bud->resource == NULL ) || g_strcasecmp( bud->resource, s + 1 ) == 0 ) )
+		if( bud->next == NULL &&
+		    ( ( s == NULL && bud->resource == NULL ) ||
+		      ( bud->resource && s && strcmp( bud->resource, s + 1 ) == 0 ) ) )
 		{
 			g_hash_table_remove( jd->buddies, bud->bare_jid );
@@ -548,5 +571,5 @@
 		{
 			for( bi = bud, prev = NULL; bi; bi = (prev=bi)->next )
-				if( g_strcasecmp( bi->resource, s + 1 ) == 0 )
+				if( strcmp( bi->resource, s + 1 ) == 0 )
 					break;
 			
Index: protocols/jabber/message.c
===================================================================
--- protocols/jabber/message.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ protocols/jabber/message.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -49,4 +49,21 @@
 	{
 		GString *fullmsg = g_string_new( "" );
+
+		for( c = node->children; ( c = xt_find_node( c, "x" ) ); c = c->next )
+		{
+			char *ns = xt_find_attr( c, "xmlns" ), *room;
+			struct xt_node *inv, *reason;
+			
+			if( strcmp( ns, XMLNS_MUC_USER ) == 0 &&
+			    ( inv = xt_find_node( c->children, "invite" ) ) )
+			{
+				room = from;
+				from = xt_find_attr( inv, "from" ) ? : from;
+
+				g_string_append_printf( fullmsg, "<< \002BitlBee\002 - Invitation to chatroom %s >>\n", room );
+				if( ( reason = xt_find_node( inv->children, "reason" ) ) && reason->text_len > 0 )
+					g_string_append( fullmsg, reason->text );
+			}
+		}
 		
 		if( ( s = strchr( from, '/' ) ) )
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ protocols/nogaim.h	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -44,9 +44,4 @@
 #include "query.h"
 #include "md5.h"
-
-#define BUF_LEN MSG_LEN
-#define BUF_LONG ( BUF_LEN * 2 )
-#define MSG_LEN 2048
-#define BUF_LEN MSG_LEN
 
 #define BUDDY_ALIAS_MAXLEN 388   /* because MSN names can be 387 characters */
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ protocols/oscar/oscar.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -60,4 +60,7 @@
 
 #define OSCAR_GROUP "Friends"
+
+#define BUF_LEN 2048
+#define BUF_LONG ( BUF_LEN * 2 )
 
 /* Don't know if support for UTF8 is really working. For now it's UTF16 here.
@@ -240,4 +243,30 @@
 };
 static int msgerrreasonlen = 25;
+
+/* Hurray, this function is NOT thread-safe \o/ */
+static char *normalize(const char *s)
+{
+	static char buf[BUF_LEN];
+	char *t, *u;
+	int x = 0;
+
+	g_return_val_if_fail((s != NULL), NULL);
+
+	u = t = g_strdup(s);
+
+	strcpy(t, s);
+	g_strdown(t);
+
+	while (*t && (x < BUF_LEN - 1)) {
+		if (*t != ' ' && *t != '!') {
+			buf[x] = *t;
+			x++;
+		}
+		t++;
+	}
+	buf[x] = '\0';
+	g_free(u);
+	return buf;
+}
 
 static gboolean oscar_callback(gpointer data, gint source,
@@ -1002,11 +1031,11 @@
 	}
 
-	tmp = g_strdup(normalize(ic->acc->user));
-	if (!strcmp(tmp, normalize(info->sn)))
+	if (!aim_sncmp(ic->acc->user, info->sn))
 		g_snprintf(ic->displayname, sizeof(ic->displayname), "%s", info->sn);
-	g_free(tmp);
-
-	imcb_buddy_status(ic, info->sn, flags, state_string, NULL);
-	/* imcb_buddy_times(ic, info->sn, signon, time_idle); */
+
+	tmp = normalize(info->sn);
+	imcb_buddy_status(ic, tmp, flags, state_string, NULL);
+	/* imcb_buddy_times(ic, tmp, signon, time_idle); */
+
 
 	return 1;
@@ -1022,5 +1051,5 @@
 	va_end(ap);
 
-	imcb_buddy_status(ic, info->sn, 0, NULL, NULL );
+	imcb_buddy_status(ic, normalize(info->sn), 0, NULL, NULL );
 
 	return 1;
@@ -1078,5 +1107,5 @@
 	
 	strip_linefeed(tmp);
-	imcb_buddy_msg(ic, userinfo->sn, tmp, flags, 0);
+	imcb_buddy_msg(ic, normalize(userinfo->sn), tmp, flags, 0);
 	g_free(tmp);
 	
@@ -1177,5 +1206,5 @@
 			message = g_strdup(args->msg);
 			strip_linefeed(message);
-			imcb_buddy_msg(ic, uin, message, 0, 0);
+			imcb_buddy_msg(ic, normalize(uin), message, 0, 0);
 			g_free(uin);
 			g_free(message);
@@ -1196,5 +1225,5 @@
 
 			strip_linefeed(message);
-			imcb_buddy_msg(ic, uin, message, 0, 0);
+			imcb_buddy_msg(ic, normalize(uin), message, 0, 0);
 			g_free(uin);
 			g_free(m);
@@ -1471,5 +1500,5 @@
 
 	for (i = 0; i < count; i++)
-		imcb_chat_add_buddy(c->cnv, info[i].sn);
+		imcb_chat_add_buddy(c->cnv, normalize(info[i].sn));
 
 	return 1;
@@ -1494,5 +1523,5 @@
 
 	for (i = 0; i < count; i++)
-		imcb_chat_remove_buddy(c->cnv, info[i].sn, NULL);
+		imcb_chat_remove_buddy(c->cnv, normalize(info[i].sn), NULL);
 
 	return 1;
@@ -1545,5 +1574,5 @@
 	tmp = g_malloc(BUF_LONG);
 	g_snprintf(tmp, BUF_LONG, "%s", msg);
-	imcb_chat_msg(ccon->cnv, info->sn, tmp, 0, 0);
+	imcb_chat_msg(ccon->cnv, normalize(info->sn), tmp, 0, 0);
 	g_free(tmp);
 
@@ -1758,5 +1787,5 @@
 			g_snprintf(sender, sizeof(sender), "%u", msg->sender);
 			strip_linefeed(dialog_msg);
-			imcb_buddy_msg(ic, sender, dialog_msg, 0, t);
+			imcb_buddy_msg(ic, normalize(sender), dialog_msg, 0, t);
 			g_free(dialog_msg);
 		} break;
@@ -1779,5 +1808,5 @@
 
 			strip_linefeed(dialog_msg);
-			imcb_buddy_msg(ic, sender, dialog_msg, 0, t);
+			imcb_buddy_msg(ic, normalize(sender), dialog_msg, 0, t);
 			g_free(dialog_msg);
 			g_free(m);
@@ -2017,11 +2046,14 @@
 	struct aim_ssi_item *curitem;
 	int tmp;
+	char *nrm;
 
 	/* Add from server list to local list */
 	tmp = 0;
 	for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
+		nrm = curitem->name ? normalize(curitem->name) : NULL;
+		
 		switch (curitem->type) {
 			case 0x0000: /* Buddy */
-				if ((curitem->name) && (!imcb_find_buddy(ic, curitem->name))) {
+				if ((curitem->name) && (!imcb_find_buddy(ic, nrm))) {
 					char *realname = NULL;
 
@@ -2029,9 +2061,9 @@
 						    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
 						
-					imcb_add_buddy(ic, curitem->name, NULL);
+					imcb_add_buddy(ic, nrm, NULL);
 					
 					if (realname) {
-						imcb_buddy_nick_hint(ic, curitem->name, realname);
-						imcb_rename_buddy(ic, curitem->name, realname);
+						imcb_buddy_nick_hint(ic, nrm, realname);
+						imcb_rename_buddy(ic, nrm, realname);
 						g_free(realname);
 					}
@@ -2045,5 +2077,5 @@
 					if (!list) {
 						char *name;
-						name = g_strdup(normalize(curitem->name));
+						name = g_strdup(nrm);
 						ic->permit = g_slist_append(ic->permit, name);
 						tmp++;
@@ -2058,5 +2090,5 @@
 					if (!list) {
 						char *name;
-						name = g_strdup(normalize(curitem->name));
+						name = g_strdup(nrm);
 						ic->deny = g_slist_append(ic->deny, name);
 						tmp++;
@@ -2120,5 +2152,5 @@
 			if( st == 0x00 )
 			{
-				imcb_add_buddy( sess->aux_data, list, NULL );
+				imcb_add_buddy( sess->aux_data, normalize(list), NULL );
 			}
 			else if( st == 0x0E )
@@ -2450,13 +2482,13 @@
 	if(type2 == 0x0002) {
 		/* User is typing */
-		imcb_buddy_typing(ic, sn, OPT_TYPING);
+		imcb_buddy_typing(ic, normalize(sn), OPT_TYPING);
 	} 
 	else if (type2 == 0x0001) {
 		/* User has typed something, but is not actively typing (stale) */
-		imcb_buddy_typing(ic, sn, OPT_THINKING);
+		imcb_buddy_typing(ic, normalize(sn), OPT_THINKING);
 	}
 	else {
 		/* User has stopped typing */
-		imcb_buddy_typing(ic, sn, 0);
+		imcb_buddy_typing(ic, normalize(sn), 0);
 	}
 	
Index: protocols/yahoo/libyahoo2.c
===================================================================
--- protocols/yahoo/libyahoo2.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ protocols/yahoo/libyahoo2.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -379,5 +379,4 @@
 
 /* call repeatedly to get the next one */
-/*
 static struct yahoo_input_data * find_input_by_id(int id)
 {
@@ -390,5 +389,4 @@
 	return NULL;
 }
-*/
 
 static struct yahoo_input_data * find_input_by_id_and_webcam_user(int id, const char * who)
@@ -795,4 +793,5 @@
 {
 	struct yahoo_data *yd = find_conn_by_id(id);
+	
 	if(!yd)
 		return;
@@ -3164,5 +3163,5 @@
 
 	LOG(("write callback: id=%d fd=%d data=%p", id, fd, data));
-	if(!yid || !yid->txqueues)
+	if(!yid || !yid->txqueues || !find_conn_by_id(id))
 		return -2;
 	
@@ -3840,9 +3839,7 @@
 	}
 
-	
-/*	do {
+	do {
 		yahoo_input_close(yid);
-	} while((yid = find_input_by_id(id)));*/
-	
+	} while((yid = find_input_by_id(id)));
 }
 
Index: protocols/yahoo/yahoo.c
===================================================================
--- protocols/yahoo/yahoo.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ protocols/yahoo/yahoo.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -163,8 +163,5 @@
 	g_slist_free( yd->buddygroups );
 	
-	if( yd->logged_in )
-		yahoo_logoff( yd->y2_id );
-	else
-		yahoo_close( yd->y2_id );
+	yahoo_logoff( yd->y2_id );
 	
 	g_free( yd );
@@ -454,8 +451,4 @@
 	struct byahoo_write_ready_data *d = data;
 	
-	if( !byahoo_get_ic_by_id( d->id ) )
-		/* WTF doesn't libyahoo clean this up? */
-		return FALSE;
-	
 	yahoo_write_ready( d->id, d->fd, d->data );
 	
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ storage_xml.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -481,5 +481,5 @@
 static storage_status_t xml_remove( const char *nick, const char *password )
 {
-	char s[512];
+	char s[512], *lc;
 	storage_status_t status;
 
@@ -488,5 +488,9 @@
 		return status;
 
-	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".xml" );
+	lc = g_strdup( nick );
+	nick_lc( lc );
+	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, lc, ".xml" );
+	g_free( lc );
+	
 	if( unlink( s ) == -1 )
 		return STORAGE_OTHER_ERROR;
Index: tests/Makefile
===================================================================
--- tests/Makefile	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ tests/Makefile	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -13,5 +13,5 @@
 main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o
 
-test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o check_jabber_sasl.o
+test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o check_jabber_sasl.o check_jabber_util.o
 
 check: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../lib/lib.o
Index: tests/check.c
===================================================================
--- tests/check.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ tests/check.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -69,4 +69,7 @@
 Suite *jabber_sasl_suite(void);
 
+/* From check_jabber_sasl.c */
+Suite *jabber_util_suite(void);
+
 int main (int argc, char **argv)
 {
@@ -115,4 +118,5 @@
 	srunner_add_suite(sr, set_suite());
 	srunner_add_suite(sr, jabber_sasl_suite());
+	srunner_add_suite(sr, jabber_util_suite());
 	if (no_fork)
 		srunner_set_fork_status(sr, CK_NOFORK);
Index: tests/check_jabber_sasl.c
===================================================================
--- tests/check_jabber_sasl.c	(revision 2e0f24d00b85c617400413bb2d179fd1028420c4)
+++ tests/check_jabber_sasl.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -5,5 +5,4 @@
 #include <string.h>
 #include <stdio.h>
-#include "arc.h"
 
 char *sasl_get_part( char *data, char *field );
Index: tests/check_jabber_util.c
===================================================================
--- tests/check_jabber_util.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
+++ tests/check_jabber_util.c	(revision 178e2f8f71e5ebd4501f455c874f816b9ba19ade)
@@ -0,0 +1,91 @@
+#include <stdlib.h>
+#include <glib.h>
+#include <gmodule.h>
+#include <check.h>
+#include <string.h>
+#include <stdio.h>
+#include "jabber/jabber.h"
+
+static struct im_connection *ic;
+
+static void check_buddy_add(int l)
+{
+	struct jabber_buddy *budw1, *budw2, *budw3, *budn, *bud;
+	
+	budw1 = jabber_buddy_add( ic, "wilmer@gaast.net/BitlBee" );
+	budw1->last_act = time( NULL ) - 100;
+	budw2 = jabber_buddy_add( ic, "WILMER@gaast.net/Telepathy" );
+	budw2->priority = 2;
+	budw2->last_act = time( NULL );
+	budw3 = jabber_buddy_add( ic, "wilmer@GAAST.NET/bitlbee" );
+	budw3->last_act = time( NULL ) - 200;
+	budw3->priority = 4;
+	/* TODO(wilmer): Shouldn't this just return budw3? */
+	fail_if( jabber_buddy_add( ic, "wilmer@gaast.net/Telepathy" ) != NULL );
+	
+	budn = jabber_buddy_add( ic, "nekkid@lamejab.net" );
+	/* Shouldn't be allowed if there's already a bare JID. */
+	fail_if( jabber_buddy_add( ic, "nekkid@lamejab.net/Illegal" ) );
+	
+	/* Case sensitivity: Case only matters after the / */
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@gaast.net/BitlBee", 0 ) ==
+	         jabber_buddy_by_jid( ic, "wilmer@gaast.net/bitlbee", 0 ) );
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@gaast.net/telepathy", 0 ) );
+	
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@gaast.net/BitlBee", 0 ) == budw1 );
+	fail_unless( jabber_buddy_by_jid( ic, "WILMER@GAAST.NET/BitlBee", GET_BUDDY_EXACT ) == budw1 );
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@GAAST.NET/BitlBee", GET_BUDDY_CREAT ) == budw1 );
+
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@gaast.net", GET_BUDDY_EXACT ) );
+	fail_unless( jabber_buddy_by_jid( ic, "WILMER@gaast.net", 0 ) == budw3 );
+
+	/* Check O_FIRST and see if it's indeed the first item from the list. */
+	fail_unless( ( bud = jabber_buddy_by_jid( ic, "wilmer@gaast.net", GET_BUDDY_FIRST ) ) == budw1 );
+	fail_unless( bud->next == budw2 && bud->next->next == budw3 && bud->next->next->next == NULL );
+	
+	/* Change the resource_select setting, now we should get a different resource. */
+	set_setstr( &ic->acc->set, "resource_select", "activity" );
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@GAAST.NET", 0 ) == budw2 );
+	
+	/* Some testing of bare JID handling (which is horrible). */
+	fail_if( jabber_buddy_by_jid( ic, "nekkid@lamejab.net/Illegal", 0 ) );
+	fail_if( jabber_buddy_by_jid( ic, "NEKKID@LAMEJAB.NET/Illegal", GET_BUDDY_CREAT ) );
+	fail_unless( jabber_buddy_by_jid( ic, "nekkid@lamejab.net", 0 ) == budn );
+	fail_unless( jabber_buddy_by_jid( ic, "NEKKID@lamejab.net", GET_BUDDY_EXACT ) == budn );
+	fail_unless( jabber_buddy_by_jid( ic, "nekkid@LAMEJAB.NET", GET_BUDDY_CREAT ) == budn );
+	
+	/* More case sensitivity testing, and see if remove works properly. */
+	fail_if( jabber_buddy_remove( ic, "wilmer@gaast.net/telepathy" ) );
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@GAAST.NET/telepathy", GET_BUDDY_CREAT ) == budw2 );
+	fail_unless( jabber_buddy_remove( ic, "wilmer@gaast.net/Telepathy" ) );
+	fail_unless( jabber_buddy_remove( ic, "wilmer@gaast.net/telepathy" ) );
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@gaast.net", 0 ) == budw1 );
+	
+	fail_if( jabber_buddy_remove( ic, "wilmer@gaast.net" ) );
+	fail_unless( jabber_buddy_by_jid( ic, "wilmer@gaast.net", 0 ) == budw1 );
+	
+	/* Check if remove_bare() indeed gets rid of all. */
+	fail_unless( jabber_buddy_remove_bare( ic, "wilmer@gaast.net" ) );
+	fail_if( jabber_buddy_by_jid( ic, "wilmer@gaast.net", 0 ) );
+
+	fail_if( jabber_buddy_remove( ic, "nekkid@lamejab.net/Illegal" ) );
+	fail_unless( jabber_buddy_remove( ic, "nekkid@lamejab.net" ) );
+	fail_if( jabber_buddy_by_jid( ic, "nekkid@lamejab.net", 0 ) );
+}
+
+Suite *jabber_util_suite (void)
+{
+	Suite *s = suite_create("jabber/util");
+	TCase *tc_core = tcase_create("Buddy");
+	struct jabber_data *jd;
+	
+	ic = g_new0( struct im_connection, 1 );
+	ic->acc = g_new0( account_t, 1 );
+	ic->proto_data = jd = g_new0( struct jabber_data, 1 );
+	jd->buddies = g_hash_table_new( g_str_hash, g_str_equal );
+	set_add( &ic->acc->set, "resource_select", "priority", NULL, ic->acc );
+	
+	suite_add_tcase (s, tc_core);
+	tcase_add_test (tc_core, check_buddy_add);
+	return s;
+}
