Index: Makefile
===================================================================
--- Makefile	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ Makefile	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -10,5 +10,6 @@
 
 # Program variables
-objects = account.o bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o
+#objects = bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS)
+objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o nick.o root_commands.o set.o storage.o $(STORAGE_OBJS)
 headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h
 subdirs = lib protocols
Index: count.c
===================================================================
--- account.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ 	(revision )
@@ -1,359 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Account management functions                                         */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-  Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include "account.h"
-#include "chat.h"
-
-account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass )
-{
-	account_t *a;
-	set_t *s;
-	
-	if( irc->accounts )
-	{
-		for( a = irc->accounts; a->next; a = a->next );
-		a = a->next = g_new0( account_t, 1 );
-	}
-	else
-	{
-		irc->accounts = a = g_new0 ( account_t, 1 );
-	}
-	
-	a->prpl = prpl;
-	a->user = g_strdup( user );
-	a->pass = g_strdup( pass );
-	a->auto_connect = 1;
-	a->irc = irc;
-	
-	s = set_add( &a->set, "auto_connect", "true", set_eval_account, a );
-	s->flags |= ACC_SET_NOSAVE;
-	
-	s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a );
-	
-	s = set_add( &a->set, "nick_source", "handle", NULL, a );
-	
-	s = set_add( &a->set, "password", NULL, set_eval_account, a );
-	s->flags |= ACC_SET_NOSAVE | SET_NULL_OK;
-	
-	s = set_add( &a->set, "username", NULL, set_eval_account, a );
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
-	set_setstr( &a->set, "username", user );
-	
-	a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free );
-	
-	/* This function adds some more settings (and might want to do more
-	   things that have to be done now, although I can't think of anything. */
-	if( prpl->init )
-		prpl->init( a );
-	
-	s = set_add( &a->set, "away", NULL, set_eval_account, a );
-	s->flags |= SET_NULL_OK;
-	
-	if( a->flags & ACC_FLAG_STATUS_MESSAGE )
-	{
-		s = set_add( &a->set, "status", NULL, set_eval_account, a );
-		s->flags |= SET_NULL_OK;
-	}
-	
-	return a;
-}
-
-char *set_eval_account( set_t *set, char *value )
-{
-	account_t *acc = set->data;
-	
-	/* Double-check: We refuse to edit on-line accounts. */
-	if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic )
-		return SET_INVALID;
-	
-	if( strcmp( set->key, "server" ) == 0 )
-	{
-		g_free( acc->server );
-		if( value && *value )
-		{
-			acc->server = g_strdup( value );
-			return value;
-		}
-		else
-		{
-			acc->server = g_strdup( set->def );
-			return g_strdup( set->def );
-		}
-	}
-	else if( strcmp( set->key, "username" ) == 0 )
-	{
-		g_free( acc->user );
-		acc->user = g_strdup( value );
-		return value;
-	}
-	else if( strcmp( set->key, "password" ) == 0 )
-	{
-		if( value )
-		{
-			g_free( acc->pass );
-			acc->pass = g_strdup( value );
-			return NULL;	/* password shouldn't be visible in plaintext! */
-		}
-		else
-		{
-			/* NULL can (should) be stored in the set_t
-			   variable, but is otherwise not correct. */
-			return SET_INVALID;
-		}
-	}
-	else if( strcmp( set->key, "auto_connect" ) == 0 )
-	{
-		if( !is_bool( value ) )
-			return SET_INVALID;
-		
-		acc->auto_connect = bool2int( value );
-		return value;
-	}
-	else if( strcmp( set->key, "away" ) == 0 ||
-	         strcmp( set->key, "status" ) == 0 )
-	{
-		if( acc->ic && acc->ic->flags & OPT_LOGGED_IN )
-		{
-			/* If we're currently on-line, set the var now already
-			   (bit of a hack) and send an update. */
-			g_free( set->value );
-			set->value = g_strdup( value );
-			
-			imc_away_send_update( acc->ic );
-		}
-		
-		return value;
-	}
-	
-	return SET_INVALID;
-}
-
-account_t *account_get( irc_t *irc, char *id )
-{
-	account_t *a, *ret = NULL;
-	char *handle, *s;
-	int nr;
-	
-	/* This checks if the id string ends with (...) */
-	if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 )
-	{
-		struct prpl *proto;
-		
-		*s = *handle = 0;
-		handle ++;
-		
-		if( ( proto = find_protocol( id ) ) )
-		{
-			for( a = irc->accounts; a; a = a->next )
-				if( a->prpl == proto &&
-				    a->prpl->handle_cmp( handle, a->user ) == 0 )
-					ret = a;
-		}
-		
-		/* Restore the string. */
-		handle --;
-		*handle = '(';
-		*s = ')';
-		
-		if( ret )
-			return ret;
-	}
-	
-	if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 )
-	{
-		for( a = irc->accounts; a; a = a->next )
-			if( ( nr-- ) == 0 )
-				return( a );
-		
-		return( NULL );
-	}
-	
-	for( a = irc->accounts; a; a = a->next )
-	{
-		if( g_strcasecmp( id, a->prpl->name ) == 0 )
-		{
-			if( !ret )
-				ret = a;
-			else
-				return( NULL ); /* We don't want to match more than one... */
-		}
-		else if( strstr( a->user, id ) )
-		{
-			if( !ret )
-				ret = a;
-			else
-				return( NULL );
-		}
-	}
-	
-	return( ret );
-}
-
-void account_del( irc_t *irc, account_t *acc )
-{
-	account_t *a, *l = NULL;
-	struct chat *c, *nc;
-	
-	if( acc->ic )
-		/* Caller should have checked, accounts still in use can't be deleted. */
-		return;
-	
-	for( a = irc->accounts; a; a = (l=a)->next )
-		if( a == acc )
-		{
-			if( l )
-				l->next = a->next;
-			else
-				irc->accounts = a->next;
-			
-			for( c = irc->chatrooms; c; c = nc )
-			{
-				nc = c->next;
-				if( acc == c->acc )
-					chat_del( irc, c );
-			}
-			
-			while( a->set )
-				set_del( &a->set, a->set->key );
-			
-			g_hash_table_destroy( a->nicks );
-			
-			g_free( a->user );
-			g_free( a->pass );
-			g_free( a->server );
-			if( a->reconnect )	/* This prevents any reconnect still queued to happen */
-				cancel_auto_reconnect( a );
-			g_free( a );
-			
-			break;
-		}
-}
-
-void account_on( irc_t *irc, account_t *a )
-{
-	if( a->ic )
-	{
-		/* Trying to enable an already-enabled account */
-		return;
-	}
-	
-	cancel_auto_reconnect( a );
-	
-	a->reconnect = 0;
-	a->prpl->login( a );
-}
-
-void account_off( irc_t *irc, account_t *a )
-{
-	imc_logout( a->ic, FALSE );
-	a->ic = NULL;
-	if( a->reconnect )
-	{
-		/* Shouldn't happen */
-		cancel_auto_reconnect( a );
-	}
-}
-
-struct account_reconnect_delay
-{
-	int start;
-	char op;
-	int step;
-	int max;
-};
-
-int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p )
-{
-	memset( p, 0, sizeof( *p ) );
-	/* A whole day seems like a sane "maximum maximum". */
-	p->max = 86400;
-	
-	/* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
-	while( *value && isdigit( *value ) )
-		p->start = p->start * 10 + *value++ - '0';
-	
-	/* Sure, call me evil for implementing my own fscanf here, but it's
-	   dead simple and I immediately know where to continue parsing. */
-	
-	if( *value == 0 )
-		/* If the string ends now, the delay is constant. */
-		return 1;
-	else if( *value != '+' && *value != '*' )
-		/* Otherwise allow either a + or a * */
-		return 0;
-	
-	p->op = *value++;
-	
-	/* + or * the delay by this number every time. */
-	while( *value && isdigit( *value ) )
-		p->step = p->step * 10 + *value++ - '0';
-	
-	if( *value == 0 )
-		/* Use the default maximum (one day). */
-		return 1;
-	else if( *value != '<' )
-		return 0;
-	
-	p->max = 0;
-	value ++;
-	while( *value && isdigit( *value ) )
-		p->max = p->max * 10 + *value++ - '0';
-	
-	return p->max > 0;
-}
-
-char *set_eval_account_reconnect_delay( set_t *set, char *value )
-{
-	struct account_reconnect_delay p;
-	
-	return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID;
-}
-
-int account_reconnect_delay( account_t *a )
-{
-	char *setting = set_getstr( &a->irc->set, "auto_reconnect_delay" );
-	struct account_reconnect_delay p;
-	
-	if( account_reconnect_delay_parse( setting, &p ) )
-	{
-		if( a->auto_reconnect_delay == 0 )
-			a->auto_reconnect_delay = p.start;
-		else if( p.op == '+' )
-			a->auto_reconnect_delay += p.step;
-		else if( p.op == '*' )
-			a->auto_reconnect_delay *= p.step;
-		
-		if( a->auto_reconnect_delay > p.max )
-			a->auto_reconnect_delay = p.max;
-	}
-	else
-	{
-		a->auto_reconnect_delay = 0;
-	}
-	
-	return a->auto_reconnect_delay;
-}
Index: count.h
===================================================================
--- account.h	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ 	(revision )
@@ -1,72 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2004 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Account management functions                                         */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-  Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#ifndef _ACCOUNT_H
-#define _ACCOUNT_H
-
-typedef struct account
-{
-	struct prpl *prpl;
-	char *user;
-	char *pass;
-	char *server;
-	
-	int auto_connect;
-	int auto_reconnect_delay;
-	int reconnect;
-	int flags;
-	
-	set_t *set;
-	GHashTable *nicks;
-	
-	struct irc *irc;
-	struct im_connection *ic;
-	struct account *next;
-} account_t;
-
-account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass );
-account_t *account_get( irc_t *irc, char *id );
-void account_del( irc_t *irc, account_t *acc );
-void account_on( irc_t *irc, account_t *a );
-void account_off( irc_t *irc, account_t *a );
-
-char *set_eval_account( set_t *set, char *value );
-char *set_eval_account_reconnect_delay( set_t *set, char *value );
-int account_reconnect_delay( account_t *a );
-
-typedef enum
-{
-	ACC_SET_NOSAVE = 0x01,          /* Don't save this setting (i.e. stored elsewhere). */
-	ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
-	ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */
-} account_set_flag_t;
-
-typedef enum
-{
-	ACC_FLAG_AWAY_MESSAGE = 0x01,   /* Supports away messages instead of just states. */
-	ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */
-} account_flag_t;
-
-#endif
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ bitlbee.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -124,4 +124,5 @@
 #define CONF_FILE_DEF ETCDIR "bitlbee.conf"
 
+#include "bee.h"
 #include "irc.h"
 #include "storage.h"
@@ -158,5 +159,5 @@
 gboolean bitlbee_io_current_client_write( gpointer data, gint source, b_input_condition cond );
 
-void root_command_string( irc_t *irc, user_t *u, char *command, int flags );
+void root_command_string( irc_t *irc, char *command );
 void root_command( irc_t *irc, char *command[] );
 gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond );
Index: at.c
===================================================================
--- chat.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ 	(revision )
@@ -1,192 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2008 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Keep track of chatrooms the user is interested in                    */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-  Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#include "bitlbee.h"
-#include "chat.h"
-
-struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel )
-{
-	struct chat *c, *l;
-	set_t *s;
-
-	if( acc->prpl->chat_join == NULL || !chat_chanok( channel ) ||
-	    chat_chancmp( channel, irc->channel ) == 0 )
-	{
-		return NULL;
-	}
-	
-	for( c = irc->chatrooms; c; c = c->next )
-	{
-		if( chat_chancmp( channel, c->channel ) == 0 )
-			return NULL;
-		
-		if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 )
-			return NULL;
-		
-		l = c;
-	}
-	
-	if( irc->chatrooms == NULL )
-		irc->chatrooms = c = g_new0( struct chat, 1 );
-	else
-		l->next = c = g_new0( struct chat, 1 );
-	
-	c->acc = acc;
-	c->handle = g_strdup( handle );
-	c->channel = g_strdup( channel );
-	
-	s = set_add( &c->set, "auto_join", "false", set_eval_bool, c );
-	/* s = set_add( &c->set, "auto_rejoin", "false", set_eval_bool, c ); */
-	s = set_add( &c->set, "nick", NULL, NULL, c );
-	s->flags |= SET_NULL_OK;
-	
-	return c;
-}
-
-struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle )
-{
-	struct chat *c;
-	
-	for( c = irc->chatrooms; c; c = c->next )
-	{
-		if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 )
-			break;
-	}
-	
-	return c;
-}
-
-struct chat *chat_bychannel( irc_t *irc, char *channel )
-{
-	struct chat *c;
-	
-	for( c = irc->chatrooms; c; c = c->next )
-	{
-		if( chat_chancmp( channel, c->channel ) == 0 )
-			break;
-	}
-	
-	return c;
-}
-
-struct chat *chat_get( irc_t *irc, char *id )
-{
-	struct chat *c, *ret = NULL;
-	int nr;
-	
-	if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 )
-	{
-		for( c = irc->chatrooms; c; c = c->next )
-			if( ( nr-- ) == 0 )
-				return c;
-		
-		return NULL;
-	}
-	
-	for( c = irc->chatrooms; c; c = c->next )
-	{
-		if( strstr( c->handle, id ) )
-		{
-			if( !ret )
-				ret = c;
-			else
-				return NULL;
-		}
-		else if( strstr( c->channel, id ) )
-		{
-			if( !ret )
-				ret = c;
-			else
-				return NULL;
-		}
-	}
-	
-	return ret;
-}
-
-int chat_del( irc_t *irc, struct chat *chat )
-{
-	struct chat *c, *l = NULL;
-	
-	for( c = irc->chatrooms; c; c = (l=c)->next )
-		if( c == chat )
-			break;
-	
-	if( c == NULL )
-		return 0;
-	else if( l == NULL )
-		irc->chatrooms = c->next;
-	else
-		l->next = c->next;
-	
-	while( c->set )
-		set_del( &c->set, c->set->key );
-	
-	g_free( c->handle );
-	g_free( c->channel );
-	g_free( c );
-	
-	return 1;
-}
-
-int chat_chancmp( char *a, char *b )
-{
-	if( !chat_chanok( a ) || !chat_chanok( b ) )
-		return 0;
-	
-	if( a[0] == b[0] )
-		return nick_cmp( a + 1, b + 1 );
-	else
-		return -1;
-}
-
-int chat_chanok( char *a )
-{
-	if( strchr( CTYPES, a[0] ) != NULL )
-		return nick_ok( a + 1 );
-	else
-		return 0;
-}
-
-int chat_join( irc_t *irc, struct chat *c, const char *password )
-{
-	struct groupchat *gc;
-	char *nick = set_getstr( &c->set, "nick" );
-
-	if( c->acc->ic == NULL || c->acc->prpl->chat_join == NULL )
-		return 0;
-	
-	if( nick == NULL )
-		nick = irc->nick;
-	
-	if( ( gc = c->acc->prpl->chat_join( c->acc->ic, c->handle, nick, password ) ) )
-	{
-		g_free( gc->channel );
-		gc->channel = g_strdup( c->channel );
-		return 1;
-	}
-	
-	return 0;
-}
Index: at.h
===================================================================
--- chat.h	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ 	(revision )
@@ -1,51 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2008 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Keep track of chatrooms the user is interested in                    */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-  Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#ifndef _CHAT_H
-#define _CHAT_H
-
-struct chat
-{
-	account_t *acc;
-	
-	char *handle;
-	char *channel;
-	set_t *set;
-	
-	struct chat *next;
-};
-
-struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel );
-struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle );
-struct chat *chat_bychannel( irc_t *irc, char *channel );
-struct chat *chat_get( irc_t *irc, char *id );
-int chat_del( irc_t *irc, struct chat *chat );
-
-int chat_chancmp( char *a, char *b );
-int chat_chanok( char *a );
-
-int chat_join( irc_t *irc, struct chat *c, const char *password );
-
-#endif
Index: conf.c
===================================================================
--- conf.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ conf.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -369,5 +369,5 @@
 		if( g_strcasecmp( ini->section, "defaults" ) == 0 )
 		{
-			set_t *s = set_find( &irc->set, ini->key );
+			set_t *s = set_find( &irc->b->set, ini->key );
 			
 			if( s )
Index: dcc.c
===================================================================
--- dcc.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ dcc.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -61,9 +61,8 @@
 unsigned int receivedchunks=0, receiveddata=0;
 
-static void dcc_finish( file_transfer_t *file );
-static void dcc_close( file_transfer_t *file );
+void dcc_finish( file_transfer_t *file );
+void dcc_close( file_transfer_t *file );
 gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond );
-int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr );
-gboolean dccs_recv_start( file_transfer_t *ft );
+int dccs_send_request( struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr );
 gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond);
 gboolean dccs_recv_write_request( file_transfer_t *ft );
@@ -71,43 +70,5 @@
 gboolean dcc_abort( dcc_file_transfer_t *df, char *reason, ... );
 
-/* As defined in ft.h */
-file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size )
-{
-	user_t *u = user_findhandle( ic, handle );
-	/* one could handle this more intelligent like imcb_buddy_msg.
-	 * can't call it directly though cause it does some wrapping.
-	 * Maybe give imcb_buddy_msg a parameter NO_WRAPPING? */
-	if (!u) return NULL;
-
-	return dccs_send_start( ic, u->nick, file_name, file_size );
-};
-
-/* As defined in ft.h */
-void imcb_file_canceled( file_transfer_t *file, char *reason )
-{
-	if( file->canceled )
-		file->canceled( file, reason );
-
-	dcc_close( file );
-}
-
-/* As defined in ft.h */
-gboolean imcb_file_recv_start( file_transfer_t *ft )
-{
-	return dccs_recv_start( ft );
-}
-
-/* As defined in ft.h */
-void imcb_file_finished( file_transfer_t *file )
-{
-	dcc_file_transfer_t *df = file->priv;
-
-	if( file->bytes_transferred >= file->file_size )
-		dcc_finish( file );
-	else
-		df->proto_finished = TRUE;
-}
-
-dcc_file_transfer_t *dcc_alloc_transfer( char *file_name, size_t file_size, struct im_connection *ic )
+dcc_file_transfer_t *dcc_alloc_transfer( const char *file_name, size_t file_size, struct im_connection *ic )
 {
 	file_transfer_t *file = g_new0( file_transfer_t, 1 );
@@ -124,8 +85,9 @@
 
 /* This is where the sending magic starts... */
-file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size )
+file_transfer_t *dccs_send_start( struct im_connection *ic, irc_user_t *iu, const char *file_name, size_t file_size )
 {
 	file_transfer_t *file;
 	dcc_file_transfer_t *df;
+	irc_t *irc = (irc_t *) ic->bee->ui_data;
 	struct sockaddr_storage saddr;
 	char *errmsg;
@@ -150,5 +112,5 @@
 	file->status = FT_STATUS_LISTENING;
 
-	if( !dccs_send_request( df, user_nick, &saddr ) )
+	if( !dccs_send_request( df, iu, &saddr ) )
 		return NULL;
 
@@ -156,5 +118,5 @@
 	df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df );
 
-	df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file );
+	irc->file_transfers = g_slist_prepend( irc->file_transfers, file );
 
 	df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df );
@@ -163,5 +125,5 @@
 	              "Accept the file transfer if you'd like the file. If you don't, "
 	              "issue the 'transfers reject' command.",
-	              user_nick, file_name, file_size / 1024 );
+	              iu->nick, file_name, file_size / 1024 );
 
 	return file;
@@ -216,5 +178,5 @@
 
 /* Creates the "DCC SEND" line and sends it to the server */
-int dccs_send_request( struct dcc_file_transfer *df, char *user_nick, struct sockaddr_storage *saddr )
+int dccs_send_request( struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr )
 {
 	char ipaddr[INET6_ADDRSTRLEN]; 
@@ -250,6 +212,5 @@
 				df->ft->file_name, ipaddr, port, df->ft->file_size );
 	
-	if ( !irc_msgfrom( df->ic->irc, user_nick, cmd ) )
-		return dcc_abort( df, "Couldn't send `DCC SEND' message to %s.", user_nick );
+	irc_send_msg_raw( iu, "PRIVMSG", iu->irc->user->nick, cmd );
 
 	g_free( cmd );
@@ -496,7 +457,8 @@
  * Cleans up after a transfer.
  */
-static void dcc_close( file_transfer_t *file )
+void dcc_close( file_transfer_t *file )
 {
 	dcc_file_transfer_t *df = file->priv;
+	irc_t *irc = (irc_t *) df->ic->bee->ui_data;
 
 	if( file->free )
@@ -514,5 +476,5 @@
 		b_event_remove( df->progress_timeout );
 	
-	df->ic->irc->file_transfers = g_slist_remove( df->ic->irc->file_transfers, file );
+	irc->file_transfers = g_slist_remove( irc->file_transfers, file );
 	
 	g_free( df );
@@ -544,4 +506,5 @@
 file_transfer_t *dcc_request( struct im_connection *ic, char *line )
 {
+	irc_t *irc = (irc_t *) ic->bee->ui_data;
 	char *pattern = "SEND"
 		" (([^\"][^ ]*)|\"(([^\"]|\\\")*)\")"
@@ -627,5 +590,5 @@
 		g_free( input );
 
-		df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, ft );
+		irc->file_transfers = g_slist_prepend( irc->file_transfers, ft );
 
 		return ft;
Index: dcc.h
===================================================================
--- dcc.h	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ dcc.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -95,10 +95,11 @@
 } dcc_file_transfer_t;
 
-file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, char *file_name, size_t file_size );
+file_transfer_t *dccs_send_start( struct im_connection *ic, irc_user_t *iu, const char *file_name, size_t file_size );
+void dcc_canceled( file_transfer_t *file, char *reason );
+gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_size );
+file_transfer_t *dcc_request( struct im_connection *ic, char *line );
+void dcc_finish( file_transfer_t *file );
+void dcc_close( file_transfer_t *file );
+gboolean dccs_recv_start( file_transfer_t *ft );
 
-void dcc_canceled( file_transfer_t *file, char *reason );
-
-gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_size );
-
-file_transfer_t *dcc_request( struct im_connection *ic, char *line );
 #endif
Index: ipc.c
===================================================================
--- ipc.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ ipc.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -138,5 +138,5 @@
 	
 	if( strchr( irc->umode, 'w' ) )
-		irc_write( irc, ":%s WALLOPS :%s", irc->myhost, cmd[1] );
+		irc_write( irc, ":%s WALLOPS :%s", irc->root->host, cmd[1] );
 }
 
@@ -147,5 +147,5 @@
 	
 	if( strchr( irc->umode, 's' ) )
-		irc_write( irc, ":%s NOTICE %s :%s", irc->myhost, irc->nick, cmd[1] );
+		irc_write( irc, ":%s NOTICE %s :%s", irc->root->host, irc->user->nick, cmd[1] );
 }
 
@@ -156,5 +156,5 @@
 	
 	if( strchr( irc->umode, 'o' ) )
-		irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->myhost, irc->nick, cmd[1] );
+		irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->root->host, irc->user->nick, cmd[1] );
 }
 
@@ -176,8 +176,8 @@
 		return;
 	
-	if( nick_cmp( cmd[1], irc->nick ) != 0 )
+	if( nick_cmp( cmd[1], irc->user->nick ) != 0 )
 		return;		/* It's not for us. */
 	
-	irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->mynick, irc->mynick, irc->myhost, irc->nick, cmd[2] );
+	irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->root->nick, irc->root->nick, irc->root->host, irc->user->nick, cmd[2] );
 	irc_abort( irc, 0, "Killed by operator: %s", cmd[2] );
 }
@@ -188,5 +188,5 @@
 		ipc_to_master_str( "HELLO\r\n" );
 	else
-		ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
+		ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
 }
 
Index: irc.c
===================================================================
--- irc.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ irc.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -5,5 +5,5 @@
   \********************************************************************/
 
-/* The big hairy IRCd part of the project                               */
+/* The IRC-based UI (for now the only one)                              */
 
 /*
@@ -24,76 +24,11 @@
 */
 
-#define BITLBEE_CORE
 #include "bitlbee.h"
-#include "sock.h"
 #include "ipc.h"
-#include "dcc.h"
-
-static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond );
-
-GSList *irc_connection_list = NULL;
-
-static char *set_eval_password( set_t *set, char *value )
-{
-	irc_t *irc = set->data;
-	
-	if( irc->status & USTATUS_IDENTIFIED && value )
-	{
-		irc_setpass( irc, value );
-		return NULL;
-	}
-	else
-	{
-		return SET_INVALID;
-	}
-}
-
-static char *set_eval_charset( set_t *set, char *value )
-{
-	irc_t *irc = set->data;
-	GIConv ic, oc;
-
-	if( g_strcasecmp( value, "none" ) == 0 )
-		value = g_strdup( "utf-8" );
-
-	if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
-	{
-		return NULL;
-	}
-	if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
-	{
-		g_iconv_close( ic );
-		return NULL;
-	}
-	
-	if( irc->iconv != (GIConv) -1 )
-		g_iconv_close( irc->iconv );
-	if( irc->oconv != (GIConv) -1 )
-		g_iconv_close( irc->oconv );
-	
-	irc->iconv = ic;
-	irc->oconv = oc;
-
-	return value;
-}
-
-static char *set_eval_away_status( set_t *set, char *value )
-{
-	irc_t *irc = set->data;
-	account_t *a;
-	
-	g_free( set->value );
-	set->value = g_strdup( value );
-	
-	for( a = irc->accounts; a; a = a->next )
-	{
-		struct im_connection *ic = a->ic;
-		
-		if( ic && ic->flags & OPT_LOGGED_IN )
-			imc_away_send_update( ic );
-	}
-	
-	return value;
-}
+
+GSList *irc_connection_list;
+
+static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond );
+static char *set_eval_charset( set_t *set, char *value );
 
 irc_t *irc_new( int fd )
@@ -102,5 +37,8 @@
 	struct sockaddr_storage sock;
 	socklen_t socklen = sizeof( sock );
+	char *host = NULL, *myhost = NULL;
+	irc_user_t *iu;
 	set_t *s;
+	bee_t *b;
 	
 	irc = g_new0( irc_t, 1 );
@@ -114,10 +52,6 @@
 	irc->last_pong = gettime();
 	
-	irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
+	irc->nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal );
 	irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
-	
-	strcpy( irc->umode, UMODE );
-	irc->mynick = g_strdup( ROOT_NICK );
-	irc->channel = g_strdup( ROOT_CHAN );
 	
 	irc->iconv = (GIConv) -1;
@@ -126,5 +60,5 @@
 	if( global.conf->hostname )
 	{
-		irc->myhost = g_strdup( global.conf->hostname );
+		myhost = g_strdup( global.conf->hostname );
 	}
 	else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 ) 
@@ -135,5 +69,5 @@
 		                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
 		{
-			irc->myhost = g_strdup( ipv6_unwrap( buf ) );
+			myhost = g_strdup( ipv6_unwrap( buf ) );
 		}
 	}
@@ -146,55 +80,63 @@
 		                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
 		{
-			irc->host = g_strdup( ipv6_unwrap( buf ) );
-		}
-	}
-	
-	if( irc->host == NULL )
-		irc->host = g_strdup( "localhost.localdomain" );
-	if( irc->myhost == NULL )
-		irc->myhost = g_strdup( "localhost.localdomain" );
+			host = g_strdup( ipv6_unwrap( buf ) );
+		}
+	}
+	
+	if( host == NULL )
+		host = g_strdup( "localhost.localdomain" );
+	if( myhost == NULL )
+		myhost = g_strdup( "localhost.localdomain" );
 	
 	if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
 		irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
-	
-	irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
 
 	irc_connection_list = g_slist_append( irc_connection_list, irc );
 	
-	s = set_add( &irc->set, "away", NULL,  set_eval_away_status, irc );
-	s->flags |= SET_NULL_OK;
-	s = set_add( &irc->set, "away_devoice", "true",  set_eval_away_devoice, irc );
-	s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
-	s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc );
-	s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc );
-	s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
-	s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
-	s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
-	s = set_add( &irc->set, "control_channel", irc->channel, set_eval_control_channel, irc );
-	s = set_add( &irc->set, "debug", "false", set_eval_bool, irc );
-	s = set_add( &irc->set, "default_target", "root", NULL, irc );
-	s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
-	s = set_add( &irc->set, "handle_unknown", "root", NULL, irc );
-	s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
-	s = set_add( &irc->set, "ops", "both", set_eval_ops, irc );
-	s = set_add( &irc->set, "password", NULL, set_eval_password, irc );
-	s->flags |= SET_NULL_OK;
-	s = set_add( &irc->set, "private", "true", set_eval_bool, irc );
-	s = set_add( &irc->set, "query_order", "lifo", NULL, irc );
-	s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
-	s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
-	s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
-	s = set_add( &irc->set, "status", NULL,  set_eval_away_status, irc );
-	s->flags |= SET_NULL_OK;
-	s = set_add( &irc->set, "strip_html", "true", NULL, irc );
-	s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
-	s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
+	b = irc->b = bee_new();
+	b->ui_data = irc;
+	b->ui = &irc_ui_funcs;
+	
+	s = set_add( &b->set, "away_devoice", "true", NULL/*set_eval_away_devoice*/, irc );
+	s = set_add( &b->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
+	s = set_add( &b->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
+	s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc );
+	//s = set_add( &b->set, "control_channel", irc->channel, NULL/*set_eval_control_channel*/, irc );
+	s = set_add( &b->set, "default_target", "root", NULL, irc );
+	s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc );
+	s = set_add( &b->set, "handle_unknown", "root", NULL, irc );
+	s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc );
+	s = set_add( &b->set, "ops", "both", NULL/*set_eval_ops*/, irc );
+	s = set_add( &b->set, "private", "true", set_eval_bool, irc );
+	s = set_add( &b->set, "query_order", "lifo", NULL, irc );
+	s = set_add( &b->set, "root_nick", ROOT_NICK, NULL/*set_eval_root_nick*/, irc );
+	s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc );
+	s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc );
+	s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc );
+
+	irc->root = iu = irc_user_new( irc, ROOT_NICK );
+	iu->host = g_strdup( myhost );
+	iu->fullname = g_strdup( ROOT_FN );
+	iu->f = &irc_user_root_funcs;
+	
+	iu = irc_user_new( irc, NS_NICK );
+	iu->host = g_strdup( myhost );
+	iu->fullname = g_strdup( ROOT_FN );
+	iu->f = &irc_user_root_funcs;
+	
+	irc->user = g_new0( irc_user_t, 1 );
+	irc->user->host = g_strdup( host );
 	
 	conf_loaddefaults( irc );
 	
 	/* Evaluator sets the iconv/oconv structures. */
-	set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
-	
-	return( irc );
+	set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) );
+	
+	irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" );
+	
+	g_free( myhost );
+	g_free( host );
+	
+	return irc;
 }
 
@@ -217,5 +159,5 @@
 		
 		ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
-	                           irc->nick ? irc->nick : "(NONE)", irc->host, reason );
+	                           irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, reason );
 	     	
 		g_free( reason );
@@ -227,5 +169,5 @@
 		
 		ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
-	        	           irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" );
+	        	           irc->user->nick ? irc->user->nick : "(NONE)", irc->root->host, "No reason given" );
 	}
 	
@@ -248,63 +190,31 @@
 }
 
-static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
-{
-	g_free( key );
-	
-	return( TRUE );
-}
-
-/* Because we have no garbage collection, this is quite annoying */
+static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data );
+
 void irc_free( irc_t * irc )
 {
-	user_t *user, *usertmp;
-	
 	log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
 	
-	if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) ) 
+	/*
+	if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) ) 
 		if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
 			irc_usermsg( irc, "Error while saving settings!" );
+	*/
 	
 	irc_connection_list = g_slist_remove( irc_connection_list, irc );
 	
-	while( irc->accounts )
-	{
-		if( irc->accounts->ic )
-			imc_logout( irc->accounts->ic, FALSE );
-		else if( irc->accounts->reconnect )
-			cancel_auto_reconnect( irc->accounts );
-		
-		if( irc->accounts->ic == NULL )
-			account_del( irc, irc->accounts );
-		else
-			/* Nasty hack, but account_del() doesn't work in this
-			   case and we don't want infinite loops, do we? ;-) */
-			irc->accounts = irc->accounts->next;
-	}
-	
+	/*
 	while( irc->queries != NULL )
 		query_del( irc, irc->queries );
-	
-	while( irc->set )
-		set_del( &irc->set, irc->set->key );
-	
-	if (irc->users != NULL)
-	{
-		user = irc->users;
-		while( user != NULL )
-		{
-			g_free( user->nick );
-			g_free( user->away );
-			g_free( user->handle );
-			if( user->user != user->nick ) g_free( user->user );
-			if( user->host != user->nick ) g_free( user->host );
-			if( user->realname != user->nick ) g_free( user->realname );
-			b_event_remove( user->sendbuf_timer );
-					
-			usertmp = user;
-			user = user->next;
-			g_free( usertmp );
-		}
-	}
+	*/
+	
+	while( irc->users )
+	{
+		irc_user_t *iu = irc->users->data;
+		irc_user_free( irc, iu->nick );
+	}
+	
+	while( irc->channels )
+		irc_channel_free( irc->channels->data );
 	
 	if( irc->ping_source_id > 0 )
@@ -318,6 +228,6 @@
 	irc->fd = -1;
 	
-	g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
-	g_hash_table_destroy( irc->userhash );
+	g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL );
+	g_hash_table_destroy( irc->nick_user_hash );
 	
 	g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
@@ -332,16 +242,5 @@
 	g_free( irc->readbuffer );
 	
-	g_free( irc->nick );
-	g_free( irc->user );
-	g_free( irc->host );
-	g_free( irc->realname );
 	g_free( irc->password );
-	
-	g_free( irc->myhost );
-	g_free( irc->mynick );
-	
-	g_free( irc->channel );
-	
-	g_free( irc->last_target );
 	
 	g_free( irc );
@@ -355,7 +254,14 @@
 }
 
+static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
+{
+	g_free( key );
+	
+	return( TRUE );
+}
+
 /* USE WITH CAUTION!
    Sets pass without checking */
-void irc_setpass (irc_t *irc, const char *pass) 
+void irc_setpass (irc_t *irc, const char *pass)
 {
 	g_free (irc->password);
@@ -368,4 +274,6 @@
 }
 
+static char **irc_splitlines( char *buffer );
+
 void irc_process( irc_t *irc )
 {
@@ -375,5 +283,5 @@
 	if( irc->readbuffer != NULL )
 	{
-		lines = irc_tokenize( irc->readbuffer );
+		lines = irc_splitlines( irc->readbuffer );
 		
 		for( i = 0; *lines[i] != '\0'; i ++ )
@@ -412,5 +320,5 @@
 						                  "`help set charset' for more information. Your "
 						                  "message was ignored.",
-						                  set_getstr( &irc->set, "charset" ) );
+						                  set_getstr( &irc->b->set, "charset" ) );
 						
 						g_free( conv );
@@ -419,5 +327,5 @@
 					else
 					{
-						irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
+						irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host,
 						           "Warning: invalid characters received at login time." );
 						
@@ -457,7 +365,9 @@
 }
 
-/* Splits a long string into separate lines. The array is NULL-terminated and, unless the string
-   contains an incomplete line at the end, ends with an empty string. */
-char **irc_tokenize( char *buffer )
+/* Splits a long string into separate lines. The array is NULL-terminated
+   and, unless the string contains an incomplete line at the end, ends with
+   an empty string. Could use g_strsplit() but this one does it in-place.
+   (So yes, it's destructive.) */
+static char **irc_splitlines( char *buffer )
 {
 	int i, j, n = 3;
@@ -590,34 +500,4 @@
 }
 
-void irc_reply( irc_t *irc, int code, char *format, ... )
-{
-	char text[IRC_MAX_LINE];
-	va_list params;
-	
-	va_start( params, format );
-	g_vsnprintf( text, IRC_MAX_LINE, format, params );
-	va_end( params );
-	irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
-	
-	return;
-}
-
-int irc_usermsg( irc_t *irc, char *format, ... )
-{
-	char text[1024];
-	va_list params;
-	char is_private = 0;
-	user_t *u;
-	
-	u = user_find( irc, irc->mynick );
-	is_private = u->is_private;
-	
-	va_start( params, format );
-	g_vsnprintf( text, sizeof( text ), format, params );
-	va_end( params );
-	
-	return( irc_msgfrom( irc, u->nick, text ) );
-}
-
 void irc_write( irc_t *irc, char *format, ... ) 
 {
@@ -630,4 +510,33 @@
 	return;
 }
+
+void irc_write_all( int now, char *format, ... )
+{
+	va_list params;
+	GSList *temp;	
+	
+	va_start( params, format );
+	
+	temp = irc_connection_list;
+	while( temp != NULL )
+	{
+		irc_t *irc = temp->data;
+		
+		if( now )
+		{
+			g_free( irc->sendbuffer );
+			irc->sendbuffer = g_strdup( "\r\n" );
+		}
+		irc_vawrite( temp->data, format, params );
+		if( now )
+		{
+			bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
+		}
+		temp = temp->next;
+	}
+	
+	va_end( params );
+	return;
+} 
 
 void irc_vawrite( irc_t *irc, char *format, va_list params )
@@ -686,103 +595,52 @@
 }
 
-void irc_write_all( int now, char *format, ... )
-{
-	va_list params;
-	GSList *temp;	
-	
-	va_start( params, format );
-	
-	temp = irc_connection_list;
-	while( temp != NULL )
-	{
-		irc_t *irc = temp->data;
-		
-		if( now )
-		{
-			g_free( irc->sendbuffer );
-			irc->sendbuffer = g_strdup( "\r\n" );
-		}
-		irc_vawrite( temp->data, format, params );
-		if( now )
-		{
-			bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
-		}
-		temp = temp->next;
-	}
-	
-	va_end( params );
-	return;
-} 
-
-void irc_names( irc_t *irc, char *channel )
-{
-	user_t *u;
-	char namelist[385] = "";
-	struct groupchat *c = NULL;
-	char *ops = set_getstr( &irc->set, "ops" );
-	
-	/* RFCs say there is no error reply allowed on NAMES, so when the
-	   channel is invalid, just give an empty reply. */
-	
-	if( g_strcasecmp( channel, irc->channel ) == 0 )
-	{
-		for( u = irc->users; u; u = u->next ) if( u->online )
-		{
-			if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
+int irc_check_login( irc_t *irc )
+{
+	if( irc->user->user && irc->user->nick )
+	{
+		if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
+		{
+			irc_send_num( irc, 464, ":This server is password-protected." );
+			return 0;
+		}
+		else
+		{
+			irc_channel_t *ic;
+			irc_user_t *iu = irc->user;
+			
+			irc->user = irc_user_new( irc, iu->nick );
+			irc->user->user = iu->user;
+			irc->user->host = iu->host;
+			irc->user->fullname = iu->fullname;
+			irc->user->f = &irc_user_self_funcs;
+			g_free( iu->nick );
+			g_free( iu );
+			
+			if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
+				ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
+			
+			irc->status |= USTATUS_LOGGED_IN;
+			
+			/* This is for bug #209 (use PASS to identify to NickServ). */
+			if( irc->password != NULL )
 			{
-				irc_reply( irc, 353, "= %s :%s", channel, namelist );
-				*namelist = 0;
+				char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
+				
+				/*irc_setpass( irc, NULL );*/
+				/*root_command( irc, send_cmd );*/
+				g_free( send_cmd[1] );
 			}
 			
-			if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )
-				strcat( namelist, "+" );
-			else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||
-			         ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )
-				strcat( namelist, "@" );
-			
-			strcat( namelist, u->nick );
-			strcat( namelist, " " );
-		}
-	}
-	else if( ( c = irc_chat_by_channel( irc, channel ) ) )
-	{
-		GList *l;
-		
-		/* root and the user aren't in the channel userlist but should
-		   show up in /NAMES, so list them first: */
-		sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick,
-		                                 strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick );
-		
-		for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) )
-		{
-			if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
-			{
-				irc_reply( irc, 353, "= %s :%s", channel, namelist );
-				*namelist = 0;
-			}
-			
-			strcat( namelist, u->nick );
-			strcat( namelist, " " );
-		}
-	}
-	
-	if( *namelist )
-		irc_reply( irc, 353, "= %s :%s", channel, namelist );
-	
-	irc_reply( irc, 366, "%s :End of /NAMES list", channel );
-}
-
-int irc_check_login( irc_t *irc )
-{
-	if( irc->user && irc->nick )
-	{
-		if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
-		{
-			irc_reply( irc, 464, ":This server is password-protected." );
-			return 0;
-		}
-		else
-		{
-			irc_login( irc );
+			irc_send_login( irc );
+			
+			irc->umode[0] = '\0';
+			irc_umode_set( irc, "+" UMODE, TRUE );
+			
+			ic = irc_channel_new( irc, ROOT_CHAN );
+			irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root );
+			irc_channel_add_user( ic, irc->user );
+			
+			irc->last_root_cmd = g_strdup( ROOT_CHAN );
+			
 			return 1;
 		}
@@ -795,133 +653,10 @@
 }
 
-void irc_login( irc_t *irc )
-{
-	user_t *u;
-	
-	irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
-	irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
-	irc_reply( irc,   3, ":%s", IRCD_INFO );
-	irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
-	irc_reply( irc,   5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee "
-	                     "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server",
-	                     CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
-	irc_motd( irc );
-	irc->umode[0] = '\0';
-	irc_umode_set( irc, "+" UMODE, 1 );
-
-	u = user_add( irc, irc->mynick );
-	u->host = g_strdup( irc->myhost );
-	u->realname = g_strdup( ROOT_FN );
-	u->online = 1;
-	u->send_handler = root_command_string;
-	u->is_private = 0; /* [SH] The channel is root's personal playground. */
-	irc_spawn( irc, u );
-	
-	u = user_add( irc, NS_NICK );
-	u->host = g_strdup( irc->myhost );
-	u->realname = g_strdup( ROOT_FN );
-	u->online = 0;
-	u->send_handler = root_command_string;
-	u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
-	
-	u = user_add( irc, irc->nick );
-	u->user = g_strdup( irc->user );
-	u->host = g_strdup( irc->host );
-	u->realname = g_strdup( irc->realname );
-	u->online = 1;
-	irc_spawn( irc, u );
-	
-	irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
-	                  "If you've never used BitlBee before, please do read the help "
-	                  "information using the \x02help\x02 command. Lots of FAQs are "
-	                  "answered there.\n"
-	                  "If you already have an account on this server, just use the "
-	                  "\x02identify\x02 command to identify yourself." );
-	
-	if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
-		ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
-	
-	irc->status |= USTATUS_LOGGED_IN;
-	
-	/* This is for bug #209 (use PASS to identify to NickServ). */
-	if( irc->password != NULL )
-	{
-		char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
-		
-		irc_setpass( irc, NULL );
-		root_command( irc, send_cmd );
-		g_free( send_cmd[1] );
-	}
-}
-
-void irc_motd( irc_t *irc )
-{
-	int fd;
-	
-	fd = open( global.conf->motdfile, O_RDONLY );
-	if( fd == -1 )
-	{
-		irc_reply( irc, 422, ":We don't need MOTDs." );
-	}
-	else
-	{
-		char linebuf[80];	/* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
-		char *add, max;
-		int len;
-		
-		linebuf[79] = len = 0;
-		max = sizeof( linebuf ) - 1;
-		
-		irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
-		while( read( fd, linebuf + len, 1 ) == 1 )
-		{
-			if( linebuf[len] == '\n' || len == max )
-			{
-				linebuf[len] = 0;
-				irc_reply( irc, 372, ":- %s", linebuf );
-				len = 0;
-			}
-			else if( linebuf[len] == '%' )
-			{
-				read( fd, linebuf + len, 1 );
-				if( linebuf[len] == 'h' )
-					add = irc->myhost;
-				else if( linebuf[len] == 'v' )
-					add = BITLBEE_VERSION;
-				else if( linebuf[len] == 'n' )
-					add = irc->nick;
-				else
-					add = "%";
-				
-				strncpy( linebuf + len, add, max - len );
-				while( linebuf[++len] );
-			}
-			else if( len < max )
-			{
-				len ++;
-			}
-		}
-		irc_reply( irc, 376, ":End of MOTD" );
-		close( fd );
-	}
-}
-
-void irc_topic( irc_t *irc, char *channel )
-{
-	struct groupchat *c = irc_chat_by_channel( irc, channel );
-	
-	if( c && c->topic )
-		irc_reply( irc, 332, "%s :%s", channel, c->topic );
-	else if( g_strcasecmp( channel, irc->channel ) == 0 )
-		irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
-	else
-		irc_reply( irc, 331, "%s :No topic for this channel", channel );
-}
-
-void irc_umode_set( irc_t *irc, char *s, int allow_priv )
+void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv )
 {
 	/* allow_priv: Set to 0 if s contains user input, 1 if you want
 	   to set a "privileged" mode (+o, +R, etc). */
-	char m[256], st = 1, *t;
+	char m[128], st = 1;
+	const char *t;
 	int i;
 	char changes[512], *p, st2 = 2;
@@ -931,6 +666,7 @@
 	
 	for( t = irc->umode; *t; t ++ )
-		m[(int)*t] = 1;
-
+		if( *t < sizeof( m ) )
+			m[(int)*t] = 1;
+	
 	p = changes;
 	for( t = s; *t; t ++ )
@@ -938,5 +674,7 @@
 		if( *t == '+' || *t == '-' )
 			st = *t == '+';
-		else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
+		else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
+		         ( st == 1 && strchr( UMODES, *t ) ) ||
+		         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
 		{
 			if( m[(int)*t] != st)
@@ -955,337 +693,16 @@
 	memset( irc->umode, 0, sizeof( irc->umode ) );
 	
-	for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
+	for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
 		if( m[i] )
 			irc->umode[strlen(irc->umode)] = i;
 	
 	if( badflag )
-		irc_reply( irc, 501, ":Unknown MODE flag" );
-	/* Deliberately no !user@host on the prefix here */
+		irc_send_num( irc, 501, ":Unknown MODE flag" );
 	if( *changes )
-		irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
-}
-
-void irc_spawn( irc_t *irc, user_t *u )
-{
-	irc_join( irc, u, irc->channel );
-}
-
-void irc_join( irc_t *irc, user_t *u, char *channel )
-{
-	char *nick;
-	
-	if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
-		irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
-	
-	if( nick_cmp( u->nick, irc->nick ) == 0 )
-	{
-		irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
-		irc_names( irc, channel );
-		irc_topic( irc, channel );
-	}
-	
-	nick = g_strdup( u->nick );
-	nick_lc( nick );
-	if( g_hash_table_lookup( irc->watches, nick ) )
-	{
-		irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
-	}
-	g_free( nick );
-}
-
-void irc_part( irc_t *irc, user_t *u, char *channel )
-{
-	irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
-}
-
-void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
-{
-	irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
-}
-
-void irc_kill( irc_t *irc, user_t *u )
-{
-	char *nick, *s;
-	char reason[128];
-	
-	if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) )
-	{
-		if( u->ic->acc->server )
-			g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
-			            u->ic->acc->server );
-		else if( ( s = strchr( u->ic->acc->user, '@' ) ) )
-			g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
-			            s + 1 );
-		else
-			g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
-			            u->ic->acc->prpl->name, irc->myhost );
-		
-		/* proto_opt might contain garbage after the : */
-		if( ( s = strchr( reason, ':' ) ) )
-			*s = 0;
-	}
-	else
-	{
-		strcpy( reason, "Leaving..." );
-	}
-	
-	irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
-	
-	nick = g_strdup( u->nick );
-	nick_lc( nick );
-	if( g_hash_table_lookup( irc->watches, nick ) )
-	{
-		irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
-	}
-	g_free( nick );
-}
-
-int irc_send( irc_t *irc, char *nick, char *s, int flags )
-{
-	struct groupchat *c = NULL;
-	user_t *u = NULL;
-	
-	if( strchr( CTYPES, *nick ) )
-	{
-		if( !( c = irc_chat_by_channel( irc, nick ) ) )
-		{
-			irc_reply( irc, 403, "%s :Channel does not exist", nick );
-			return( 0 );
-		}
-	}
-	else
-	{
-		u = user_find( irc, nick );
-		
-		if( !u )
-		{
-			if( irc->is_private )
-				irc_reply( irc, 401, "%s :Nick does not exist", nick );
-			else
-				irc_usermsg( irc, "Nick `%s' does not exist!", nick );
-			return( 0 );
-		}
-	}
-	
-	if( *s == 1 && s[strlen(s)-1] == 1 )
-	{
-		if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
-		{
-			if( s[7] == ' ' ) s ++;
-			s += 3;
-			*(s++) = '/';
-			*(s++) = 'm';
-			*(s++) = 'e';
-			*(s++) = ' ';
-			s -= 4;
-			s[strlen(s)-1] = 0;
-		}
-		else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
-		{
-			u = user_find( irc, irc->mynick );
-			irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
-			return( 1 );
-		}
-		else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
-		{
-			u = user_find( irc, irc->mynick );
-			irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
-			return( 1 );
-		}
-		else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
-		{
-			if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 )
-			{
-				time_t current_typing_notice = time( NULL );
-				
-				if( current_typing_notice - u->last_typing_notice >= 5 )
-				{
-					u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 );
-					u->last_typing_notice = current_typing_notice;
-				}
-			}
-			return( 1 );
-		}
-		else if( g_strncasecmp( s + 1, "DCC", 3 ) == 0 )
-		{
-			if( u && u->ic && u->ic->acc->prpl->transfer_request )
-			{
-				file_transfer_t *ft = dcc_request( u->ic, s + 5 );
-				if ( ft )
-					u->ic->acc->prpl->transfer_request( u->ic, ft, u->handle );
-			}
-			return( 1 );
-		}		
-		else
-		{
-			irc_usermsg( irc, "Supported CTCPs are ACTION, VERSION, PING, TYPING, DCC" );
-			return( 0 );
-		}
-	}
-	
-	if( u )
-	{
-		/* For the next message, we probably do have to send new notices... */
-		u->last_typing_notice = 0;
-		u->is_private = irc->is_private;
-		
-		if( u->is_private )
-		{
-			if( !u->online )
-				irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
-			else if( u->away )
-				irc_reply( irc, 301, "%s :%s", u->nick, u->away );
-		}
-		
-		if( u->send_handler )
-		{
-			u->send_handler( irc, u, s, flags );
-			return 1;
-		}
-	}
-	else if( c && c->ic && c->ic->acc && c->ic->acc->prpl )
-	{
-		return( imc_chat_msg( c, s, 0 ) );
-	}
-	
-	return( 0 );
-}
-
-static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
-{
-	user_t *u = data;
-	
-	/* Shouldn't happen, but just to be sure. */
-	if( u->sendbuf_len < 2 )
-		return FALSE;
-	
-	u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
-	imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags );
-	
-	g_free( u->sendbuf );
-	u->sendbuf = NULL;
-	u->sendbuf_len = 0;
-	u->sendbuf_timer = 0;
-	u->sendbuf_flags = 0;
-	
-	return FALSE;
-}
-
-void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
-{
-	if( !u || !u->ic ) return;
-	
-	if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
-	{
-		int delay;
-		
-		if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
-		{
-			/* Flush the buffer */
-			b_event_remove( u->sendbuf_timer );
-			buddy_send_handler_delayed( u, -1, 0 );
-		}
-
-		if( u->sendbuf_len == 0 )
-		{
-			u->sendbuf_len = strlen( msg ) + 2;
-			u->sendbuf = g_new( char, u->sendbuf_len );
-			u->sendbuf[0] = 0;
-			u->sendbuf_flags = flags;
-		}
-		else
-		{
-			u->sendbuf_len += strlen( msg ) + 1;
-			u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
-		}
-		
-		strcat( u->sendbuf, msg );
-		strcat( u->sendbuf, "\n" );
-		
-		delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
-		if( delay <= 5 )
-			delay *= 1000;
-		
-		if( u->sendbuf_timer > 0 )
-			b_event_remove( u->sendbuf_timer );
-		u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
-	}
-	else
-	{
-		imc_buddy_msg( u->ic, u->handle, msg, flags );
-	}
-}
-
-int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
-{
-	char last = 0;
-	char *s = msg, *line = msg;
-	
-	/* The almighty linesplitter .. woohoo!! */
-	while( !last )
-	{
-		if( *s == '\r' && *(s+1) == '\n' )
-			*(s++) = 0;
-		if( *s == '\n' )
-		{
-			last = s[1] == 0;
-			*s = 0;
-		}
-		else
-		{
-			last = s[0] == 0;
-		}
-		if( *s == 0 )
-		{
-			if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
-			{
-				irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
-				           type, to, line + 4 );
-			}
-			else
-			{
-				irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
-				           type, to, prefix ? prefix : "", line );
-			}
-			line = s + 1;
-		}
-		s ++;
-	}
-	
-	return( 1 );
-}
-
-int irc_msgfrom( irc_t *irc, char *nick, char *msg )
-{
-	user_t *u = user_find( irc, nick );
-	static char *prefix = NULL;
-	
-	if( !u ) return( 0 );
-	if( prefix && *prefix ) g_free( prefix );
-	
-	if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
-	{
-		int len = strlen( irc->nick) + 3;
-		prefix = g_new (char, len );
-		g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
-		prefix[len-1] = 0;
-	}
-	else
-	{
-		prefix = "";
-	}
-	
-	return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
-}
-
-int irc_noticefrom( irc_t *irc, char *nick, char *msg )
-{
-	user_t *u = user_find( irc, nick );
-	
-	if( u )
-		return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
-	else
-		return( 0 );
-}
+		irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
+		           irc->user->user, irc->user->host, irc->user->nick,
+		           changes );
+}
+
 
 /* Returns 0 if everything seems to be okay, a number >0 when there was a
@@ -1325,25 +742,31 @@
 }
 
-struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
-{
-	struct groupchat *c;
-	account_t *a;
-	
-	/* This finds the connection which has a conversation which belongs to this channel */
-	for( a = irc->accounts; a; a = a->next )
-	{
-		if( a->ic == NULL )
-			continue;
-		
-		c = a->ic->groupchats;
-		while( c )
-		{
-			if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
-				return c;
-			
-			c = c->next;
-		}
-	}
-	
-	return NULL;
-}
+
+static char *set_eval_charset( set_t *set, char *value )
+{
+	irc_t *irc = set->data;
+	GIConv ic, oc;
+
+	if( g_strcasecmp( value, "none" ) == 0 )
+		value = g_strdup( "utf-8" );
+
+	if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
+	{
+		return NULL;
+	}
+	if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
+	{
+		g_iconv_close( ic );
+		return NULL;
+	}
+	
+	if( irc->iconv != (GIConv) -1 )
+		g_iconv_close( irc->iconv );
+	if( irc->oconv != (GIConv) -1 )
+		g_iconv_close( irc->oconv );
+	
+	irc->iconv = ic;
+	irc->oconv = oc;
+
+	return value;
+}
Index: irc.h
===================================================================
--- irc.h	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ irc.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -5,5 +5,5 @@
   \********************************************************************/
 
-/* The big hairy IRCd part of the project                               */
+/* The IRC-based UI (for now the only one)                              */
 
 /*
@@ -33,10 +33,12 @@
 #define IRC_PING_STRING "PinglBee"
 
-#define UMODES "abisw"
-#define UMODES_PRIV "Ro"
-#define CMODES "nt"
-#define CMODE "t"
-#define UMODE "s"
-#define CTYPES "&#"
+#define UMODES "abisw"     /* Allowed umodes (although they mostly do nothing) */
+#define UMODES_PRIV "Ro"   /* Allowed, but not by user directly */
+#define UMODES_KEEP "R"    /* Don't allow unsetting using /MODE */
+#define CMODES "nt"        /* Allowed modes */
+#define CMODE "t"          /* Default mode */
+#define UMODE "s"          /* Default mode */
+
+#define CTYPES "&#"        /* Valid channel name prefixes */
 
 typedef enum
@@ -48,4 +50,6 @@
 	USTATUS_SHUTDOWN = 8
 } irc_status_t;
+
+struct irc_user;
 
 typedef struct irc
@@ -59,44 +63,96 @@
 	GIConv iconv, oconv;
 
-	int sentbytes;
-	time_t oldtime;
-
+	struct irc_user *root;
+	struct irc_user *user;
+	
+	char *last_root_cmd;
+
+	char *password; /* HACK: Used to save the user's password, but before
+	                   logging in, this may contain a password we should
+	                   send to identify after USER/NICK are received. */
+
+	char umode[8];
+	
+	struct query *queries;
+	GSList *file_transfers;
+	
+	GSList *users, *channels;
+	GHashTable *nick_user_hash;
+	GHashTable *watches;
+
+	gint r_watch_source_id;
+	gint w_watch_source_id;
+	gint ping_source_id;
+	
+	struct bee *b;
+} irc_t;
+
+typedef enum
+{
+	IRC_USER_PRIVATE = 1,
+} irc_user_flags_t;
+
+typedef struct irc_user
+{
+	irc_t *irc;
+	
 	char *nick;
 	char *user;
 	char *host;
-	char *realname;
-	char *password; /* HACK: Used to save the user's password, but before
-	                   logging in, this may contain a password we should
-	                   send to identify after USER/NICK are received. */
-
-	char umode[8];
-	
-	char *myhost;
-	char *mynick;
-
-	char *channel;
-	int c_id;
-
-	char is_private;		/* Not too nice... */
-	char *last_target;
-	
-	struct query *queries;
-	struct account *accounts;
-	GSList *file_transfers;
-	struct chat *chatrooms;
-	
-	struct __USER *users;
-	GHashTable *userhash;
-	GHashTable *watches;
-	struct __NICK *nicks;
+	char *fullname;
+	
+	/* Nickname in lowercase for case sensitive searches */
+	char *key;
+	
+	irc_user_flags_t flags;
+	
+	char *sendbuf;
+	int sendbuf_len;
+	guint sendbuf_timer;
+	//int sendbuf_flags;
+	
+	struct bee_user *bu;
+	
+	const struct irc_user_funcs *f;
+} irc_user_t;
+
+struct irc_user_funcs
+{
+	gboolean (*privmsg)( irc_user_t *iu, const char *msg );
+};
+
+extern const struct irc_user_funcs irc_user_root_funcs;
+extern const struct irc_user_funcs irc_user_self_funcs;
+
+typedef enum
+{
+	IRC_CHANNEL_JOINED = 1,
+} irc_channel_flags_t;
+
+typedef struct irc_channel
+{
+	irc_t *irc;
+	char *name;
+	char mode[8];
+	int flags;
+	
+	char *topic;
+	char *topic_who;
+	time_t topic_time;
+	
+	GSList *users;
 	struct set *set;
-
-	gint r_watch_source_id;
-	gint w_watch_source_id;
-	gint ping_source_id;
-} irc_t;
-
-#include "user.h"
-
+	
+	const struct irc_channel_funcs *f;
+} irc_channel_t;
+
+struct irc_channel_funcs
+{
+	gboolean (*privmsg)( irc_channel_t *iu, const char *msg );
+};
+
+extern const struct bee_ui_funcs irc_ui_funcs;
+
+/* irc.c */
 extern GSList *irc_connection_list;
 
@@ -104,40 +160,52 @@
 void irc_abort( irc_t *irc, int immed, char *format, ... ) G_GNUC_PRINTF( 3, 4 );
 void irc_free( irc_t *irc );
-
-void irc_exec( irc_t *irc, char **cmd );
+void irc_setpass (irc_t *irc, const char *pass);
+
 void irc_process( irc_t *irc );
 char **irc_parse_line( char *line );
 char *irc_build_line( char **cmd );
 
-void irc_vawrite( irc_t *irc, char *format, va_list params );
 void irc_write( irc_t *irc, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
 void irc_write_all( int now, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
-void irc_reply( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 );
-G_MODULE_EXPORT int irc_usermsg( irc_t *irc, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
-char **irc_tokenize( char *buffer );
-
-void irc_login( irc_t *irc );
+void irc_vawrite( irc_t *irc, char *format, va_list params );
+
 int irc_check_login( irc_t *irc );
-void irc_motd( irc_t *irc );
-void irc_names( irc_t *irc, char *channel );
-void irc_topic( irc_t *irc, char *channel );
-void irc_umode_set( irc_t *irc, char *s, int allow_priv );
-void irc_who( irc_t *irc, char *channel );
-void irc_spawn( irc_t *irc, user_t *u );
-void irc_join( irc_t *irc, user_t *u, char *channel );
-void irc_part( irc_t *irc, user_t *u, char *channel );
-void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker );
-void irc_kill( irc_t *irc, user_t *u );
-void irc_invite( irc_t *irc, char *nick, char *channel );
-void irc_whois( irc_t *irc, char *nick );
-void irc_setpass( irc_t *irc, const char *pass ); /* USE WITH CAUTION! */
-
-int irc_send( irc_t *irc, char *nick, char *s, int flags );
-int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg );
-int irc_msgfrom( irc_t *irc, char *nick, char *msg );
-int irc_noticefrom( irc_t *irc, char *nick, char *msg );
-
-void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags );
-struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel );
+
+void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv );
+
+/* irc_channel.c */
+irc_channel_t *irc_channel_new( irc_t *irc, const char *name );
+irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name );
+int irc_channel_free( irc_channel_t *ic );
+int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu );
+int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu );
+gboolean irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu );
+int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who );
+gboolean irc_channel_name_ok( const char *name );
+
+/* irc_commands.c */
+void irc_exec( irc_t *irc, char **cmd );
+
+/* irc_send.c */
+void irc_send_num( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 );
+void irc_send_login( irc_t *irc );
+void irc_send_motd( irc_t *irc );
+void irc_usermsg( irc_t *irc, char *format, ... );
+void irc_send_join( irc_channel_t *ic, irc_user_t *iu );
+void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason );
+void irc_send_names( irc_channel_t *ic );
+void irc_send_topic( irc_channel_t *ic, gboolean topic_change );
+void irc_send_whois( irc_user_t *iu );
+void irc_send_who( irc_t *irc, GSList *l, const char *channel );
+void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix );
+void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg );
+void irc_send_nick( irc_user_t *iu, const char *new );
+
+/* irc_user.c */
+irc_user_t *irc_user_new( irc_t *irc, const char *nick );
+int irc_user_free( irc_t *irc, const char *nick );
+irc_user_t *irc_user_by_name( irc_t *irc, const char *nick );
+int irc_user_set_nick( irc_user_t *iu, const char *new );
+gint irc_user_cmp( gconstpointer a_, gconstpointer b_ );
 
 #endif
Index: irc_channel.c
===================================================================
--- irc_channel.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ irc_channel.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,160 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* The IRC-based UI - Representing (virtual) channels.                  */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "bitlbee.h"
+
+static const struct irc_channel_funcs control_channel_funcs;
+
+irc_channel_t *irc_channel_new( irc_t *irc, const char *name )
+{
+	irc_channel_t *ic;
+	
+	if( strchr( CTYPES, name[0] ) == NULL || !nick_ok( name + 1 ) )
+		return NULL;
+	
+	ic = g_new0( irc_channel_t, 1 );
+	ic->f = &control_channel_funcs;
+	ic->irc = irc;
+	ic->name = g_strdup( name );
+	strcpy( ic->mode, CMODE );
+	
+	irc_channel_add_user( ic, irc->root );
+	
+	irc->channels = g_slist_prepend( irc->channels, ic );
+	
+	return ic;
+}
+
+irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name )
+{
+	GSList *l;
+	
+	for( l = irc->channels; l; l = l->next )
+	{
+		irc_channel_t *ic = l->data;
+		
+		if( name[0] == ic->name[0] && nick_cmp( name + 1, ic->name + 1 ) == 0 )
+			return ic;
+	}
+	
+	return NULL;
+}
+
+int irc_channel_free( irc_channel_t *ic )
+{
+	irc_t *irc = ic->irc;
+	
+	if( ic->flags & IRC_CHANNEL_JOINED )
+		irc_channel_del_user( ic, irc->user );
+	
+	irc->channels = g_slist_remove( irc->channels, ic );
+	g_slist_free( ic->users );
+	
+	g_free( ic->name );
+	g_free( ic->topic );
+	g_free( ic );
+	
+	return 1;
+}
+
+int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu )
+{
+	if( irc_channel_has_user( ic, iu ) )
+		return 0;
+	
+	ic->users = g_slist_insert_sorted( ic->users, iu, irc_user_cmp );
+	
+	if( iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED )
+	{
+		ic->flags |= IRC_CHANNEL_JOINED;
+		irc_send_join( ic, iu );
+	}
+	
+	return 1;
+}
+
+int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu )
+{
+	if( !irc_channel_has_user( ic, iu ) )
+		return 0;
+	
+	ic->users = g_slist_remove( ic->users, iu );
+	
+	if( ic->flags & IRC_CHANNEL_JOINED )
+		irc_send_part( ic, iu, "" );
+	
+	if( iu == ic->irc->user )
+		ic->flags &= ~IRC_CHANNEL_JOINED;
+	
+	return 1;
+}
+
+/* Currently a fairly stupid one-liner but I fear it's going to get worse. :-) */
+gboolean irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu )
+{
+	return g_slist_find( ic->users, iu ) != NULL;
+}
+
+int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *iu )
+{
+	g_free( ic->topic );
+	ic->topic = g_strdup( topic );
+	
+	g_free( ic->topic_who );
+	if( iu )
+		ic->topic_who = g_strdup_printf( "%s!%s@%s", iu->nick, iu->user, iu->host );
+	else
+		ic->topic_who = NULL;
+	
+	ic->topic_time = time( NULL );
+	
+	if( ic->flags & IRC_CHANNEL_JOINED )
+		irc_send_topic( ic, TRUE );
+	
+	return 1;
+}
+
+gboolean irc_channel_name_ok( const char *name )
+{
+	return strchr( CTYPES, name[0] ) != NULL && nick_ok( name + 1 );
+}
+
+/* Channel-type dependent functions, for control channels: */
+static gboolean control_channel_privmsg( irc_channel_t *ic, const char *msg )
+{
+	char cmd[strlen(msg)+1];
+	
+	g_free( ic->irc->last_root_cmd );
+	ic->irc->last_root_cmd = g_strdup( ic->name );
+	
+	strcpy( cmd, msg );
+	root_command_string( ic->irc, cmd );
+	
+	return TRUE;
+}
+
+static const struct irc_channel_funcs control_channel_funcs = {
+	control_channel_privmsg,
+};
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ irc_commands.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2006 Wilmer van der Gaast and others                *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -53,10 +53,10 @@
 	else if( global.conf->auth_pass )
 	{
-		irc_reply( irc, 464, ":Incorrect password" );
+		irc_send_num( irc, 464, ":Incorrect password" );
 	}
 	else
 	{
 		/* Remember the password and try to identify after USER/NICK. */
-		irc_setpass( irc, cmd[1] );
+		/*irc_setpass( irc, cmd[1] ); */
 		irc_check_login( irc );
 	}
@@ -65,6 +65,6 @@
 static void irc_cmd_user( irc_t *irc, char **cmd )
 {
-	irc->user = g_strdup( cmd[1] );
-	irc->realname = g_strdup( cmd[4] );
+	irc->user->user = g_strdup( cmd[1] );
+	irc->user->fullname = g_strdup( cmd[4] );
 	
 	irc_check_login( irc );
@@ -73,21 +73,20 @@
 static void irc_cmd_nick( irc_t *irc, char **cmd )
 {
-	if( irc->nick )
-	{
-		irc_reply( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" );
-	}
-	/* This is not clean, but for now it'll have to be like this... */
-	else if( ( nick_cmp( cmd[1], irc->mynick ) == 0 ) || ( nick_cmp( cmd[1], NS_NICK ) == 0 ) )
-	{
-		irc_reply( irc, 433, ":This nick is already in use" );
+	if( irc->user->nick )
+	{
+		irc_send_num( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" );
+	}
+	else if( irc_user_by_name( irc, cmd[1] ) )
+	{
+		irc_send_num( irc, 433, ":This nick is already in use" );
 	}
 	else if( !nick_ok( cmd[1] ) )
 	{
 		/* [SH] Invalid characters. */
-		irc_reply( irc, 432, ":This nick contains invalid characters" );
-	}
-	else
-	{
-		irc->nick = g_strdup( cmd[1] );
+		irc_send_num( irc, 432, ":This nick contains invalid characters" );
+	}
+	else
+	{
+		irc->user->nick = g_strdup( cmd[1] );
 		
 		irc_check_login( irc );
@@ -105,130 +104,166 @@
 static void irc_cmd_ping( irc_t *irc, char **cmd )
 {
-	irc_write( irc, ":%s PONG %s :%s", irc->myhost, irc->myhost, cmd[1]?cmd[1]:irc->myhost );
-}
-
-static void irc_cmd_oper( irc_t *irc, char **cmd )
-{
-	if( global.conf->oper_pass &&
-	    ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
-	        md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
-	        strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
-	{
-		irc_umode_set( irc, "+o", 1 );
-		irc_reply( irc, 381, ":Password accepted" );
-	}
-	else
-	{
-		irc_reply( irc, 432, ":Incorrect password" );
-	}
+	irc_write( irc, ":%s PONG %s :%s", irc->root->host,
+	           irc->root->host, cmd[1]?cmd[1]:irc->root->host );
+}
+
+static void irc_cmd_pong( irc_t *irc, char **cmd )
+{
+	/* We could check the value we get back from the user, but in
+	   fact we don't care, we're just happy s/he's still alive. */
+	irc->last_pong = gettime();
+	irc->pinging = 0;
+}
+
+static void irc_cmd_join( irc_t *irc, char **cmd )
+{
+	irc_channel_t *ic;
+	
+	if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
+		ic = irc_channel_new( irc, cmd[1] );
+	
+	if( ic == NULL )
+		irc_send_num( irc, 479, "%s :Invalid channel name", cmd[1] );
+	
+	if( ic->flags & IRC_CHANNEL_JOINED )
+		return; /* Dude, you're already there...
+		           RFC doesn't have any reply for that though? */
+	
+	irc_channel_add_user( ic, irc->user );
+}
+
+static void irc_cmd_names( irc_t *irc, char **cmd )
+{
+	irc_channel_t *ic;
+	
+	if( cmd[1] && ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
+		irc_send_names( ic );
+	/* With no args, we should show /names of all chans. Make the code
+	   below work well if necessary.
+	else
+	{
+		GSList *l;
+		
+		for( l = irc->channels; l; l = l->next )
+			irc_send_names( l->data );
+	}
+	*/
+}
+
+static void irc_cmd_part( irc_t *irc, char **cmd )
+{
+	irc_channel_t *ic;
+	
+	if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
+	{
+		irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
+	}
+	else if( !irc_channel_del_user( ic, irc->user ) )
+	{
+		irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] );
+	}
+}
+
+static void irc_cmd_whois( irc_t *irc, char **cmd )
+{
+	char *nick = cmd[1];
+	irc_user_t *iu = irc_user_by_name( irc, nick );
+	
+	if( iu )
+		irc_send_whois( iu );
+	else
+		irc_send_num( irc, 401, "%s :Nick does not exist", nick );
+}
+
+static void irc_cmd_whowas( irc_t *irc, char **cmd )
+{
+	/* For some reason irssi tries a whowas when whois fails. We can
+	   ignore this, but then the user never gets a "user not found"
+	   message from irssi which is a bit annoying. So just respond
+	   with not-found and irssi users will get better error messages */
+	
+	irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] );
+	irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] );
+}
+
+static void irc_cmd_motd( irc_t *irc, char **cmd )
+{
+	irc_send_motd( irc );
 }
 
 static void irc_cmd_mode( irc_t *irc, char **cmd )
 {
-	if( strchr( CTYPES, *cmd[1] ) )
-	{
-		if( cmd[2] )
+	if( irc_channel_name_ok( cmd[1] ) )
+	{
+		irc_channel_t *ic;
+		
+		if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
+			irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
+		else if( cmd[2] )
 		{
 			if( *cmd[2] == '+' || *cmd[2] == '-' )
-				irc_reply( irc, 477, "%s :Can't change channel modes", cmd[1] );
+				irc_send_num( irc, 477, "%s :Can't change channel modes", cmd[1] );
 			else if( *cmd[2] == 'b' )
-				irc_reply( irc, 368, "%s :No bans possible", cmd[1] );
+				irc_send_num( irc, 368, "%s :No bans possible", cmd[1] );
 		}
 		else
-			irc_reply( irc, 324, "%s +%s", cmd[1], CMODE );
-	}
-	else
-	{
-		if( nick_cmp( cmd[1], irc->nick ) == 0 )
+			irc_send_num( irc, 324, "%s +%s", cmd[1], ic->mode );
+	}
+	else
+	{
+		if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
 		{
 			if( cmd[2] )
 				irc_umode_set( irc, cmd[2], 0 );
 			else
-				irc_reply( irc, 221, "+%s", irc->umode );
+				irc_send_num( irc, 221, "+%s", irc->umode );
 		}
 		else
-			irc_reply( irc, 502, ":Don't touch their modes" );
-	}
-}
-
-static void irc_cmd_names( irc_t *irc, char **cmd )
-{
-	irc_names( irc, cmd[1]?cmd[1]:irc->channel );
-}
-
-static void irc_cmd_part( irc_t *irc, char **cmd )
-{
-	struct groupchat *c;
-	
-	if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
-	{
-		user_t *u = user_find( irc, irc->nick );
-		
-		/* Not allowed to leave control channel */
-		irc_part( irc, u, irc->channel );
-		irc_join( irc, u, irc->channel );
-	}
-	else if( ( c = irc_chat_by_channel( irc, cmd[1] ) ) )
-	{
-		user_t *u = user_find( irc, irc->nick );
-		
-		irc_part( irc, u, c->channel );
-		
-		if( c->ic )
-		{
-			c->joined = 0;
-			c->ic->acc->prpl->chat_leave( c );
-		}
-	}
-	else
-	{
-		irc_reply( irc, 403, "%s :No such channel", cmd[1] );
-	}
-}
-
-static void irc_cmd_join( irc_t *irc, char **cmd )
-{
-	if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
-		; /* Dude, you're already there...
-		     RFC doesn't have any reply for that though? */
-	else if( cmd[1] )
-	{
-		struct chat *c;
-		
-		if( strchr( CTYPES, cmd[1][0] ) == NULL || cmd[1][1] == 0 )
-			irc_reply( irc, 479, "%s :Invalid channel name", cmd[1] );
-		else if( ( c = chat_bychannel( irc, cmd[1] ) ) && c->acc && c->acc->ic )
-			chat_join( irc, c, cmd[2] );
-		else
-			irc_reply( irc, 403, "%s :No such channel", cmd[1] );
-	}
-}
-
-static void irc_cmd_invite( irc_t *irc, char **cmd )
-{
-	char *nick = cmd[1], *channel = cmd[2];
-	struct groupchat *c = irc_chat_by_channel( irc, channel );
-	user_t *u = user_find( irc, nick );
-	
-	if( u && c && ( u->ic == c->ic ) )
-		if( c->ic && c->ic->acc->prpl->chat_invite )
-		{
-			c->ic->acc->prpl->chat_invite( c, u->handle, NULL );
-			irc_reply( irc, 341, "%s %s", nick, channel );
-			return;
-		}
-	
-	irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
+			irc_send_num( irc, 502, ":Don't touch their modes" );
+	}
+}
+
+static void irc_cmd_who( irc_t *irc, char **cmd )
+{
+	char *channel = cmd[1];
+	irc_channel_t *ic;
+	
+	if( !channel || *channel == '0' || *channel == '*' || !*channel )
+		irc_send_who( irc, irc->users, "**" );
+	else if( ( ic = irc_channel_by_name( irc, channel ) ) )
+		irc_send_who( irc, ic->users, channel );
+	else
+		irc_send_num( irc, 403, "%s :No such channel", channel );
 }
 
 static void irc_cmd_privmsg( irc_t *irc, char **cmd )
 {
-	if ( !cmd[2] ) 
-	{
-		irc_reply( irc, 412, ":No text to send" );
-	}
-	else if ( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) 
-	{
-		irc_write( irc, ":%s!%s@%s %s %s :%s", irc->nick, irc->user, irc->host, cmd[0], cmd[1], cmd[2] ); 
+	irc_channel_t *ic;
+	irc_user_t *iu;
+	
+	if( !cmd[2] ) 
+	{
+		irc_send_num( irc, 412, ":No text to send" );
+	}
+	else if( irc_channel_name_ok( cmd[1] ) &&
+	         ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
+	{
+		if( ic->f->privmsg )
+			ic->f->privmsg( ic, cmd[2] );
+	}
+	else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) )
+	{
+		if( iu->f->privmsg )
+			iu->f->privmsg( iu, cmd[2] );
+	}
+	else
+	{
+		irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] );
+	}
+
+
+#if 0
+	else if( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) 
+	{
 	}
 	else 
@@ -271,36 +306,49 @@
 		irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? OPT_AWAY : 0 );
 	}
-}
-
-static void irc_cmd_who( irc_t *irc, char **cmd )
-{
-	char *channel = cmd[1];
-	user_t *u = irc->users;
-	struct groupchat *c;
-	GList *l;
-	
-	if( !channel || *channel == '0' || *channel == '*' || !*channel )
-		while( u )
-		{
-			irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", u->online ? irc->channel : "*", u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname );
-			u = u->next;
-		}
-	else if( g_strcasecmp( channel, irc->channel ) == 0 )
-		while( u )
-		{
-			if( u->online )
-				irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname );
-			u = u->next;
-		}
-	else if( ( c = irc_chat_by_channel( irc, channel ) ) )
-		for( l = c->in_room; l; l = l->next )
-		{
-			if( ( u = user_findhandle( c->ic, l->data ) ) )
-				irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname );
-		}
-	else if( ( u = user_find( irc, channel ) ) )
-		irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname );
-	
-	irc_reply( irc, 315, "%s :End of /WHO list", channel?channel:"**" );
+#endif
+}
+
+static void irc_cmd_nickserv( irc_t *irc, char **cmd )
+{
+	/* [SH] This aliases the NickServ command to PRIVMSG root */
+	/* [TV] This aliases the NS command to PRIVMSG root as well */
+	root_command( irc, cmd + 1 );
+}
+
+
+
+#if 0
+//#if 0
+static void irc_cmd_oper( irc_t *irc, char **cmd )
+{
+	if( global.conf->oper_pass &&
+	    ( strncmp( global.conf->oper_pass, "md5:", 4 ) == 0 ?
+	        md5_verify_password( cmd[2], global.conf->oper_pass + 4 ) == 0 :
+	        strcmp( cmd[2], global.conf->oper_pass ) == 0 ) )
+	{
+		irc_umode_set( irc, "+o", 1 );
+		irc_send_num( irc, 381, ":Password accepted" );
+	}
+	else
+	{
+		irc_send_num( irc, 432, ":Incorrect password" );
+	}
+}
+
+static void irc_cmd_invite( irc_t *irc, char **cmd )
+{
+	char *nick = cmd[1], *channel = cmd[2];
+	struct groupchat *c = irc_chat_by_channel( irc, channel );
+	user_t *u = user_find( irc, nick );
+	
+	if( u && c && ( u->ic == c->ic ) )
+		if( c->ic && c->ic->acc->prpl->chat_invite )
+		{
+			c->ic->acc->prpl->chat_invite( c, u->handle, NULL );
+			irc_send_num( irc, 341, "%s %s", nick, channel );
+			return;
+		}
+	
+	irc_send_num( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
 }
 
@@ -320,7 +368,7 @@
 		{
 			if( u->online && u->away )
-				irc_reply( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host );
+				irc_send_num( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host );
 			else
-				irc_reply( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host );
+				irc_send_num( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host );
 		}
 }
@@ -377,5 +425,5 @@
 		buff[strlen(buff)-1] = '\0';
 	
-	irc_reply( irc, 303, ":%s", buff );
+	irc_send_num( irc, 303, ":%s", buff );
 }
 
@@ -406,7 +454,7 @@
 			
 			if( u && u->online )
-				irc_reply( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" );
+				irc_send_num( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" );
 			else
-				irc_reply( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" );
+				irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" );
 		}
 		else if( cmd[i][0] == '-' )
@@ -419,5 +467,5 @@
 				g_free( okey );
 				
-				irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
+				irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
 			}
 		}
@@ -463,5 +511,5 @@
 		u->away[j] = 0;
 		
-		irc_reply( irc, 306, ":You're now away: %s", u->away );
+		irc_send_num( irc, 306, ":You're now away: %s", u->away );
 		/* irc_umode_set( irc, irc->myhost, "+a" ); */
 	}
@@ -471,5 +519,5 @@
 		u->away = NULL;
 		/* irc_umode_set( irc, irc->myhost, "-a" ); */
-		irc_reply( irc, 305, ":Welcome back" );
+		irc_send_num( irc, 305, ":Welcome back" );
 	}
 	
@@ -477,69 +525,7 @@
 }
 
-static void irc_cmd_whois( irc_t *irc, char **cmd )
-{
-	char *nick = cmd[1];
-	user_t *u = user_find( irc, nick );
-	
-	if( u )
-	{
-		irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname );
-		
-		if( u->ic )
-			irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->ic->acc->user,
-			           u->ic->acc->server && *u->ic->acc->server ? u->ic->acc->server : "",
-			           u->ic->acc->prpl->name );
-		else
-			irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO );
-		
-		if( !u->online )
-			irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
-		else if( u->away )
-			irc_reply( irc, 301, "%s :%s", u->nick, u->away );
-		if( u->status_msg )
-			irc_reply( irc, 333, "%s :Status: %s", u->nick, u->status_msg );
-		
-		irc_reply( irc, 318, "%s :End of /WHOIS list", nick );
-	}
-	else
-	{
-		irc_reply( irc, 401, "%s :Nick does not exist", nick );
-	}
-}
-
-static void irc_cmd_whowas( irc_t *irc, char **cmd )
-{
-	/* For some reason irssi tries a whowas when whois fails. We can
-	   ignore this, but then the user never gets a "user not found"
-	   message from irssi which is a bit annoying. So just respond
-	   with not-found and irssi users will get better error messages */
-	
-	irc_reply( irc, 406, "%s :Nick does not exist", cmd[1] );
-	irc_reply( irc, 369, "%s :End of WHOWAS", cmd[1] );
-}
-
-static void irc_cmd_nickserv( irc_t *irc, char **cmd )
-{
-	/* [SH] This aliases the NickServ command to PRIVMSG root */
-	/* [TV] This aliases the NS command to PRIVMSG root as well */
-	root_command( irc, cmd + 1 );
-}
-
-static void irc_cmd_motd( irc_t *irc, char **cmd )
-{
-	irc_motd( irc );
-}
-
-static void irc_cmd_pong( irc_t *irc, char **cmd )
-{
-	/* We could check the value we get back from the user, but in
-	   fact we don't care, we're just happy he's still alive. */
-	irc->last_pong = gettime();
-	irc->pinging = 0;
-}
-
 static void irc_cmd_version( irc_t *irc, char **cmd )
 {
-	irc_reply( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU );
+	irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU );
 }
 
@@ -572,6 +558,7 @@
 		ipc_to_master( cmd );
 	
-	irc_reply( irc, 382, "%s :Rehashing", global.conf_file );
-}
+	irc_send_num( irc, 382, "%s :Rehashing", global.conf_file );
+}
+#endif
 
 static const command_t irc_commands[] = {
@@ -581,13 +568,20 @@
 	{ "quit",        0, irc_cmd_quit,        0 },
 	{ "ping",        0, irc_cmd_ping,        0 },
+	{ "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
+	{ "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
+	{ "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
+	{ "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
+	{ "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
+	{ "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
+	{ "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
+	{ "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
+	{ "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
+	{ "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
+	{ "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
+	{ "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
+#if 0
 	{ "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
-	{ "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
-	{ "names",       0, irc_cmd_names,       IRC_CMD_LOGGED_IN },
-	{ "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
-	{ "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
 	{ "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
-	{ "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
 	{ "notice",      1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
-	{ "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
 	{ "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
 	{ "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
@@ -595,10 +589,4 @@
 	{ "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
 	{ "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
-	{ "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
-	{ "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
-	{ "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
-	{ "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
-	{ "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
-	{ "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
 	{ "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
 	{ "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
@@ -610,4 +598,5 @@
 	{ "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
 	{ "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
+#endif
 	{ NULL }
 };
@@ -628,17 +617,17 @@
 			if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN )
 			{
-				irc_reply( irc, 462, ":Only allowed before logging in" );
+				irc_send_num( irc, 462, ":Only allowed before logging in" );
 			}
 			else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) )
 			{
-				irc_reply( irc, 451, ":Register first" );
+				irc_send_num( irc, 451, ":Register first" );
 			}
 			else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) )
 			{
-				irc_reply( irc, 481, ":Permission denied - You're not an IRC operator" );
+				irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" );
 			}
 			else if( n_arg < irc_commands[i].required_parameters )
 			{
-				irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
+				irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] );
 			}
 			else if( irc_commands[i].flags & IRC_CMD_TO_MASTER )
@@ -657,4 +646,4 @@
 	
 	if( irc->status >= USTATUS_LOGGED_IN )
-		irc_reply( irc, 421, "%s :Unknown command", cmd[0] );
-}
+		irc_send_num( irc, 421, "%s :Unknown command", cmd[0] );
+}
Index: irc_im.c
===================================================================
--- irc_im.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ irc_im.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,215 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* Some glue to put the IRC and the IM stuff together.                  */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "bitlbee.h"
+#include "dcc.h"
+
+/* IM->IRC callbacks */
+
+static const struct irc_user_funcs irc_user_im_funcs;
+
+static gboolean bee_irc_user_new( bee_t *bee, bee_user_t *bu )
+{
+	irc_user_t *iu;
+	char nick[MAX_NICK_LENGTH+1], *s;
+	
+	memset( nick, 0, MAX_NICK_LENGTH + 1 );
+	strcpy( nick, nick_get( bu->ic->acc, bu->handle ) );
+	
+	bu->ui_data = iu = irc_user_new( (irc_t*) bee->ui_data, nick );
+	iu->bu = bu;
+	
+	if( ( s = strchr( bu->handle, '@' ) ) )
+	{
+		iu->host = g_strdup( s + 1 );
+		iu->user = g_strndup( bu->handle, s - bu->handle );
+	}
+	else if( bu->ic->acc->server )
+	{
+		iu->host = g_strdup( bu->ic->acc->server );
+		iu->user = g_strdup( bu->handle );
+		
+		/* s/ /_/ ... important for AOL screennames */
+		for( s = iu->user; *s; s ++ )
+			if( *s == ' ' )
+				*s = '_';
+	}
+	else
+	{
+		iu->host = g_strdup( bu->ic->acc->prpl->name );
+		iu->user = g_strdup( bu->handle );
+	}
+	
+	if( set_getbool( &bee->set, "private" ) )
+		iu->flags |= IRC_USER_PRIVATE;
+	
+	iu->f = &irc_user_im_funcs;
+	//iu->last_typing_notice = 0;
+	
+	return TRUE;
+}
+
+static gboolean bee_irc_user_free( bee_t *bee, bee_user_t *bu )
+{
+	return irc_user_free( bee->ui_data, bu->ui_data );
+}
+
+static gboolean bee_irc_user_status( bee_t *bee, bee_user_t *bu, bee_user_t *old )
+{
+	irc_t *irc = bee->ui_data;
+	irc_channel_t *ic = irc->channels->data; /* For now, just pick the first channel. */
+	
+	if( ( bu->flags & BEE_USER_ONLINE ) != ( old->flags & BEE_USER_ONLINE ) )
+	{
+		if( bu->flags & BEE_USER_ONLINE )
+			irc_channel_add_user( ic, (irc_user_t*) bu->ui_data );
+		else
+			irc_channel_del_user( ic, (irc_user_t*) bu->ui_data );
+	}
+	
+	return TRUE;
+}
+
+static gboolean bee_irc_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at )
+{
+	irc_t *irc = bee->ui_data;
+	irc_channel_t *ic = irc->channels->data;
+	irc_user_t *iu = (irc_user_t *) bu->ui_data;
+	char *dst, *prefix = NULL;
+	char *wrapped;
+	
+	if( iu->flags & IRC_USER_PRIVATE )
+	{
+		dst = irc->user->nick;
+	}
+	else
+	{
+		dst = ic->name;
+		prefix = g_strdup_printf( "%s%s", irc->user->nick, set_getstr( &bee->set, "to_char" ) );
+	}
+	
+	wrapped = word_wrap( msg, 425 );
+	irc_send_msg( iu, "PRIVMSG", dst, wrapped, prefix );
+	
+	g_free( wrapped );
+	g_free( prefix );
+	
+	return TRUE;
+}
+
+static gboolean bee_irc_user_fullname( bee_t *bee, bee_user_t *bu )
+{
+	irc_user_t *iu = (irc_user_t *) bu->ui_data;
+	irc_t *irc = (irc_t *) bee->ui_data;
+	char *s;
+	
+	if( iu->fullname != iu->nick )
+		g_free( iu->fullname );
+	iu->fullname = g_strdup( bu->fullname );
+	
+	/* Strip newlines (unlikely, but IRC-unfriendly so they must go)
+	   TODO(wilmer): Do the same with away msgs again! */
+	for( s = iu->fullname; *s; s ++ )
+		if( isspace( *s ) ) *s = ' ';
+	
+	if( ( bu->ic->flags & OPT_LOGGED_IN ) && set_getbool( &bee->set, "display_namechanges" ) )
+	{
+		char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
+		irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
+	}
+	
+	s = set_getstr( &bu->ic->acc->set, "nick_source" );
+	if( strcmp( s, "handle" ) != 0 )
+	{
+		char *name = g_strdup( bu->fullname );
+		
+		if( strcmp( s, "first_name" ) == 0 )
+		{
+			int i;
+			for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {}
+			name[i] = '\0';
+		}
+		
+		imcb_buddy_nick_hint( bu->ic, bu->handle, name );
+		
+		g_free( name );
+	}
+	
+	return TRUE;
+}
+
+/* File transfers */
+static file_transfer_t *bee_irc_ft_in_start( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size )
+{
+	return dccs_send_start( bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size );
+}
+
+gboolean bee_irc_ft_out_start( struct im_connection *ic, file_transfer_t *ft )
+{
+	return dccs_recv_start( ft );
+}
+
+void bee_irc_ft_close( struct im_connection *ic, file_transfer_t *ft )
+{
+	return dcc_close( ft );
+}
+
+void bee_irc_ft_finished( struct im_connection *ic, file_transfer_t *file )
+{
+	dcc_file_transfer_t *df = file->priv;
+
+	if( file->bytes_transferred >= file->file_size )
+		dcc_finish( file );
+	else
+		df->proto_finished = TRUE;
+}
+
+const struct bee_ui_funcs irc_ui_funcs = {
+	bee_irc_user_new,
+	bee_irc_user_free,
+	bee_irc_user_fullname,
+	bee_irc_user_status,
+	bee_irc_user_msg,
+	
+	bee_irc_ft_in_start,
+	bee_irc_ft_out_start,
+	bee_irc_ft_close,
+	bee_irc_ft_finished,
+};
+
+
+/* IRC->IM calls */
+
+static gboolean bee_irc_user_privmsg( irc_user_t *iu, const char *msg )
+{
+	if( iu->bu )
+		return bee_user_msg( iu->irc->b, iu->bu, msg, 0 );
+	else
+		return FALSE;
+}
+
+static const struct irc_user_funcs irc_user_im_funcs = {
+	bee_irc_user_privmsg,
+};
Index: irc_send.c
===================================================================
--- irc_send.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ irc_send.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,318 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* The IRC-based UI - Sending responses to commands/etc.                */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "bitlbee.h"
+
+void irc_send_num( irc_t *irc, int code, char *format, ... )
+{
+	char text[IRC_MAX_LINE];
+	va_list params;
+	
+	va_start( params, format );
+	g_vsnprintf( text, IRC_MAX_LINE, format, params );
+	va_end( params );
+	
+	irc_write( irc, ":%s %03d %s %s", irc->root->host, code, irc->user->nick ? : "*", text );
+}
+
+void irc_send_login( irc_t *irc )
+{
+	irc_send_num( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->user->nick );
+	irc_send_num( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->root->host );
+	irc_send_num( irc,   3, ":%s", IRCD_INFO );
+	irc_send_num( irc,   4, "%s %s %s %s", irc->root->host, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
+	irc_send_num( irc,   5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee "
+	                        "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server",
+	                        CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
+	irc_send_motd( irc );
+	
+	irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
+	                  "If you've never used BitlBee before, please do read the help "
+	                  "information using the \x02help\x02 command. Lots of FAQs are "
+	                  "answered there.\n"
+	                  "If you already have an account on this server, just use the "
+	                  "\x02identify\x02 command to identify yourself." );
+}
+
+void irc_send_motd( irc_t *irc )
+{
+	int fd;
+	
+	fd = open( global.conf->motdfile, O_RDONLY );
+	if( fd == -1 )
+	{
+		irc_send_num( irc, 422, ":We don't need MOTDs." );
+	}
+	else
+	{
+		char linebuf[80];	/* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
+		char *add, max;
+		int len;
+		
+		linebuf[79] = len = 0;
+		max = sizeof( linebuf ) - 1;
+		
+		irc_send_num( irc, 375, ":- %s Message Of The Day - ", irc->root->host );
+		while( read( fd, linebuf + len, 1 ) == 1 )
+		{
+			if( linebuf[len] == '\n' || len == max )
+			{
+				linebuf[len] = 0;
+				irc_send_num( irc, 372, ":- %s", linebuf );
+				len = 0;
+			}
+			else if( linebuf[len] == '%' )
+			{
+				read( fd, linebuf + len, 1 );
+				if( linebuf[len] == 'h' )
+					add = irc->root->host;
+				else if( linebuf[len] == 'v' )
+					add = BITLBEE_VERSION;
+				else if( linebuf[len] == 'n' )
+					add = irc->user->nick;
+				else
+					add = "%";
+				
+				strncpy( linebuf + len, add, max - len );
+				while( linebuf[++len] );
+			}
+			else if( len < max )
+			{
+				len ++;
+			}
+		}
+		irc_send_num( irc, 376, ":End of MOTD" );
+		close( fd );
+	}
+}
+
+void irc_usermsg( irc_t *irc, char *format, ... )
+{
+	irc_channel_t *ic;
+	irc_user_t *iu;
+	char text[1024];
+	va_list params;
+	
+	va_start( params, format );
+	g_vsnprintf( text, sizeof( text ), format, params );
+	va_end( params );
+	
+	if( irc->last_root_cmd &&
+	    irc_channel_name_ok( irc->last_root_cmd ) && 
+	    ( ic = irc_channel_by_name( irc, irc->last_root_cmd ) ) &&
+	    ic->flags & IRC_CHANNEL_JOINED )
+		irc_send_msg( irc->root, "PRIVMSG", irc->last_root_cmd, text, NULL );
+	else if( irc->last_root_cmd &&
+	         ( iu = irc_user_by_name( irc, irc->last_root_cmd ) ) &&
+	         iu->f == &irc_user_root_funcs )
+		irc_send_msg( iu, "PRIVMSG", irc->user->nick, text, NULL );
+	else
+	{
+		g_free( irc->last_root_cmd );
+		irc->last_root_cmd = NULL;
+		
+		irc_send_msg( irc->root, "PRIVMSG", irc->user->nick, text, NULL );
+	}
+	
+	/*return( irc_msgfrom( irc, u->nick, text ) );*/
+}
+
+void irc_send_join( irc_channel_t *ic, irc_user_t *iu )
+{
+	irc_t *irc = ic->irc;
+	
+	irc_write( irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name );
+	
+	if( iu == irc->user )
+	{
+		irc_write( irc, ":%s MODE %s +%s", irc->root->host, ic->name, ic->mode );
+		irc_send_names( ic );
+		irc_send_topic( ic, FALSE );
+	}
+}
+
+void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason )
+{
+	irc_write( ic->irc, ":%s!%s@%s PART %s :%s", iu->nick, iu->user, iu->host, ic->name, reason );
+}
+
+void irc_send_names( irc_channel_t *ic )
+{
+	GSList *l;
+	char namelist[385] = "";
+	//char *ops = set_getstr( &ic->irc->b->set, "ops" );
+	
+	/* RFCs say there is no error reply allowed on NAMES, so when the
+	   channel is invalid, just give an empty reply. */
+	for( l = ic->users; l; l = l->next )
+	{
+		irc_user_t *iu = l->data;
+		
+		if( strlen( namelist ) + strlen( iu->nick ) > sizeof( namelist ) - 4 )
+		{
+			irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist );
+			*namelist = 0;
+		}
+		
+		/*
+		if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )
+			strcat( namelist, "+" );
+		else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||
+		         ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )
+			strcat( namelist, "@" );
+		*/
+		
+		strcat( namelist, iu->nick );
+		strcat( namelist, " " );
+	}
+	
+	if( *namelist )
+		irc_send_num( ic->irc, 353, "= %s :%s", ic->name, namelist );
+	
+	irc_send_num( ic->irc, 366, "%s :End of /NAMES list", ic->name );
+}
+
+void irc_send_topic( irc_channel_t *ic, gboolean topic_change )
+{
+	if( topic_change && ic->topic_who )
+	{
+		irc_write( ic->irc, ":%s TOPIC %s :%s", ic->topic_who, 
+		           ic->name, ic->topic && *ic->topic ? ic->topic : "" );
+	}
+	else if( ic->topic )
+	{
+		irc_send_num( ic->irc, 332, "%s :%s", ic->name, ic->topic );
+		if( ic->topic_who )
+			irc_send_num( ic->irc, 333, "%s %s %d",
+			              ic->name, ic->topic_who, (int) ic->topic_time );
+	}
+	else
+		irc_send_num( ic->irc, 331, "%s :No topic for this channel", ic->name );
+}
+
+void irc_send_whois( irc_user_t *iu )
+{
+	irc_t *irc = iu->irc;
+	
+	irc_send_num( irc, 311, "%s %s %s * :%s",
+	              iu->nick, iu->user, iu->host, iu->fullname );
+	
+	if( iu->bu )
+	{
+		bee_user_t *bu = iu->bu;
+		
+		irc_send_num( irc, 312, "%s %s.%s :%s network", iu->nick, bu->ic->acc->user,
+		           bu->ic->acc->server && *bu->ic->acc->server ? bu->ic->acc->server : "",
+		           bu->ic->acc->prpl->name );
+		
+		if( bu->status )
+		{
+			if( bu->status_msg )
+				irc_send_num( irc, 301, "%s :%s (%s)", iu->nick, bu->status, bu->status_msg );
+			else
+				irc_send_num( irc, 301, "%s :%s", iu->nick, bu->status );
+		}
+		
+		/*
+		if( u->status_msg )
+			irc_send_num( irc, 333, "%s :Status: %s", u->nick, u->status_msg );
+		*/
+	}
+	else
+	{
+		irc_send_num( irc, 312, "%s %s :%s", iu->nick, irc->root->host, IRCD_INFO " " BITLBEE_VERSION );
+	}
+	
+	irc_send_num( irc, 318, "%s :End of /WHOIS list", iu->nick );
+}
+
+void irc_send_who( irc_t *irc, GSList *l, const char *channel )
+{
+	while( l )
+	{
+		irc_user_t *iu = l->data;
+		/* TODO(wilmer): Restore away/channel information here */
+		irc_send_num( irc, 352, "%s %s %s %s %s %c :0 %s",
+		              channel ? : "*", iu->user, iu->host, irc->root->host,
+		              iu->nick, 'H', iu->fullname );
+		l = l->next;
+	}
+	
+	irc_send_num( irc, 315, "%s :End of /WHO list", channel );
+}
+
+void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix )
+{
+	char last = 0;
+	const char *s = msg, *line = msg;
+	char raw_msg[strlen(msg)+1024];
+	
+	while( !last )
+	{
+		if( *s == '\r' && *(s+1) == '\n' )
+			s++;
+		if( *s == '\n' )
+		{
+			last = s[1] == 0;
+		}
+		else
+		{
+			last = s[0] == 0;
+		}
+		if( *s == 0 || *s == '\n' )
+		{
+			if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) &&
+			    g_strcasecmp( type, "PRIVMSG" ) == 0 )
+			{
+				strcpy( raw_msg, "\001ACTION " );
+				strncat( raw_msg, line + 4, s - line - 4 );
+				strcat( raw_msg, "\001" );
+				irc_send_msg_raw( iu, type, dst, raw_msg );
+			}
+			else
+			{
+				*raw_msg = '\0';
+				if( prefix && *prefix )
+					strcpy( raw_msg, prefix );
+				strncat( raw_msg, line, s - line );
+				irc_send_msg_raw( iu, type, dst, raw_msg );
+			}
+			line = s + 1;
+		}
+		s ++;
+	}
+}
+
+void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg )
+{
+	irc_write( iu->irc, ":%s!%s@%s %s %s :%s",
+	           iu->nick, iu->user, iu->host, type, dst, msg );
+}
+
+void irc_send_nick( irc_user_t *iu, const char *new )
+{
+	irc_write( iu->irc, ":%s!%s@%s NICK %s",
+	           iu->nick, iu->user, iu->host, new );
+}
Index: irc_user.c
===================================================================
--- irc_user.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ irc_user.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,164 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* Stuff to handle, save and search IRC buddies                         */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "bitlbee.h"
+
+irc_user_t *irc_user_new( irc_t *irc, const char *nick )
+{
+	irc_user_t *iu = g_new0( irc_user_t, 1 );
+	
+	iu->irc = irc;
+	iu->nick = g_strdup( nick );
+	iu->user = iu->host = iu->fullname = iu->nick;
+	
+	iu->flags = set_getbool( &irc->b->set, "private" ) ? IRC_USER_PRIVATE : 0;
+	
+	iu->key = g_strdup( nick );
+	nick_lc( iu->key );
+	/* Using the hash table for speed and irc->users for easy iteration
+	   through the list (since the GLib API doesn't have anything sane
+	   for that.) */
+	g_hash_table_insert( irc->nick_user_hash, iu->key, iu );
+	irc->users = g_slist_insert_sorted( irc->users, iu, irc_user_cmp );
+	
+	return iu;
+}
+
+int irc_user_free( irc_t *irc, const char *nick )
+{
+	irc_user_t *iu;
+	GSList *l;
+	
+	if( !( iu = irc_user_by_name( irc, nick ) ) )
+		return 0;
+	
+	irc->users = g_slist_remove( irc->users, iu );
+	g_hash_table_remove( irc->nick_user_hash, iu->key );
+	
+	for( l = irc->channels; l; l = l->next )
+		irc_channel_del_user( (irc_channel_t*) l->data, iu );
+	
+	g_free( iu->nick );
+	if( iu->nick != iu->user ) g_free( iu->user );
+	if( iu->nick != iu->host ) g_free( iu->host );
+	if( iu->nick != iu->fullname ) g_free( iu->fullname );
+	g_free( iu->sendbuf );
+	if( iu->sendbuf_timer ) b_event_remove( iu->sendbuf_timer );
+	g_free( iu->key );
+	
+	return 1;
+}
+
+irc_user_t *irc_user_by_name( irc_t *irc, const char *nick )
+{
+	char key[strlen(nick)+1];
+	
+	strcpy( key, nick );
+	if( nick_lc( key ) )
+		return g_hash_table_lookup( irc->nick_user_hash, key );
+	else
+		return NULL;
+}
+
+int irc_user_set_nick( irc_user_t *iu, const char *new )
+{
+	irc_t *irc = iu->irc;
+	char key[strlen(new)+1];
+	GSList *cl;
+	
+	strcpy( key, new );
+	if( iu == NULL || !nick_lc( key ) || irc_user_by_name( irc, new ) )
+		return 0;
+	
+	for( cl = irc->channels; cl; cl = cl->next )
+	{
+		irc_channel_t *ic = cl->data;
+		
+		/* Send a NICK update if we're renaming our user, or someone
+		   who's in the same channel like our user. */
+		if( iu == irc->user ||
+		    ( ( ic->flags & IRC_CHANNEL_JOINED ) &&
+		      irc_channel_has_user( ic, iu ) ) )
+		{
+			irc_send_nick( iu, new );
+			break;
+		}
+	}
+	
+	irc->users = g_slist_remove( irc->users, iu );
+	g_hash_table_remove( irc->nick_user_hash, iu->key );
+	
+	if( iu->nick == iu->user ) iu->user = NULL;
+	if( iu->nick == iu->host ) iu->host = NULL;
+	if( iu->nick == iu->fullname ) iu->fullname = NULL;
+	g_free( iu->nick );
+	iu->nick = g_strdup( new );
+	if( iu->user == NULL ) iu->user = g_strdup( iu->nick );
+	if( iu->host == NULL ) iu->host = g_strdup( iu->nick );
+	if( iu->fullname == NULL ) iu->fullname = g_strdup( iu->nick );
+	
+	iu->key = g_strdup( key );
+	g_hash_table_insert( irc->nick_user_hash, iu->key, iu );
+	irc->users = g_slist_insert_sorted( irc->users, iu, irc_user_cmp );
+	
+	return 1;
+}
+
+gint irc_user_cmp( gconstpointer a_, gconstpointer b_ )
+{
+	const irc_user_t *a = a_, *b = b_;
+	
+	return strcmp( a->key, b->key );
+}
+
+/* User-type dependent functions, for root/NickServ: */
+static gboolean root_privmsg( irc_user_t *iu, const char *msg )
+{
+	char cmd[strlen(msg)+1];
+	
+	g_free( iu->irc->last_root_cmd );
+	iu->irc->last_root_cmd = g_strdup( iu->nick );
+	
+	strcpy( cmd, msg );
+	root_command_string( iu->irc, cmd );
+	
+	return TRUE;
+}
+
+const struct irc_user_funcs irc_user_root_funcs = {
+	root_privmsg,
+};
+
+/* Echo to yourself: */
+static gboolean self_privmsg( irc_user_t *iu, const char *msg )
+{
+	irc_send_msg_raw( iu, "PRIVMSG", iu->nick, msg );
+	
+	return TRUE;
+}
+
+const struct irc_user_funcs irc_user_self_funcs = {
+	self_privmsg,
+};
Index: nick.c
===================================================================
--- nick.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ nick.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -78,5 +78,5 @@
 		
 		nick_strip( nick );
-		if( set_getbool( &acc->irc->set, "lcnicks" ) )
+		if( set_getbool( &acc->bee->set, "lcnicks" ) )
 			nick_lc( nick );
 	}
@@ -92,9 +92,10 @@
 void nick_dedupe( account_t *acc, const char *handle, char nick[MAX_NICK_LENGTH+1] )
 {
+	irc_t *irc = (irc_t*) acc->bee->ui_data;
 	int inf_protection = 256;
 	
 	/* Now, find out if the nick is already in use at the moment, and make
 	   subtle changes to make it unique. */
-	while( !nick_ok( nick ) || user_find( acc->irc, nick ) )
+	while( !nick_ok( nick ) || irc_user_by_name( irc, nick ) )
 	{
 		if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) )
@@ -112,17 +113,17 @@
 			int i;
 			
-			irc_usermsg( acc->irc, "Warning: Almost had an infinite loop in nick_get()! "
-			                       "This used to be a fatal BitlBee bug, but we tried to fix it. "
-			                       "This message should *never* appear anymore. "
-			                       "If it does, please *do* send us a bug report! "
-			                       "Please send all the following lines in your report:" );
-			
-			irc_usermsg( acc->irc, "Trying to get a sane nick for handle %s", handle );
+			irc_usermsg( irc, "Warning: Almost had an infinite loop in nick_get()! "
+			                  "This used to be a fatal BitlBee bug, but we tried to fix it. "
+			                  "This message should *never* appear anymore. "
+			                  "If it does, please *do* send us a bug report! "
+			                  "Please send all the following lines in your report:" );
+			
+			irc_usermsg( irc, "Trying to get a sane nick for handle %s", handle );
 			for( i = 0; i < MAX_NICK_LENGTH; i ++ )
-				irc_usermsg( acc->irc, "Char %d: %c/%d", i, nick[i], nick[i] );
-			
-			irc_usermsg( acc->irc, "FAILED. Returning an insane nick now. Things might break. "
-			                       "Good luck, and please don't forget to paste the lines up here "
-			                       "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" );
+				irc_usermsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] );
+			
+			irc_usermsg( irc, "FAILED. Returning an insane nick now. Things might break. "
+			                  "Good luck, and please don't forget to paste the lines up here "
+			                  "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" );
 			
 			g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() );
Index: protocols/Makefile
===================================================================
--- protocols/Makefile	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/Makefile	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -10,5 +10,6 @@
 
 # [SH] Program variables
-objects = nogaim.o
+objects = account.o bee.o bee_ft.o bee_user.o nogaim.o
+
 
 # [SH] The next two lines should contain the directory name (in $(subdirs))
Index: protocols/account.c
===================================================================
--- protocols/account.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ protocols/account.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,360 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* Account management functions                                         */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define BITLBEE_CORE
+#include "bitlbee.h"
+#include "account.h"
+#include "chat.h"
+
+account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass )
+{
+	account_t *a;
+	set_t *s;
+	
+	if( bee->accounts )
+	{
+		for( a = bee->accounts; a->next; a = a->next );
+		a = a->next = g_new0( account_t, 1 );
+	}
+	else
+	{
+		bee->accounts = a = g_new0 ( account_t, 1 );
+	}
+	
+	a->prpl = prpl;
+	a->user = g_strdup( user );
+	a->pass = g_strdup( pass );
+	a->auto_connect = 1;
+	a->bee = bee;
+	
+	s = set_add( &a->set, "auto_connect", "true", set_eval_account, a );
+	s->flags |= ACC_SET_NOSAVE;
+	
+	s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a );
+	
+	s = set_add( &a->set, "nick_source", "handle", NULL, a );
+	
+	s = set_add( &a->set, "password", NULL, set_eval_account, a );
+	s->flags |= ACC_SET_NOSAVE | SET_NULL_OK;
+	
+	s = set_add( &a->set, "username", NULL, set_eval_account, a );
+	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
+	set_setstr( &a->set, "username", user );
+	
+	a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free );
+	
+	/* This function adds some more settings (and might want to do more
+	   things that have to be done now, although I can't think of anything. */
+	if( prpl->init )
+		prpl->init( a );
+	
+	s = set_add( &a->set, "away", NULL, set_eval_account, a );
+	s->flags |= SET_NULL_OK;
+	
+	if( a->flags & ACC_FLAG_STATUS_MESSAGE )
+	{
+		s = set_add( &a->set, "status", NULL, set_eval_account, a );
+		s->flags |= SET_NULL_OK;
+	}
+	
+	return a;
+}
+
+char *set_eval_account( set_t *set, char *value )
+{
+	account_t *acc = set->data;
+	
+	/* Double-check: We refuse to edit on-line accounts. */
+	if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic )
+		return SET_INVALID;
+	
+	if( strcmp( set->key, "server" ) == 0 )
+	{
+		g_free( acc->server );
+		if( value && *value )
+		{
+			acc->server = g_strdup( value );
+			return value;
+		}
+		else
+		{
+			acc->server = g_strdup( set->def );
+			return g_strdup( set->def );
+		}
+	}
+	else if( strcmp( set->key, "username" ) == 0 )
+	{
+		g_free( acc->user );
+		acc->user = g_strdup( value );
+		return value;
+	}
+	else if( strcmp( set->key, "password" ) == 0 )
+	{
+		if( value )
+		{
+			g_free( acc->pass );
+			acc->pass = g_strdup( value );
+			return NULL;	/* password shouldn't be visible in plaintext! */
+		}
+		else
+		{
+			/* NULL can (should) be stored in the set_t
+			   variable, but is otherwise not correct. */
+			return SET_INVALID;
+		}
+	}
+	else if( strcmp( set->key, "auto_connect" ) == 0 )
+	{
+		if( !is_bool( value ) )
+			return SET_INVALID;
+		
+		acc->auto_connect = bool2int( value );
+		return value;
+	}
+	else if( strcmp( set->key, "away" ) == 0 ||
+	         strcmp( set->key, "status" ) == 0 )
+	{
+		if( acc->ic && acc->ic->flags & OPT_LOGGED_IN )
+		{
+			/* If we're currently on-line, set the var now already
+			   (bit of a hack) and send an update. */
+			g_free( set->value );
+			set->value = g_strdup( value );
+			
+			imc_away_send_update( acc->ic );
+		}
+		
+		return value;
+	}
+	
+	return SET_INVALID;
+}
+
+account_t *account_get( bee_t *bee, char *id )
+{
+	account_t *a, *ret = NULL;
+	char *handle, *s;
+	int nr;
+	
+	/* This checks if the id string ends with (...) */
+	if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 )
+	{
+		struct prpl *proto;
+		
+		*s = *handle = 0;
+		handle ++;
+		
+		if( ( proto = find_protocol( id ) ) )
+		{
+			for( a = bee->accounts; a; a = a->next )
+				if( a->prpl == proto &&
+				    a->prpl->handle_cmp( handle, a->user ) == 0 )
+					ret = a;
+		}
+		
+		/* Restore the string. */
+		handle --;
+		*handle = '(';
+		*s = ')';
+		
+		if( ret )
+			return ret;
+	}
+	
+	if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 )
+	{
+		for( a = bee->accounts; a; a = a->next )
+			if( ( nr-- ) == 0 )
+				return( a );
+		
+		return( NULL );
+	}
+	
+	for( a = bee->accounts; a; a = a->next )
+	{
+		if( g_strcasecmp( id, a->prpl->name ) == 0 )
+		{
+			if( !ret )
+				ret = a;
+			else
+				return( NULL ); /* We don't want to match more than one... */
+		}
+		else if( strstr( a->user, id ) )
+		{
+			if( !ret )
+				ret = a;
+			else
+				return( NULL );
+		}
+	}
+	
+	return( ret );
+}
+
+void account_del( bee_t *bee, account_t *acc )
+{
+	account_t *a, *l = NULL;
+	
+	if( acc->ic )
+		/* Caller should have checked, accounts still in use can't be deleted. */
+		return;
+	
+	for( a = bee->accounts; a; a = (l=a)->next )
+		if( a == acc )
+		{
+			if( l )
+				l->next = a->next;
+			else
+				bee->accounts = a->next;
+			
+			/** FIXME
+			for( c = bee->chatrooms; c; c = nc )
+			{
+				nc = c->next;
+				if( acc == c->acc )
+					chat_del( bee, c );
+			}
+			*/
+			
+			while( a->set )
+				set_del( &a->set, a->set->key );
+			
+			g_hash_table_destroy( a->nicks );
+			
+			g_free( a->user );
+			g_free( a->pass );
+			g_free( a->server );
+			if( a->reconnect )	/* This prevents any reconnect still queued to happen */
+				cancel_auto_reconnect( a );
+			g_free( a );
+			
+			break;
+		}
+}
+
+void account_on( bee_t *bee, account_t *a )
+{
+	if( a->ic )
+	{
+		/* Trying to enable an already-enabled account */
+		return;
+	}
+	
+	cancel_auto_reconnect( a );
+	
+	a->reconnect = 0;
+	a->prpl->login( a );
+}
+
+void account_off( bee_t *bee, account_t *a )
+{
+	imc_logout( a->ic, FALSE );
+	a->ic = NULL;
+	if( a->reconnect )
+	{
+		/* Shouldn't happen */
+		cancel_auto_reconnect( a );
+	}
+}
+
+struct account_reconnect_delay
+{
+	int start;
+	char op;
+	int step;
+	int max;
+};
+
+int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p )
+{
+	memset( p, 0, sizeof( *p ) );
+	/* A whole day seems like a sane "maximum maximum". */
+	p->max = 86400;
+	
+	/* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
+	while( *value && isdigit( *value ) )
+		p->start = p->start * 10 + *value++ - '0';
+	
+	/* Sure, call me evil for implementing my own fscanf here, but it's
+	   dead simple and I immediately know where to continue parsing. */
+	
+	if( *value == 0 )
+		/* If the string ends now, the delay is constant. */
+		return 1;
+	else if( *value != '+' && *value != '*' )
+		/* Otherwise allow either a + or a * */
+		return 0;
+	
+	p->op = *value++;
+	
+	/* + or * the delay by this number every time. */
+	while( *value && isdigit( *value ) )
+		p->step = p->step * 10 + *value++ - '0';
+	
+	if( *value == 0 )
+		/* Use the default maximum (one day). */
+		return 1;
+	else if( *value != '<' )
+		return 0;
+	
+	p->max = 0;
+	value ++;
+	while( *value && isdigit( *value ) )
+		p->max = p->max * 10 + *value++ - '0';
+	
+	return p->max > 0;
+}
+
+char *set_eval_account_reconnect_delay( set_t *set, char *value )
+{
+	struct account_reconnect_delay p;
+	
+	return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID;
+}
+
+int account_reconnect_delay( account_t *a )
+{
+	char *setting = set_getstr( &a->bee->set, "auto_reconnect_delay" );
+	struct account_reconnect_delay p;
+	
+	if( account_reconnect_delay_parse( setting, &p ) )
+	{
+		if( a->auto_reconnect_delay == 0 )
+			a->auto_reconnect_delay = p.start;
+		else if( p.op == '+' )
+			a->auto_reconnect_delay += p.step;
+		else if( p.op == '*' )
+			a->auto_reconnect_delay *= p.step;
+		
+		if( a->auto_reconnect_delay > p.max )
+			a->auto_reconnect_delay = p.max;
+	}
+	else
+	{
+		a->auto_reconnect_delay = 0;
+	}
+	
+	return a->auto_reconnect_delay;
+}
Index: protocols/account.h
===================================================================
--- protocols/account.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ protocols/account.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,72 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* Account management functions                                         */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef _ACCOUNT_H
+#define _ACCOUNT_H
+
+typedef struct account
+{
+	struct prpl *prpl;
+	char *user;
+	char *pass;
+	char *server;
+	
+	int auto_connect;
+	int auto_reconnect_delay;
+	int reconnect;
+	int flags;
+	
+	set_t *set;
+	GHashTable *nicks;
+	
+	struct bee *bee;
+	struct im_connection *ic;
+	struct account *next;
+} account_t;
+
+account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass );
+account_t *account_get( bee_t *bee, char *id );
+void account_del( bee_t *bee, account_t *acc );
+void account_on( bee_t *bee, account_t *a );
+void account_off( bee_t *bee, account_t *a );
+
+char *set_eval_account( set_t *set, char *value );
+char *set_eval_account_reconnect_delay( set_t *set, char *value );
+int account_reconnect_delay( account_t *a );
+
+typedef enum
+{
+	ACC_SET_NOSAVE = 0x01,          /* Don't save this setting (i.e. stored elsewhere). */
+	ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
+	ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */
+} account_set_flag_t;
+
+typedef enum
+{
+	ACC_FLAG_AWAY_MESSAGE = 0x01,   /* Supports away messages instead of just states. */
+	ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */
+} account_flag_t;
+
+#endif
Index: protocols/bee.c
===================================================================
--- protocols/bee.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ protocols/bee.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,47 @@
+#include "bitlbee.h"
+
+bee_t *bee_new()
+{
+	bee_t *b = g_new0( bee_t, 1 );
+	set_t *s;
+	
+	s = set_add( &b->set, "away", NULL, NULL/*set_eval_away_status*/, b );
+	s->flags |= SET_NULL_OK;
+	s = set_add( &b->set, "auto_connect", "true", set_eval_bool, b );
+	s = set_add( &b->set, "auto_reconnect", "true", set_eval_bool, b );
+	s = set_add( &b->set, "auto_reconnect_delay", "5*3<900", NULL/*set_eval_account_reconnect_delay*/, b );
+	s = set_add( &b->set, "debug", "false", set_eval_bool, b );
+	s = set_add( &b->set, "password", NULL, NULL/*set_eval_password*/, b );
+	s->flags |= SET_NULL_OK;
+	s = set_add( &b->set, "save_on_quit", "true", set_eval_bool, b );
+	s = set_add( &b->set, "status", NULL, NULL/*set_eval_away_status*/, b );
+	s->flags |= SET_NULL_OK;
+	s = set_add( &b->set, "strip_html", "true", NULL, b );
+	
+	return b;
+}
+
+void bee_free( bee_t *b )
+{
+	account_t *acc = b->accounts;
+	
+	while( acc )
+	{
+		if( acc->ic )
+			imc_logout( acc->ic, FALSE );
+		else if( acc->reconnect )
+			cancel_auto_reconnect( acc );
+		
+		if( acc->ic == NULL )
+			account_del( b, acc );
+		else
+			/* Nasty hack, but account_del() doesn't work in this
+			   case and we don't want infinite loops, do we? ;-) */
+			acc = acc->next;
+	}
+	
+	while( b->set )
+		set_del( &b->set, b->set->key );
+	
+	g_free( b );
+}
Index: protocols/bee.h
===================================================================
--- protocols/bee.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ protocols/bee.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,102 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* Stuff to handle, save and search buddies                             */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef __BEE_H__
+#define __BEE_H__
+
+struct bee_ui_funcs;
+
+typedef struct bee
+{
+	struct set *set;
+	
+	GSList *users;
+	struct account *accounts; /* TODO(wilmer): Use GSList here too? */
+	
+	const struct bee_ui_funcs *ui;
+	void *ui_data;
+} bee_t;
+
+bee_t *bee_new();
+void bee_free( bee_t *b );
+
+typedef enum
+{
+	BEE_USER_ONLINE = 1,    /* Compatibility with old OPT_LOGGED_IN flag */
+	BEE_USER_AWAY = 4,      /* Compatibility with old OPT_AWAY flag */
+} bee_user_flags_t;
+
+typedef struct bee_user
+{
+	struct im_connection *ic;
+	char *handle;
+	char *fullname;
+	char *group;
+
+	bee_user_flags_t flags;
+	char *status;
+	char *status_msg;
+	
+	bee_t *bee;
+	void *ui_data;
+} bee_user_t;
+
+typedef struct bee_ui_funcs
+{
+	gboolean (*user_new)( bee_t *bee, struct bee_user *bu );
+	gboolean (*user_free)( bee_t *bee, struct bee_user *bu );
+	gboolean (*user_fullname)( bee_t *bee, bee_user_t *bu );
+	gboolean (*user_status)( bee_t *bee, struct bee_user *bu, struct bee_user *old );
+	gboolean (*user_msg)( bee_t *bee, bee_user_t *bu, const char *msg, time_t sent_at );
+	
+	struct file_transfer* (*ft_in_start)( bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size );
+	gboolean (*ft_out_start)( struct im_connection *ic, struct file_transfer *ft );
+	void (*ft_close)( struct im_connection *ic, struct file_transfer *ft );
+	void (*ft_finished)( struct im_connection *ic, struct file_transfer *ft );
+} bee_ui_funcs_t;
+
+
+/* bee.c */
+bee_t *bee_new();
+void bee_free( bee_t *b );
+
+/* bee_user.c */
+bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle );
+int bee_user_free( bee_t *bee, struct im_connection *ic, const char *handle );
+bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle );
+int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags );
+
+/* Callbacks from IM modules to core: */
+/* Buddy activity */
+/* To manipulate the status of a handle.
+ * - flags can be |='d with OPT_* constants. You will need at least:
+ *   OPT_LOGGED_IN and OPT_AWAY.
+ * - 'state' and 'message' can be NULL */
+G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message );
+/* 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, const char *handle, char *msg, guint32 flags, time_t sent_at );
+
+#endif /* __BEE_H__ */
Index: protocols/bee_ft.c
===================================================================
--- protocols/bee_ft.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ protocols/bee_ft.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,66 @@
+/********************************************************************\
+* BitlBee -- An IRC to other IM-networks gateway                     *
+*                                                                    *
+* Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net>             *
+\********************************************************************/
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define BITLBEE_CORE
+#include "bitlbee.h"
+#include "ft.h"
+
+file_transfer_t *imcb_file_send_start( struct im_connection *ic, char *handle, char *file_name, size_t file_size )
+{
+	bee_t *bee = ic->bee; 
+	bee_user_t *bu = bee_user_by_handle( bee, ic, handle );
+	
+	if( bee->ui->ft_in_start )
+		return bee->ui->ft_in_start( bee, bu, file_name, file_size );
+	else
+		return NULL;
+}
+
+gboolean imcb_file_recv_start( struct im_connection *ic, file_transfer_t *ft )
+{
+	bee_t *bee = ic->bee;
+	
+	if( bee->ui->ft_out_start )
+		return bee->ui->ft_out_start( ic, ft );
+	else
+		return FALSE;
+}
+
+void imcb_file_canceled( struct im_connection *ic, file_transfer_t *file, char *reason )
+{
+	bee_t *bee = ic->bee;
+	
+	if( file->canceled )
+		file->canceled( file, reason );
+	
+	if( bee->ui->ft_close )
+		bee->ui->ft_close( ic, file );
+}
+
+void imcb_file_finished( struct im_connection *ic, file_transfer_t *file )
+{
+	bee_t *bee = ic->bee;
+	
+	if( bee->ui->ft_finished )
+		bee->ui->ft_finished( ic, file );
+}
Index: protocols/bee_user.c
===================================================================
--- protocols/bee_user.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ protocols/bee_user.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,197 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* Stuff to handle, save and search buddies                             */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define BITLBEE_CORE
+#include "bitlbee.h"
+
+bee_user_t *bee_user_new( bee_t *bee, struct im_connection *ic, const char *handle )
+{
+	bee_user_t *bu;
+	
+	if( bee_user_by_handle( bee, ic, handle ) != NULL )
+		return NULL;
+	
+	bu = g_new0( bee_user_t, 1 );
+	bu->bee = bee;
+	bu->ic = ic;
+	bu->handle = g_strdup( handle );
+	bee->users = g_slist_prepend( bee->users, bu );
+	
+	if( bee->ui->user_new )
+		bee->ui->user_new( bee, bu );
+	
+	return bu;
+}
+
+int bee_user_free( bee_t *bee, struct im_connection *ic, const char *handle )
+{
+	bee_user_t *bu;
+	
+	if( ( bu = bee_user_by_handle( bee, ic, handle ) ) == NULL )
+		return 0;
+	
+	if( bee->ui->user_free )
+		bee->ui->user_free( bee, bu );
+	
+	g_free( bu->handle );
+	g_free( bu->fullname );
+	g_free( bu->group );
+	g_free( bu->status );
+	g_free( bu->status_msg );
+	
+	bee->users = g_slist_remove( bee->users, bu );
+	
+	return 1;
+}
+
+bee_user_t *bee_user_by_handle( bee_t *bee, struct im_connection *ic, const char *handle )
+{
+	GSList *l;
+	
+	for( l = bee->users; l; l = l->next )
+	{
+		bee_user_t *bu = l->data;
+		
+		if( bu->ic == ic && ic->acc->prpl->handle_cmp( bu->handle, handle ) == 0 )
+			return bu;
+	}
+	
+	return NULL;
+}
+
+int bee_user_msg( bee_t *bee, bee_user_t *bu, const char *msg, int flags )
+{
+	char *buf = NULL;
+	int st;
+	
+	if( ( bu->ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
+	{
+		buf = escape_html( msg );
+		msg = buf;
+	}
+	else
+		buf = g_strdup( msg );
+	
+	st = bu->ic->acc->prpl->buddy_msg( bu->ic, bu->handle, buf, flags );
+	g_free( buf );
+	
+	return st;
+}
+
+
+/* IM->UI callbacks */
+void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message )
+{
+	bee_t *bee = ic->bee;
+	bee_user_t *bu, *old;
+	
+	if( !( bu = bee_user_by_handle( bee, ic, handle ) ) )
+	{
+		if( g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "add" ) == 0 )
+		{
+			bu = bee_user_new( bee, ic, handle );
+		}
+		else
+		{
+			if( g_strcasecmp( set_getstr( &ic->bee->set, "handle_unknown" ), "ignore" ) != 0 )
+			{
+				imcb_log( ic, "imcb_buddy_status() for unknown handle %s:\n"
+				              "flags = %d, state = %s, message = %s", handle, flags,
+				              state ? state : "NULL", message ? message : "NULL" );
+			}
+			
+			return;
+		}
+	}
+	
+	/* May be nice to give the UI something to compare against. */
+	old = g_memdup( bu, sizeof( bee_user_t ) );
+	
+	/* TODO(wilmer): OPT_AWAY, or just state == NULL ? */
+	bu->flags = flags;
+	bu->status = g_strdup( ( flags & OPT_AWAY ) && state == NULL ? "Away" : state );
+	bu->status_msg = g_strdup( message );
+	
+	if( bee->ui->user_status )
+		bee->ui->user_status( bee, bu, old );
+	
+	g_free( old->status_msg );
+	g_free( old->status );
+	g_free( old );
+#if 0	
+	/* LISPy... */
+	if( ( set_getbool( &ic->bee->set, "away_devoice" ) ) &&		/* Don't do a thing when user doesn't want it */
+	    ( u->online ) &&						/* Don't touch offline people */
+	    ( ( ( u->online != oo ) && !u->away ) ||			/* Voice joining people */
+	      ( ( u->online == oo ) && ( oa == !u->away ) ) ) )		/* (De)voice people changing state */
+	{
+		char *from;
+		
+		if( set_getbool( &ic->bee->set, "simulate_netsplit" ) )
+		{
+			from = g_strdup( ic->irc->myhost );
+		}
+		else
+		{
+			from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick,
+			                                    ic->irc->myhost );
+		}
+		irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel,
+		                                          u->away?'-':'+', u->nick );
+		g_free( from );
+	}
+#endif
+}
+
+void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at )
+{
+	bee_t *bee = ic->bee;
+	bee_user_t *bu;
+	
+	bu = bee_user_by_handle( bee, ic, handle );
+	
+	if( !bu )
+	{
+		char *h = set_getstr( &bee->set, "handle_unknown" );
+		
+		if( g_strcasecmp( h, "ignore" ) == 0 )
+		{
+			return;
+		}
+		else if( g_strncasecmp( h, "add", 3 ) == 0 )
+		{
+			bu = bee_user_new( bee, ic, handle );
+		}
+	}
+	
+	if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) ||
+	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) )
+		strip_html( msg );
+	
+	if( bee->ui->user_msg && bu )
+		bee->ui->user_msg( bee, bu, msg, sent_at );
+	else
+		imcb_log( ic, "Message from unknown handle %s:\n%s", handle, msg );
+}
Index: protocols/chat.c
===================================================================
--- protocols/chat.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ protocols/chat.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,192 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2008 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* Keep track of chatrooms the user is interested in                    */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "bitlbee.h"
+#include "chat.h"
+
+struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel )
+{
+	struct chat *c, *l;
+	set_t *s;
+
+	if( acc->prpl->chat_join == NULL || !chat_chanok( channel ) ||
+	    chat_chancmp( channel, irc->channel ) == 0 )
+	{
+		return NULL;
+	}
+	
+	for( c = irc->chatrooms; c; c = c->next )
+	{
+		if( chat_chancmp( channel, c->channel ) == 0 )
+			return NULL;
+		
+		if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 )
+			return NULL;
+		
+		l = c;
+	}
+	
+	if( irc->chatrooms == NULL )
+		irc->chatrooms = c = g_new0( struct chat, 1 );
+	else
+		l->next = c = g_new0( struct chat, 1 );
+	
+	c->acc = acc;
+	c->handle = g_strdup( handle );
+	c->channel = g_strdup( channel );
+	
+	s = set_add( &c->set, "auto_join", "false", set_eval_bool, c );
+	/* s = set_add( &c->set, "auto_rejoin", "false", set_eval_bool, c ); */
+	s = set_add( &c->set, "nick", NULL, NULL, c );
+	s->flags |= SET_NULL_OK;
+	
+	return c;
+}
+
+struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle )
+{
+	struct chat *c;
+	
+	for( c = irc->chatrooms; c; c = c->next )
+	{
+		if( acc == c->acc && g_strcasecmp( handle, c->handle ) == 0 )
+			break;
+	}
+	
+	return c;
+}
+
+struct chat *chat_bychannel( irc_t *irc, char *channel )
+{
+	struct chat *c;
+	
+	for( c = irc->chatrooms; c; c = c->next )
+	{
+		if( chat_chancmp( channel, c->channel ) == 0 )
+			break;
+	}
+	
+	return c;
+}
+
+struct chat *chat_get( irc_t *irc, char *id )
+{
+	struct chat *c, *ret = NULL;
+	int nr;
+	
+	if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 )
+	{
+		for( c = irc->chatrooms; c; c = c->next )
+			if( ( nr-- ) == 0 )
+				return c;
+		
+		return NULL;
+	}
+	
+	for( c = irc->chatrooms; c; c = c->next )
+	{
+		if( strstr( c->handle, id ) )
+		{
+			if( !ret )
+				ret = c;
+			else
+				return NULL;
+		}
+		else if( strstr( c->channel, id ) )
+		{
+			if( !ret )
+				ret = c;
+			else
+				return NULL;
+		}
+	}
+	
+	return ret;
+}
+
+int chat_del( irc_t *irc, struct chat *chat )
+{
+	struct chat *c, *l = NULL;
+	
+	for( c = irc->chatrooms; c; c = (l=c)->next )
+		if( c == chat )
+			break;
+	
+	if( c == NULL )
+		return 0;
+	else if( l == NULL )
+		irc->chatrooms = c->next;
+	else
+		l->next = c->next;
+	
+	while( c->set )
+		set_del( &c->set, c->set->key );
+	
+	g_free( c->handle );
+	g_free( c->channel );
+	g_free( c );
+	
+	return 1;
+}
+
+int chat_chancmp( char *a, char *b )
+{
+	if( !chat_chanok( a ) || !chat_chanok( b ) )
+		return 0;
+	
+	if( a[0] == b[0] )
+		return nick_cmp( a + 1, b + 1 );
+	else
+		return -1;
+}
+
+int chat_chanok( char *a )
+{
+	if( strchr( CTYPES, a[0] ) != NULL )
+		return nick_ok( a + 1 );
+	else
+		return 0;
+}
+
+int chat_join( irc_t *irc, struct chat *c, const char *password )
+{
+	struct groupchat *gc;
+	char *nick = set_getstr( &c->set, "nick" );
+
+	if( c->acc->ic == NULL || c->acc->prpl->chat_join == NULL )
+		return 0;
+	
+	if( nick == NULL )
+		nick = irc->nick;
+	
+	if( ( gc = c->acc->prpl->chat_join( c->acc->ic, c->handle, nick, password ) ) )
+	{
+		g_free( gc->channel );
+		gc->channel = g_strdup( c->channel );
+		return 1;
+	}
+	
+	return 0;
+}
Index: protocols/chat.h
===================================================================
--- protocols/chat.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
+++ protocols/chat.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -0,0 +1,51 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2008 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* Keep track of chatrooms the user is interested in                    */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#ifndef _CHAT_H
+#define _CHAT_H
+
+struct chat
+{
+	account_t *acc;
+	
+	char *handle;
+	char *channel;
+	set_t *set;
+	
+	struct chat *next;
+};
+
+struct chat *chat_add( irc_t *irc, account_t *acc, char *handle, char *channel );
+struct chat *chat_byhandle( irc_t *irc, account_t *acc, char *handle );
+struct chat *chat_bychannel( irc_t *irc, char *channel );
+struct chat *chat_get( irc_t *irc, char *id );
+int chat_del( irc_t *irc, struct chat *chat );
+
+int chat_chancmp( char *a, char *b );
+int chat_chanok( char *a );
+
+int chat_join( irc_t *irc, struct chat *c, const char *password );
+
+#endif
Index: protocols/ft.h
===================================================================
--- protocols/ft.h	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/ft.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -168,8 +168,8 @@
  * the canceled() and free() callbacks given in file will be called by this function.
  */
-void imcb_file_canceled( file_transfer_t *file, char *reason );
+void imcb_file_canceled( struct im_connection *ic, file_transfer_t *file, char *reason );
 
-gboolean imcb_file_recv_start( file_transfer_t *ft );
+gboolean imcb_file_recv_start( struct im_connection *ic, file_transfer_t *ft );
 
-void imcb_file_finished( file_transfer_t *file );
+void imcb_file_finished( struct im_connection *ic, file_transfer_t *file );
 #endif
Index: protocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/jabber/iq.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -392,5 +392,5 @@
 			if( ( strcmp( sub, "both" ) == 0 || strcmp( sub, "to" ) == 0 ) )
 			{
-				if( initial || imcb_find_buddy( ic, jid ) == NULL )
+				if( initial || bee_user_by_handle( ic->bee, ic, jid ) == NULL )
 					imcb_add_buddy( ic, jid, ( group && group->text_len ) ?
 					                           group->text : NULL );
@@ -590,5 +590,5 @@
 	    strcmp( s, "result" ) == 0 )
 	{
-		if( imcb_find_buddy( ic, jid ) == NULL )
+		if( bee_user_by_handle( ic->bee, ic, jid ) == NULL )
 			imcb_add_buddy( ic, jid, NULL );
 	}
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/jabber/jabber.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -267,5 +267,5 @@
 	
 	while( jd->filetransfers )
-		imcb_file_canceled( ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" );
+		imcb_file_canceled( ic, ( ( struct jabber_transfer *) jd->filetransfers->data )->ft, "Logging out" );
 
 	while( jd->streamhosts )
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/jabber/jabber_util.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -279,6 +279,5 @@
 	presence_send_request( bla->ic, bla->handle, "subscribed" );
 	
-	if( imcb_find_buddy( bla->ic, bla->handle ) == NULL )
-		imcb_ask_add( bla->ic, bla->handle, NULL );
+	imcb_ask_add( bla->ic, bla->handle, NULL );
 	
 	g_free( bla->handle );
@@ -462,5 +461,5 @@
 		
 		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) &&
-		    ( bare_exists || imcb_find_buddy( ic, jid ) ) )
+		    ( bare_exists || bee_user_by_handle( ic->bee, ic, jid ) ) )
 		{
 			*s = '/';
@@ -483,5 +482,6 @@
 		if( bud == NULL )
 			/* No match. Create it now? */
-			return ( ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid_ ) ) ?
+			return ( ( flags & GET_BUDDY_CREAT ) &&
+			         bee_user_by_handle( ic->bee, ic, jid_ ) ) ?
 			           jabber_buddy_add( ic, jid_ ) : NULL;
 		else if( bud->resource && ( flags & GET_BUDDY_EXACT ) )
Index: protocols/jabber/s5bytestream.c
===================================================================
--- protocols/jabber/s5bytestream.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/jabber/s5bytestream.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -567,5 +567,5 @@
 	xt_free_node( reply );
 
-	imcb_file_canceled( tf->ft, "couldn't connect to any streamhosts" );
+	imcb_file_canceled( tf->ic, tf->ft, "couldn't connect to any streamhosts" );
 
 	bt->tf->watch_in = 0;
@@ -604,5 +604,5 @@
 		
 	if( !jabber_write_packet( tf->ic, reply ) )
-		imcb_file_canceled( tf->ft, "Error transmitting bytestream response" );
+		imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream response" );
 	xt_free_node( reply );
 }
@@ -644,5 +644,5 @@
 
 	if( tf->bytesread >= tf->ft->file_size )
-		imcb_file_finished( tf->ft );
+		imcb_file_finished( tf->ic, tf->ft );
 
 	tf->ft->write( tf->ft, tf->ft->buffer, ret );	
@@ -660,5 +660,5 @@
 	if( tf->watch_in )
 	{
-		imcb_file_canceled( ft, "BUG in jabber file transfer: write_request called when already watching for input" );
+		imcb_file_canceled( tf->ic, ft, "BUG in jabber file transfer: write_request called when already watching for input" );
 		return FALSE;
 	}
@@ -706,5 +706,5 @@
 
 	if( tf->byteswritten >= ft->file_size )
-		imcb_file_finished( ft );
+		imcb_file_finished( tf->ic, ft );
 	else
 		bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt );
@@ -1006,5 +1006,5 @@
 
 	if( !jabber_write_packet( tf->ic, iq ) )
-		imcb_file_canceled( tf->ft, "Error transmitting bytestream request" );
+		imcb_file_canceled( tf->ic, tf->ft, "Error transmitting bytestream request" );
 	return TRUE;
 }
@@ -1021,5 +1021,5 @@
 
 	if( jd->streamhosts==NULL ) /* we're done here unless we have a proxy to try */
-		imcb_file_canceled( tf->ft, error );
+		imcb_file_canceled( tf->ic, tf->ft, error );
 
 	/* MUST always return FALSE! */
Index: protocols/jabber/si.c
===================================================================
--- protocols/jabber/si.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/jabber/si.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -91,9 +91,9 @@
 
 	if( !foundft )
-		imcb_file_canceled( tf->ft, "Buddy's client doesn't feature file transfers" );
+		imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature file transfers" );
 	else if( !foundbt )
-		imcb_file_canceled( tf->ft, "Buddy's client doesn't feature byte streams (required)" );
+		imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature byte streams (required)" );
 	else if( !foundsi )
-		imcb_file_canceled( tf->ft, "Buddy's client doesn't feature stream initiation (required)" );
+		imcb_file_canceled( tf->ic, tf->ft, "Buddy's client doesn't feature stream initiation (required)" );
 		
 	return foundft && foundbt && foundsi;
@@ -109,5 +109,5 @@
 
 	/* and start the receive logic */
-	imcb_file_recv_start( tf->ft );
+	imcb_file_recv_start( tf->ic, tf->ft );
 
 }
@@ -156,5 +156,5 @@
 	if( bud == NULL )
 	{
-		imcb_file_canceled( ft, "Couldn't find buddy (BUG?)" );
+		imcb_file_canceled( ic, ft, "Couldn't find buddy (BUG?)" );
 		return;
 	}
Index: protocols/msn/Makefile
===================================================================
--- protocols/msn/Makefile	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/msn/Makefile	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -10,5 +10,5 @@
 
 # [SH] Program variables
-objects = invitation.o msn.o msn_util.o ns.o passport.o sb.o tables.o
+objects = msn.o msn_util.o ns.o passport.o sb.o tables.o
 
 CFLAGS += -Wall
Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/msn/msn.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -81,7 +81,9 @@
 	if( md )
 	{
+		/** Disabling MSN ft support for now.
 		while( md->filetransfers ) {
 			imcb_file_canceled( md->filetransfers->data, "Closing connection" );
 		}
+		*/
 		
 		if( md->fd >= 0 )
@@ -344,5 +346,5 @@
 	ret->send_typing = msn_send_typing;
 	ret->handle_cmp = g_strcasecmp;
-	ret->transfer_request = msn_ftp_transfer_request;
+	//ret->transfer_request = msn_ftp_transfer_request;
 
 	register_protocol(ret);
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/msn/msn_util.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -96,6 +96,5 @@
 	msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname );
 	
-	if( imcb_find_buddy( bla->ic, bla->handle ) == NULL )
-		imcb_ask_add( bla->ic, bla->handle, NULL );
+	imcb_ask_add( bla->ic, bla->handle, NULL );
 	
 	g_free( bla->handle );
Index: protocols/msn/sb.c
===================================================================
--- protocols/msn/sb.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/msn/sb.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -691,4 +691,6 @@
 			}
 		}
+#if 0
+		// Disable MSN ft support for now.
 		else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 )
 		{
@@ -723,4 +725,5 @@
 			g_free( command );
 		}
+#endif
 		else if( g_strncasecmp( ct, "application/x-msnmsgrp2p", 24 ) == 0 ) 
 		{
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/nogaim.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -38,6 +38,4 @@
 #include "chat.h"
 
-static int remove_chat_buddy_silent( struct groupchat *b, const char *handle );
-
 GSList *connections;
 
@@ -91,6 +89,4 @@
 }
 #endif
-
-/* nogaim.c */
 
 GList *protocols = NULL;
@@ -125,5 +121,4 @@
 }
 
-/* nogaim.c */
 void nogaim_init()
 {
@@ -156,6 +151,4 @@
 GSList *get_connections() { return connections; }
 
-/* multi.c */
-
 struct im_connection *imcb_new( account_t *acc )
 {
@@ -164,5 +157,5 @@
 	ic = g_new0( struct im_connection, 1 );
 	
-	ic->irc = acc->irc;
+	ic->bee = acc->bee;
 	ic->acc = acc;
 	acc->ic = ic;
@@ -178,5 +171,5 @@
 	
 	/* Destroy the pointer to this connection from the account list */
-	for( a = ic->irc->accounts; a; a = a->next )
+	for( a = ic->bee->accounts; a; a = a->next )
 		if( a->ic == ic )
 		{
@@ -199,10 +192,10 @@
 	va_end( params );
 
-	if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
-	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
+	if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) ||
+	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) )
 		strip_html( text );
 	
 	/* Try to find a different connection on the same protocol. */
-	for( a = ic->irc->accounts; a; a = a->next )
+	for( a = ic->bee->accounts; a; a = a->next )
 		if( a->prpl == ic->acc->prpl && a->ic != ic )
 			break;
@@ -210,7 +203,8 @@
 	/* If we found one, include the screenname in the message. */
 	if( a )
-		irc_usermsg( ic->irc, "%s(%s) - %s", ic->acc->prpl->name, ic->acc->user, text );
+		/* FIXME(wilmer): ui_log callback or so */
+		irc_usermsg( ic->bee->ui_data, "%s(%s) - %s", ic->acc->prpl->name, ic->acc->user, text );
 	else
-		irc_usermsg( ic->irc, "%s - %s", ic->acc->prpl->name, text );
+		irc_usermsg( ic->bee->ui_data, "%s - %s", ic->acc->prpl->name, text );
 	
 	g_free( text );
@@ -263,8 +257,4 @@
 void imcb_connected( struct im_connection *ic )
 {
-	irc_t *irc = ic->irc;
-	struct chat *c;
-	user_t *u;
-	
 	/* MSN servers sometimes redirect you to a different server and do
 	   the whole login sequence again, so these "late" calls to this
@@ -273,6 +263,4 @@
 		return;
 	
-	u = user_find( ic->irc, ic->irc->nick );
-	
 	imcb_log( ic, "Logged in" );
 	
@@ -287,4 +275,5 @@
 	ic->acc->auto_reconnect_delay = 0;
 	
+	/*
 	for( c = irc->chatrooms; c; c = c->next )
 	{
@@ -295,4 +284,5 @@
 			chat_join( irc, c, NULL );
 	}
+	*/
 }
 
@@ -302,5 +292,5 @@
 	
 	a->reconnect = 0;
-	account_on( a->irc, a );
+	account_on( a->bee, a );
 	
 	return( FALSE );	/* Only have to run the timeout once */
@@ -315,7 +305,7 @@
 void imc_logout( struct im_connection *ic, int allow_reconnect )
 {
-	irc_t *irc = ic->irc;
-	user_t *t, *u;
+	bee_t *bee = ic->bee;
 	account_t *a;
+	GSList *l;
 	int delay;
 	
@@ -337,20 +327,15 @@
 	ic->away = NULL;
 	
-	u = irc->users;
-	while( u )
-	{
-		if( u->ic == ic )
-		{
-			t = u->next;
-			user_del( irc, u->nick );
-			u = t;
-		}
-		else
-			u = u->next;
-	}
-	
-	query_del_by_conn( ic->irc, ic );
-	
-	for( a = irc->accounts; a; a = a->next )
+	for( l = bee->users; l; l = l->next )
+	{
+		bee_user_t *bu = l->data;
+		
+		if( bu->ic == ic )
+			bee_user_free( bee, ic, bu->handle );
+	}
+	
+	//query_del_by_conn( ic->irc, ic );
+	
+	for( a = bee->accounts; a; a = a->next )
 		if( a->ic == ic )
 			break;
@@ -360,5 +345,5 @@
 		/* Uhm... This is very sick. */
 	}
-	else if( allow_reconnect && set_getbool( &irc->set, "auto_reconnect" ) &&
+	else if( allow_reconnect && set_getbool( &bee->set, "auto_reconnect" ) &&
 	         set_getbool( &a->set, "auto_reconnect" ) &&
 	         ( delay = account_reconnect_delay( a ) ) > 0 )
@@ -371,25 +356,18 @@
 }
 
-
-/* dialogs.c */
-
 void imcb_ask( struct im_connection *ic, char *msg, void *data,
                query_callback doit, query_callback dont )
 {
-	query_add( ic->irc, ic, msg, doit, dont, data );
-}
-
-
-/* list.c */
+	//query_add( ic->irc, ic, msg, doit, dont, data );
+}
 
 void imcb_add_buddy( struct im_connection *ic, const char *handle, const char *group )
 {
-	user_t *u;
-	char nick[MAX_NICK_LENGTH+1], *s;
-	irc_t *irc = ic->irc;
-	
-	if( user_findhandle( ic, handle ) )
-	{
-		if( set_getbool( &irc->set, "debug" ) )
+	bee_user_t *bu;
+	bee_t *bee = ic->bee;
+	
+	if( bee_user_by_handle( bee, ic, handle ) )
+	{
+		if( set_getbool( &bee->set, "debug" ) )
 			imcb_log( ic, "User already exists, ignoring add request: %s", handle );
 		
@@ -402,91 +380,22 @@
 	}
 	
-	memset( nick, 0, MAX_NICK_LENGTH + 1 );
-	strcpy( nick, nick_get( ic->acc, handle ) );
-	
-	u = user_add( ic->irc, nick );
-	
-//	if( !realname || !*realname ) realname = nick;
-//	u->realname = g_strdup( realname );
-	
-	if( ( s = strchr( handle, '@' ) ) )
-	{
-		u->host = g_strdup( s + 1 );
-		u->user = g_strndup( handle, s - handle );
-	}
-	else if( ic->acc->server )
-	{
-		u->host = g_strdup( ic->acc->server );
-		u->user = g_strdup( handle );
-		
-		/* s/ /_/ ... important for AOL screennames */
-		for( s = u->user; *s; s ++ )
-			if( *s == ' ' )
-				*s = '_';
-	}
-	else
-	{
-		u->host = g_strdup( ic->acc->prpl->name );
-		u->user = g_strdup( handle );
-	}
-	
-	u->ic = ic;
-	u->handle = g_strdup( handle );
-	if( group ) u->group = g_strdup( group );
-	u->send_handler = buddy_send_handler;
-	u->last_typing_notice = 0;
-}
-
-struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle )
-{
-	static struct buddy b[1];
-	user_t *u;
-	
-	u = user_findhandle( ic, handle );
-	
-	if( !u )
-		return( NULL );
-	
-	memset( b, 0, sizeof( b ) );
-	strncpy( b->name, handle, 80 );
-	strncpy( b->show, u->realname, BUDDY_ALIAS_MAXLEN );
-	b->present = u->online;
-	b->ic = u->ic;
-	
-	return( b );
-}
-
-void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname )
-{
-	user_t *u = user_findhandle( ic, handle );
-	char *set;
-	
-	if( !u || !realname ) return;
-	
-	if( g_strcasecmp( u->realname, realname ) != 0 )
-	{
-		if( u->realname != u->nick ) g_free( u->realname );
-		
-		u->realname = g_strdup( realname );
-		
-		if( ( ic->flags & OPT_LOGGED_IN ) && set_getbool( &ic->irc->set, "display_namechanges" ) )
-			imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname );
-	}
-	
-	set = set_getstr( &ic->acc->set, "nick_source" );
-	if( strcmp( set, "handle" ) != 0 )
-	{
-		char *name = g_strdup( realname );
-		
-		if( strcmp( set, "first_name" ) == 0 )
-		{
-			int i;
-			for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {}
-			name[i] = '\0';
-		}
-		
-		imcb_buddy_nick_hint( ic, handle, name );
-		
-		g_free( name );
+	bu = bee_user_new( bee, ic, handle );
+	bu->group = g_strdup( group );
+}
+
+void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *fullname )
+{
+	bee_t *bee = ic->bee;
+	bee_user_t *bu = bee_user_by_handle( bee, ic, handle );
+	
+	if( !bu || !fullname ) return;
+	
+	if( !bu->fullname || strcmp( bu->fullname, fullname ) != 0 )
+	{
+		g_free( bu->fullname );
+		bu->fullname = g_strdup( fullname );
+		
+		if( bee->ui->user_fullname )
+			bee->ui->user_fullname( bee, bu );
 	}
 }
@@ -494,8 +403,5 @@
 void imcb_remove_buddy( struct im_connection *ic, const char *handle, char *group )
 {
-	user_t *u;
-	
-	if( ( u = user_findhandle( ic, handle ) ) )
-		user_del( ic->irc, u->nick );
+	bee_user_free( ic->bee, ic, handle );
 }
 
@@ -504,4 +410,5 @@
 void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick )
 {
+#if 0
 	user_t *u = user_findhandle( ic, handle );
 	char newnick[MAX_NICK_LENGTH+1], *orig_nick;
@@ -518,5 +425,5 @@
 		/* Some processing to make sure this string is a valid IRC nickname. */
 		nick_strip( newnick );
-		if( set_getbool( &ic->irc->set, "lcnicks" ) )
+		if( set_getbool( &ic->bee->set, "lcnicks" ) )
 			nick_lc( newnick );
 		
@@ -535,4 +442,5 @@
 		}
 	}
+#endif
 }
 
@@ -544,4 +452,5 @@
 };
 
+#if 0
 static void imcb_ask_auth_cb_no( void *data )
 {
@@ -563,7 +472,9 @@
 	g_free( cbd );
 }
+#endif
 
 void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname )
 {
+#if 0
 	struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 );
 	char *s, *realname_ = NULL;
@@ -580,7 +491,9 @@
 	data->handle = g_strdup( handle );
 	query_add( ic->irc, ic, s, imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, data );
-}
-
-
+#endif
+}
+
+
+#if 0
 static void imcb_ask_add_cb_no( void *data )
 {
@@ -597,7 +510,9 @@
 	return imcb_ask_add_cb_no( data );
 }
+#endif
 
 void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname )
 {
+#if 0
 	struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 );
 	char *s;
@@ -612,163 +527,13 @@
 	data->handle = g_strdup( handle );
 	query_add( ic->irc, ic, s, imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data );
-}
-
-
-/* server.c */                    
-
-void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message )
-{
+#endif
+}
+
+void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags )
+{
+#if 0
 	user_t *u;
-	int oa, oo;
-	
-	u = user_findhandle( ic, (char*) handle );
-	
-	if( !u )
-	{
-		if( g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "add" ) == 0 )
-		{
-			imcb_add_buddy( ic, (char*) handle, NULL );
-			u = user_findhandle( ic, (char*) handle );
-		}
-		else
-		{
-			if( set_getbool( &ic->irc->set, "debug" ) || g_strcasecmp( set_getstr( &ic->irc->set, "handle_unknown" ), "ignore" ) != 0 )
-			{
-				imcb_log( ic, "imcb_buddy_status() for unknown handle %s:", handle );
-				imcb_log( ic, "flags = %d, state = %s, message = %s", flags,
-				          state ? state : "NULL", message ? message : "NULL" );
-			}
-			
-			return;
-		}
-	}
-	
-	oa = u->away != NULL;
-	oo = u->online;
-	
-	g_free( u->away );
-	g_free( u->status_msg );
-	u->away = u->status_msg = NULL;
-	
-	if( ( flags & OPT_LOGGED_IN ) && !u->online )
-	{
-		irc_spawn( ic->irc, u );
-		u->online = 1;
-	}
-	else if( !( flags & OPT_LOGGED_IN ) && u->online )
-	{
-		struct groupchat *c;
-		
-		irc_kill( ic->irc, u );
-		u->online = 0;
-		
-		/* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */
-		for( c = ic->groupchats; c; c = c->next )
-			remove_chat_buddy_silent( c, handle );
-	}
-	
-	if( flags & OPT_AWAY )
-	{
-		if( state && message )
-		{
-			u->away = g_strdup_printf( "%s (%s)", state, message );
-		}
-		else if( state )
-		{
-			u->away = g_strdup( state );
-		}
-		else if( message )
-		{
-			u->away = g_strdup( message );
-		}
-		else
-		{
-			u->away = g_strdup( "Away" );
-		}
-	}
-	else
-	{
-		u->status_msg = g_strdup( message );
-	}
-	
-	/* LISPy... */
-	if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) &&		/* Don't do a thing when user doesn't want it */
-	    ( u->online ) &&						/* Don't touch offline people */
-	    ( ( ( u->online != oo ) && !u->away ) ||			/* Voice joining people */
-	      ( ( u->online == oo ) && ( oa == !u->away ) ) ) )		/* (De)voice people changing state */
-	{
-		char *from;
-		
-		if( set_getbool( &ic->irc->set, "simulate_netsplit" ) )
-		{
-			from = g_strdup( ic->irc->myhost );
-		}
-		else
-		{
-			from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick,
-			                                    ic->irc->myhost );
-		}
-		irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel,
-		                                          u->away?'-':'+', u->nick );
-		g_free( from );
-	}
-}
-
-void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at )
-{
-	irc_t *irc = ic->irc;
-	char *wrapped;
-	user_t *u;
-	
-	u = user_findhandle( ic, handle );
-	
-	if( !u )
-	{
-		char *h = set_getstr( &irc->set, "handle_unknown" );
-		
-		if( g_strcasecmp( h, "ignore" ) == 0 )
-		{
-			if( set_getbool( &irc->set, "debug" ) )
-				imcb_log( ic, "Ignoring message from unknown handle %s", handle );
-			
-			return;
-		}
-		else if( g_strncasecmp( h, "add", 3 ) == 0 )
-		{
-			int private = set_getbool( &irc->set, "private" );
-			
-			if( h[3] )
-			{
-				if( g_strcasecmp( h + 3, "_private" ) == 0 )
-					private = 1;
-				else if( g_strcasecmp( h + 3, "_channel" ) == 0 )
-					private = 0;
-			}
-			
-			imcb_add_buddy( ic, handle, NULL );
-			u = user_findhandle( ic, handle );
-			u->is_private = private;
-		}
-		else
-		{
-			imcb_log( ic, "Message from unknown handle %s:", handle );
-			u = user_find( irc, irc->mynick );
-		}
-	}
-	
-	if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
-	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
-		strip_html( msg );
-
-	wrapped = word_wrap( msg, 425 );
-	irc_msgfrom( irc, u->nick, wrapped );
-	g_free( wrapped );
-}
-
-void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags )
-{
-	user_t *u;
-	
-	if( !set_getbool( &ic->irc->set, "typing_notice" ) )
+	
+	if( !set_getbool( &ic->bee->set, "typing_notice" ) )
 		return;
 	
@@ -780,8 +545,15 @@
 		irc_privmsg( ic->irc, u, "PRIVMSG", ic->irc->nick, NULL, buf );
 	}
+#endif
+}
+
+struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *handle )
+{
+	return bee_user_by_handle( ic->bee, ic, handle );
 }
 
 struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle )
 {
+#if 0
 	struct groupchat *c;
 	
@@ -801,17 +573,20 @@
 	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" ) )
+	if( set_getbool( &ic->bee->set, "debug" ) )
 		imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle );
 	
 	return c;
+#endif
+	return NULL;
 }
 
 void imcb_chat_free( struct groupchat *c )
 {
+#if 0
 	struct im_connection *ic = c->ic;
 	struct groupchat *l;
 	GList *ir;
 	
-	if( set_getbool( &ic->irc->set, "debug" ) )
+	if( set_getbool( &ic->bee->set, "debug" ) )
 		imcb_log( ic, "You were removed from conversation %p", c );
 	
@@ -846,8 +621,10 @@
 		g_free( c );
 	}
+#endif
 }
 
 void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t flags, time_t sent_at )
 {
+#if 0
 	struct im_connection *ic = c->ic;
 	char *wrapped;
@@ -860,6 +637,6 @@
 	u = user_findhandle( ic, who );
 	
-	if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
-	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
+	if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) ||
+	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) )
 		strip_html( msg );
 	
@@ -874,8 +651,10 @@
 	}
 	g_free( wrapped );
+#endif
 }
 
 void imcb_chat_log( struct groupchat *c, char *format, ... )
 {
+#if 0
 	irc_t *irc = c->ic->irc;
 	va_list params;
@@ -892,8 +671,10 @@
 	
 	g_free( text );
+#endif
 }
 
 void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at )
 {
+#if 0
 	struct im_connection *ic = c->ic;
 	user_t *u = NULL;
@@ -906,6 +687,6 @@
 		u = user_findhandle( ic, who );
 	
-	if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) ||
-	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) )
+	if( ( g_strcasecmp( set_getstr( &ic->bee->set, "strip_html" ), "always" ) == 0 ) ||
+	    ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->bee->set, "strip_html" ) ) )
 		strip_html( topic );
 	
@@ -915,15 +696,14 @@
 	if( c->joined && u )
 		irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic );
-}
-
-
-/* buddy_chat.c */
+#endif
+}
 
 void imcb_chat_add_buddy( struct groupchat *b, const char *handle )
 {
+#if 0
 	user_t *u = user_findhandle( b->ic, handle );
 	int me = 0;
 	
-	if( set_getbool( &b->ic->irc->set, "debug" ) )
+	if( set_getbool( &b->ic->bee->set, "debug" ) )
 		imcb_log( b->ic, "User %s added to conversation %p", handle, b );
 	
@@ -952,4 +732,5 @@
 		b->in_room = g_list_append( b->in_room, g_strdup( handle ) );
 	}
+#endif
 }
 
@@ -957,8 +738,9 @@
 void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason )
 {
+#if 0
 	user_t *u;
 	int me = 0;
 	
-	if( set_getbool( &b->ic->irc->set, "debug" ) )
+	if( set_getbool( &b->ic->bee->set, "debug" ) )
 		imcb_log( b->ic, "User %s removed from conversation %p (%s)", handle, b, reason ? reason : "" );
 	
@@ -980,6 +762,8 @@
 	if( me || ( remove_chat_buddy_silent( b, handle ) && b->joined && u ) )
 		irc_part( b->ic->irc, u, b->channel );
-}
-
+#endif
+}
+
+#if 0
 static int remove_chat_buddy_silent( struct groupchat *b, const char *handle )
 {
@@ -1000,10 +784,11 @@
 	}
 	
-	return( 0 );
-}
+	return 0;
+}
+#endif
 
 
 /* Misc. BitlBee stuff which shouldn't really be here */
-
+#if 0
 char *set_eval_away_devoice( set_t *set, char *value )
 {
@@ -1018,5 +803,5 @@
 	/* Horror.... */
 	
-	if( st != set_getbool( &irc->set, "away_devoice" ) )
+	if( st != set_getbool( &irc->b->set, "away_devoice" ) )
 	{
 		char list[80] = "";
@@ -1060,5 +845,5 @@
 	return value;
 }
-
+#endif
 
 
@@ -1067,10 +852,9 @@
    them all from some wrappers. We'll start to define some down here: */
 
-int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags )
+int imc_chat_msg( struct groupchat *c, char *msg, int flags )
 {
 	char *buf = NULL;
-	int st;
-	
-	if( ( ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
+	
+	if( ( c->ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
 	{
 		buf = escape_html( msg );
@@ -1078,20 +862,4 @@
 	}
 	
-	st = ic->acc->prpl->buddy_msg( ic, handle, msg, flags );
-	g_free( buf );
-	
-	return st;
-}
-
-int imc_chat_msg( struct groupchat *c, char *msg, int flags )
-{
-	char *buf = NULL;
-	
-	if( ( c->ic->flags & OPT_DOES_HTML ) && ( g_strncasecmp( msg, "<html>", 6 ) != 0 ) )
-	{
-		buf = escape_html( msg );
-		msg = buf;
-	}
-	
 	c->ic->acc->prpl->chat_msg( c, msg, flags );
 	g_free( buf );
@@ -1107,5 +875,5 @@
 	
 	away = set_getstr( &ic->acc->set, "away" ) ?
-	     : set_getstr( &ic->irc->set, "away" );
+	     : set_getstr( &ic->bee->set, "away" );
 	if( away && *away )
 	{
@@ -1118,5 +886,5 @@
 		away = NULL;
 		msg = set_getstr( &ic->acc->set, "status" ) ?
-		    : set_getstr( &ic->irc->set, "status" );
+		    : set_getstr( &ic->bee->set, "status" );
 	}
 	
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/nogaim.h	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -87,5 +87,5 @@
 	
 	/* BitlBee */
-	irc_t *irc;
+	bee_t *bee;
 	
 	struct groupchat *groupchats;
@@ -286,14 +286,6 @@
 G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick );
 
-/* Buddy activity */
-/* To manipulate the status of a handle.
- * - flags can be |='d with OPT_* constants. You will need at least:
- *   OPT_LOGGED_IN and OPT_AWAY.
- * - 'state' and 'message' can be NULL */
-G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message );
-/* 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, const 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 struct bee_user *imcb_buddy_by_handle( struct im_connection *ic, const char *handle );
 G_MODULE_EXPORT void imcb_clean_handle( struct im_connection *ic, char *handle );
 
@@ -320,5 +312,4 @@
 /* Actions, or whatever. */
 int imc_away_send_update( struct im_connection *ic );
-int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags );
 int imc_chat_msg( struct groupchat *c, char *msg, int flags );
 
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ protocols/oscar/oscar.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -1190,6 +1190,5 @@
 	aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
 	// aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
-	if(imcb_find_buddy(data->ic, uin) == NULL)
-		imcb_ask_add(data->ic, uin, NULL);
+	imcb_ask_add(data->ic, uin, NULL);
 	
 	g_free(uin);
@@ -1952,4 +1951,5 @@
 	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
 	if (odata->icq) {
+		/** FIXME(wilmer): Hmm, lost the ability to get away msgs here, do we care to get that back?
 		struct buddy *budlight = imcb_find_buddy(g, who);
 		if (budlight)
@@ -1957,4 +1957,5 @@
 				if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
 					aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
+		*/
 	} else
 		aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
@@ -2094,5 +2095,5 @@
 		switch (curitem->type) {
 			case 0x0000: /* Buddy */
-				if ((curitem->name) && (!imcb_find_buddy(ic, nrm))) {
+				if ((curitem->name) && (!imcb_buddy_by_handle(ic, nrm))) {
 					char *realname = NULL;
 
Index: root_commands.c
===================================================================
--- root_commands.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ root_commands.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -32,5 +32,5 @@
 #include <string.h>
 
-void root_command_string( irc_t *irc, user_t *u, char *command, int flags )
+void root_command_string( irc_t *irc, char *command )
 {
 	char *cmd[IRC_MAX_ARGS];
@@ -161,5 +161,5 @@
 		irc->status |= USTATUS_IDENTIFIED;
 		irc_umode_set( irc, "+R", 1 );
-		if( set_getbool( &irc->set, "auto_connect" ) )
+		if( set_getbool( &irc->b->set, "auto_connect" ) )
 			cmd_account( irc, account_on );
 		break;
@@ -201,5 +201,5 @@
 	storage_status_t status;
 	
-	status = storage_remove (irc->nick, cmd[1]);
+	status = storage_remove (irc->user->nick, cmd[1]);
 	switch (status) {
 	case STORAGE_NO_SUCH_USER:
@@ -213,5 +213,5 @@
 		irc->status &= ~USTATUS_IDENTIFIED;
 		irc_umode_set( irc, "-R", 1 );
-		irc_usermsg( irc, "Account `%s' removed", irc->nick );
+		irc_usermsg( irc, "Account `%s' removed", irc->user->nick );
 		break;
 	default:
@@ -221,4 +221,14 @@
 }
 
+static void cmd_save( irc_t *irc, char **cmd )
+{
+	if( ( irc->status & USTATUS_IDENTIFIED ) == 0 )
+		irc_usermsg( irc, "Please create an account first" );
+	else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK )
+		irc_usermsg( irc, "Configuration saved" );
+	else
+		irc_usermsg( irc, "Configuration could not be saved!" );
+}
+
 struct cmd_account_del_data
 {
@@ -232,5 +242,5 @@
 	account_t *a;
 	
-	for( a = cad->irc->accounts; a && a != cad->a; a = a->next );
+	for( a = cad->irc->b->accounts; a && a != cad->a; a = a->next );
 	
 	if( a == NULL )
@@ -244,5 +254,5 @@
 	else
 	{
-		account_del( cad->irc, a );
+		account_del( cad->irc->b, a );
 		irc_usermsg( cad->irc, "Account deleted" );
 	}
@@ -285,5 +295,5 @@
 		set_name = set_full;
 		
-		head = &irc->set;
+		head = &irc->b->set;
 	}
 	else 
@@ -356,5 +366,5 @@
 	account_t *a;
 	
-	if( ( a = account_get( irc, id ) ) )
+	if( ( a = account_get( irc->b, id ) ) )
 		return &a->set;
 	else
@@ -404,5 +414,5 @@
 		}
 
-		a = account_add( irc, prpl, cmd[3], cmd[4] );
+		a = account_add( irc->b, prpl, cmd[3], cmd[4] );
 		if( cmd[5] )
 		{
@@ -418,5 +428,5 @@
 		MIN_ARGS( 2 );
 
-		if( !( a = account_get( irc, cmd[2] ) ) )
+		if( !( a = account_get( irc->b, cmd[2] ) ) )
 		{
 			irc_usermsg( irc, "Invalid account" );
@@ -440,5 +450,5 @@
 			                       "set' command. Are you sure you want to delete this "
 			                       "account?", a->prpl->name, a->user );
-			query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, cad );
+			//query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, cad );
 			g_free( msg );
 		}
@@ -451,5 +461,5 @@
 			irc_usermsg( irc, "Account list:" );
 		
-		for( a = irc->accounts; a; a = a->next )
+		for( a = irc->b->accounts; a; a = a->next )
 		{
 			char *con;
@@ -474,5 +484,5 @@
 		if( cmd[2] )
 		{
-			if( ( a = account_get( irc, cmd[2] ) ) )
+			if( ( a = account_get( irc->b, cmd[2] ) ) )
 			{
 				if( a->ic )
@@ -483,5 +493,5 @@
 				else
 				{
-					account_on( irc, a );
+					account_on( irc->b, a );
 				}
 			}
@@ -494,10 +504,11 @@
 		else
 		{
-			if ( irc->accounts ) {
+			if ( irc->b->accounts )
+			{
 				irc_usermsg( irc, "Trying to get all accounts connected..." );
 			
-				for( a = irc->accounts; a; a = a->next )
+				for( a = irc->b->accounts; a; a = a->next )
 					if( !a->ic && a->auto_connect )
-						account_on( irc, a );
+						account_on( irc->b, a );
 			} 
 			else
@@ -513,17 +524,17 @@
 			irc_usermsg( irc, "Deactivating all active (re)connections..." );
 			
-			for( a = irc->accounts; a; a = a->next )
+			for( a = irc->b->accounts; a; a = a->next )
 			{
 				if( a->ic )
-					account_off( irc, a );
+					account_off( irc->b, a );
 				else if( a->reconnect )
 					cancel_auto_reconnect( a );
 			}
 		}
-		else if( ( a = account_get( irc, cmd[2] ) ) )
+		else if( ( a = account_get( irc->b, cmd[2] ) ) )
 		{
 			if( a->ic )
 			{
-				account_off( irc, a );
+				account_off( irc->b, a );
 			}
 			else if( a->reconnect )
@@ -556,4 +567,5 @@
 }
 
+#if 0
 static void cmd_add( irc_t *irc, char **cmd )
 {
@@ -644,57 +656,46 @@
 	}
 }
+#endif
 
 static void cmd_rename( irc_t *irc, char **cmd )
 {
-	user_t *u;
-	
-	if( g_strcasecmp( cmd[1], irc->nick ) == 0 )
+	irc_user_t *iu;
+	
+	iu = irc_user_by_name( irc, cmd[1] );
+	
+	if( iu == NULL )
+	{
+		irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
+	}
+	else if( iu == irc->user )
 	{
 		irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] );
 	}
-	else if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
-	{
-		if( strchr( CTYPES, cmd[2][0] ) && nick_ok( cmd[2] + 1 ) )
-		{
-			u = user_find( irc, irc->nick );
-			
-			irc_part( irc, u, irc->channel );
-			g_free( irc->channel );
-			irc->channel = g_strdup( cmd[2] );
-			irc_join( irc, u, irc->channel );
-			
-			if( strcmp( cmd[0], "set_rename" ) != 0 )
-				set_setstr( &irc->set, "control_channel", cmd[2] );
-		}
-	}
-	else if( user_find( irc, cmd[2] ) && ( nick_cmp( cmd[1], cmd[2] ) != 0 ) )
+	else if( !nick_ok( cmd[2] ) )
+	{
+		irc_usermsg( irc, "Nick `%s' is invalid", cmd[2] );
+	}
+	else if( irc_user_by_name( irc, cmd[2] ) )
 	{
 		irc_usermsg( irc, "Nick `%s' already exists", cmd[2] );
 	}
-	else if( !nick_ok( cmd[2] ) )
-	{
-		irc_usermsg( irc, "Nick `%s' is invalid", cmd[2] );
-	}
-	else if( !( u = user_find( irc, cmd[1] ) ) )
-	{
-		irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
-	}
-	else
-	{
-		user_rename( irc, cmd[1], cmd[2] );
-		irc_write( irc, ":%s!%s@%s NICK %s", cmd[1], u->user, u->host, cmd[2] );
-		if( g_strcasecmp( cmd[1], irc->mynick ) == 0 )
-		{
-			g_free( irc->mynick );
-			irc->mynick = g_strdup( cmd[2] );
-			
+	else
+	{
+		if( !irc_user_set_nick( iu, cmd[2] ) )
+		{
+			irc_usermsg( irc, "Error while changing nick" );
+			return;
+		}
+		
+		if( iu == irc->root )
+		{
 			/* If we're called internally (user did "set root_nick"),
 			   let's not go O(INF). :-) */
 			if( strcmp( cmd[0], "set_rename" ) != 0 )
-				set_setstr( &irc->set, "root_nick", cmd[2] );
-		}
-		else if( u->send_handler == buddy_send_handler )
-		{
-			nick_set( u->ic->acc, u->handle, cmd[2] );
+				set_setstr( &irc->b->set, "root_nick", cmd[2] );
+		}
+		else if( iu->bu )
+		{
+			nick_set( iu->bu->ic->acc, iu->bu->handle, cmd[2] );
 		}
 		
@@ -703,4 +704,5 @@
 }
 
+#if 0
 char *set_eval_root_nick( set_t *set, char *new_nick )
 {
@@ -913,14 +915,4 @@
 {
 	cmd_set_real( irc, cmd, NULL, NULL );
-}
-
-static void cmd_save( irc_t *irc, char **cmd )
-{
-	if( ( irc->status & USTATUS_IDENTIFIED ) == 0 )
-		irc_usermsg( irc, "Please create an account first" );
-	else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK )
-		irc_usermsg( irc, "Configuration saved" );
-	else
-		irc_usermsg( irc, "Configuration could not be saved!" );
 }
 
@@ -990,32 +982,4 @@
 }
 
-static void cmd_nick( irc_t *irc, char **cmd ) 
-{
-	account_t *a;
-
-	if( !cmd[1] || !( a = account_get( irc, cmd[1] ) ) )
-	{
-		irc_usermsg( irc, "Invalid account");
-	}
-	else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
-	{
-		irc_usermsg( irc, "That account is not on-line" );
-	}
-	else if ( !cmd[2] ) 
-	{
-		irc_usermsg( irc, "Your name is `%s'" , a->ic->displayname ? a->ic->displayname : "NULL" );
-	}
-	else if ( !a->prpl->set_my_name ) 
-	{
-		irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
-	}
-	else
-	{
-		irc_usermsg( irc, "Setting your name to `%s'", cmd[2] );
-		
-		a->prpl->set_my_name( a->ic, cmd[2] );
-	}
-}
-
 static void cmd_qlist( irc_t *irc, char **cmd )
 {
@@ -1036,10 +1000,4 @@
 		else
 			irc_usermsg( irc, "%d, BitlBee: %s", num, q->question );
-}
-
-static void cmd_join_chat( irc_t *irc, char **cmd )
-{
-	irc_usermsg( irc, "This command is now obsolete. "
-	                  "Please try the `chat' command instead." );
 }
 
@@ -1217,18 +1175,22 @@
 	}
 }
+#endif
 
 const command_t commands[] = {
 	{ "help",           0, cmd_help,           0 }, 
+	{ "account",        1, cmd_account,        0 },
 	{ "identify",       1, cmd_identify,       0 },
 	{ "register",       1, cmd_register,       0 },
 	{ "drop",           1, cmd_drop,           0 },
-	{ "account",        1, cmd_account,        0 },
+	{ "save",           0, cmd_save,           0 },
+#if 0
 	{ "add",            2, cmd_add,            0 },
 	{ "info",           1, cmd_info,           0 },
+#endif
 	{ "rename",         2, cmd_rename,         0 },
+#if 0
 	{ "remove",         1, cmd_remove,         0 },
 	{ "block",          1, cmd_block,          0 },
 	{ "allow",          1, cmd_allow,          0 },
-	{ "save",           0, cmd_save,           0 },
 	{ "set",            0, cmd_set,            0 },
 	{ "yes",            0, cmd_yesno,          0 },
@@ -1240,4 +1202,5 @@
 	{ "chat",           1, cmd_chat,           0 },
 	{ "transfer",       0, cmd_transfer,       0 },
+#endif
 	{ NULL }
 };
Index: set.c
===================================================================
--- set.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ set.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -225,4 +225,5 @@
 }
 
+/*
 char *set_eval_ops( set_t *set, char *value )
 {
@@ -246,2 +247,3 @@
 	return value;
 }
+*/
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ storage_xml.c	(revision a87e6ba665985301eedbe981a81110846aad8a9d)
@@ -147,5 +147,5 @@
 		                         arc_decode( pass_cr, pass_len, &password, xd->given_pass ) )
 		{
-			xd->current_account = account_add( irc, prpl, handle, password );
+			xd->current_account = account_add( irc->b, prpl, handle, password );
 			if( server )
 				set_setstr( &xd->current_account->set, "server", server );
@@ -181,5 +181,5 @@
 				xd->current_set_head = &xd->current_account->set;
 			else
-				xd->current_set_head = &xd->irc->set;
+				xd->current_set_head = &xd->irc->b->set;
 			
 			xd->current_setting = g_strdup( setting );
@@ -215,5 +215,5 @@
 		if( xd->current_account && handle && channel )
 		{
-			xd->current_chat = chat_add( xd->irc, xd->current_account, handle, channel );
+			//xd->current_chat = chat_add( xd->irc, xd->current_account, handle, channel );
 		}
 		else
@@ -353,5 +353,5 @@
 static storage_status_t xml_load( irc_t *irc, const char *password )
 {
-	return xml_load_real( irc, irc->nick, password, XML_PASS_UNKNOWN );
+	return xml_load_real( irc, irc->user->nick, password, XML_PASS_UNKNOWN );
 }
 
@@ -396,5 +396,5 @@
 	md5_state_t md5_state;
 	
-	path2 = g_strdup( irc->nick );
+	path2 = g_strdup( irc->user->nick );
 	nick_lc( path2 );
 	g_snprintf( path, sizeof( path ) - 2, "%s%s%s", global.conf->configdir, path2, ".xml" );
@@ -422,15 +422,15 @@
 	pass_buf = base64_encode( pass_md5, 21 );
 	
-	if( !xml_printf( fd, 0, "<user nick=\"%s\" password=\"%s\" version=\"%d\">\n", irc->nick, pass_buf, XML_FORMAT_VERSION ) )
+	if( !xml_printf( fd, 0, "<user nick=\"%s\" password=\"%s\" version=\"%d\">\n", irc->user->nick, pass_buf, XML_FORMAT_VERSION ) )
 		goto write_error;
 	
 	g_free( pass_buf );
 	
-	for( set = irc->set; set; set = set->next )
+	for( set = irc->b->set; set; set = set->next )
 		if( set->value )
 			if( !xml_printf( fd, 1, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) )
 				goto write_error;
 	
-	for( acc = irc->accounts; acc; acc = acc->next )
+	for( acc = irc->b->accounts; acc; acc = acc->next )
 	{
 		unsigned char *pass_cr;
@@ -470,4 +470,5 @@
 			goto write_error;
 		
+#if 0
 		for( c = irc->chatrooms; c; c = c->next )
 		{
@@ -488,4 +489,5 @@
 				goto write_error;
 		}
+#endif
 		
 		if( !xml_printf( fd, 1, "</account>\n" ) )
Index: er.c
===================================================================
--- user.c	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ 	(revision )
@@ -1,231 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2004 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Stuff to handle, save and search buddies                             */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-  Suite 330, Boston, MA  02111-1307  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-user_t *user_add( irc_t *irc, char *nick )
-{
-	user_t *u, *lu = NULL;
-	char *key;
-	
-	if( !nick_ok( nick ) )
-		return( NULL );
-	
-	if( user_find( irc, nick ) != NULL )
-		return( NULL );
-	
-	if( ( u = irc->users ) )
-	{
-		while( u )
-		{
-			if( nick_cmp( nick, u->nick ) < 0 )
-				break;
-			
-			lu = u;
-			u = u->next;
-		}
-		
-		u = g_new0( user_t, 1 );
-		if( lu )
-		{
-			u->next = lu->next;
-			lu->next = u;
-		}
-		else
-		{
-			u->next = irc->users;
-			irc->users = u;
-		}
-	}
-	else
-	{
-		irc->users = u = g_new0( user_t, 1 );
-	}
-	
-	u->user = u->realname = u->host = u->nick = g_strdup( nick );
-	u->is_private = set_getbool( &irc->set, "private" );
-	
-	key = g_strdup( nick );
-	nick_lc( key );
-	g_hash_table_insert( irc->userhash, key, u );
-	
-	return( u );
-}
-
-int user_del( irc_t *irc, char *nick )
-{
-	user_t *u, *t;
-	char *key;
-	gpointer okey, ovalue;
-	
-	if( !nick_ok( nick ) )
-		return( 0 );
-	
-	u = irc->users;
-	t = NULL;
-	while( u )
-	{
-		if( nick_cmp( u->nick, nick ) == 0 )
-		{
-			/* Get this key now already, since "nick" might be free()d
-			   at the time we start playing with the hash... */
-			key = g_strdup( nick );
-			nick_lc( key );
-			
-			if( t )
-				t->next = u->next;
-			else
-				irc->users = u->next;
-			if( u->online )
-				irc_kill( irc, u );
-			g_free( u->nick );
-			if( u->nick != u->user ) g_free( u->user );
-			if( u->nick != u->host ) g_free( u->host );
-			if( u->nick != u->realname ) g_free( u->realname );
-			g_free( u->group );
-			g_free( u->away );
-			g_free( u->handle );
-			g_free( u->sendbuf );
-			if( u->sendbuf_timer ) b_event_remove( u->sendbuf_timer );
-			g_free( u );
-			
-			if( !g_hash_table_lookup_extended( irc->userhash, key, &okey, &ovalue ) || ovalue != u )
-			{
-				g_free( key );
-				return( 1 );	/* Although this is a severe error, the user is removed from the list... */
-			}
-			g_hash_table_remove( irc->userhash, key );
-			g_free( key );
-			g_free( okey );
-			
-			return( 1 );
-		}
-		u = (t=u)->next;
-	}
-	
-	return( 0 );
-}
-
-user_t *user_find( irc_t *irc, char *nick )
-{
-	char key[512] = "";
-	
-	strncpy( key, nick, sizeof( key ) - 1 );
-	if( nick_lc( key ) )
-		return( g_hash_table_lookup( irc->userhash, key ) );
-	else
-		return( NULL );
-}
-
-user_t *user_findhandle( struct im_connection *ic, const char *handle )
-{
-	user_t *u;
-	char *nick;
-	
-	/* First, let's try a hash lookup. If it works, it's probably faster. */
-	if( ( nick = g_hash_table_lookup( ic->acc->nicks, handle ) ) &&
-	    ( u = user_find( ic->irc, nick ) ) &&
-	    ( ic->acc->prpl->handle_cmp( handle, u->handle ) == 0 ) )
-		return u;
-	
-	/* However, it doesn't always work, so in that case we'll have to dig
-	   through the whole userlist. :-( */
-	for( u = ic->irc->users; u; u = u->next )
-		if( u->ic == ic && u->handle && ic->acc->prpl->handle_cmp( u->handle, handle ) == 0 )
-			return u;
-	
-	return NULL;
-}
-
-/* DO NOT PASS u->nick FOR oldnick !!! */
-void user_rename( irc_t *irc, char *oldnick, char *newnick )
-{
-	user_t *u = user_find( irc, oldnick );
-	gpointer okey, ovalue;
-	char *key;
-	
-	if( !u ) return;	/* Should've been checked by the caller... */
-	
-	g_free( u->nick );
-	if( u->nick == u->user ) u->user = NULL;
-	if( u->nick == u->host ) u->host = NULL;
-	if( u->nick == u->realname ) u->realname = NULL;
-	u->nick = g_strdup( newnick );
-	if( !u->user ) u->user = u->nick;
-	if( !u->host ) u->host = u->nick;
-	if( !u->realname ) u->realname = u->nick;
-	
-	/* Remove the old reference to this user from the hash and create a
-	   new one with the new nick. This is indeed a bit messy. */
-	key = g_strdup( oldnick );
-	nick_lc( key );
-	if( !g_hash_table_lookup_extended( irc->userhash, key, &okey, &ovalue ) || ovalue != u )
-	{
-		g_free( key );
-		return;		/* This really shouldn't happen! */	
-	}
-	g_hash_table_remove( irc->userhash, key );
-	g_free( key );
-	g_free( okey );
-	
-	key = g_strdup( newnick );
-	nick_lc( key );
-	g_hash_table_insert( irc->userhash, key, u );
-	
-	/* Also, let's try to keep the linked list nicely sorted. Fear this
-	   code. If my teacher would see this, she would cry. ;-) */
-	{
-		user_t *u1, *lu1;
-		
-		/* Remove the user from the old position in the chain. */
-		if( u == irc->users )
-		{
-			irc->users = u->next;
-		}
-		else
-		{
-			u1 = u;
-			for( lu1 = irc->users; lu1->next != u1; lu1 = lu1->next );
-			lu1->next = u1->next;
-		}
-		
-		/* Search for the new position. */
-		for( lu1 = NULL, u1 = irc->users; u1; u1 = u1->next )
-		{
-			if( nick_cmp( newnick, u1->nick ) < 0 )
-				break;
-			
-			lu1 = u1;
-		}
-		
-		/* Insert it at this new position. */
-		u->next = u1;
-		if( lu1 )
-			lu1->next = u;
-		else
-			irc->users = u;
-	}
-}
Index: er.h
===================================================================
--- user.h	(revision 3ab1d317831a6c1830bb648a1a8d63a41c92f651)
+++ 	(revision )
@@ -1,62 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2004 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Stuff to handle, save and search buddies                             */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
-  Suite 330, Boston, MA  02111-1307  USA
-*/
-#ifndef __USER_H__
-#define __USER_H__
-
-typedef struct __USER
-{
-	char *nick;
-	char *user;
-	char *host;
-	char *realname;
-	
-	char *away;
-	char *status_msg; /* Non-IRC extension, but nice on IM. */
-	
-	char is_private;
-	char online;
-	
-	char *handle;
-	char *group;
-	struct im_connection *ic;
-
- 	char *sendbuf;
- 	time_t last_typing_notice;
- 	int sendbuf_len;
- 	guint sendbuf_timer;
-    	int sendbuf_flags;
-	
-	void (*send_handler) ( irc_t *irc, struct __USER *u, char *msg, int flags );
-	
-	struct __USER *next;
-} user_t;
-
-user_t *user_add( struct irc *irc, char *nick );
-int user_del( irc_t *irc, char *nick );
-G_MODULE_EXPORT user_t *user_find( irc_t *irc, char *nick );
-G_MODULE_EXPORT user_t *user_findhandle( struct im_connection *ic, const char *handle );
-void user_rename( irc_t *irc, char *oldnick, char *newnick );
-
-#endif /* __USER_H__ */
