Index: Makefile
===================================================================
--- Makefile	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ Makefile	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -10,5 +10,5 @@
 
 # Program variables
-objects = account.o bitlbee.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o
+objects = account.o bitlbee.o chat.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o
 headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h otr.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h
 subdirs = lib protocols
Index: account.c
===================================================================
--- account.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ account.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -190,4 +190,5 @@
 {
 	account_t *a, *l = NULL;
+	struct chat *c, *nc;
 	
 	if( acc->ic )
@@ -202,4 +203,11 @@
 			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 )
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ bitlbee.h	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -132,4 +132,5 @@
 #include "account.h"
 #include "nick.h"
+#include "chat.h"
 #include "conf.h"
 #include "log.h"
Index: chat.c
===================================================================
--- chat.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
+++ chat.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -0,0 +1,191 @@
+  /********************************************************************\
+  * 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"
+
+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: chat.h
===================================================================
--- chat.h	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
+++ chat.h	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -0,0 +1,46 @@
+  /********************************************************************\
+  * 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
+*/
+
+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 );
Index: conf.c
===================================================================
--- conf.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ conf.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -79,5 +79,5 @@
 	}
 	
-	while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hu:" ) ) >= 0 )
+	while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:u:" ) ) >= 0 )
 	/*     ^^^^ Just to make sure we skip this step from the REHASH handler. */
 	{
@@ -314,5 +314,5 @@
 			else
 			{
-				fprintf( stderr, "Error: Unknown setting `%s` in configuration file.\n", ini->key );
+				fprintf( stderr, "Error: Unknown setting `%s` in configuration file (line %d).\n", ini->key, ini->line );
 				return 0;
 				/* For now just ignore unknown keys... */
@@ -321,6 +321,6 @@
 		else if( g_strcasecmp( ini->section, "defaults" ) != 0 )
 		{
-			fprintf( stderr, "Error: Unknown section [%s] in configuration file. "
-			                 "BitlBee configuration must be put in a [settings] section!\n", ini->section );
+			fprintf( stderr, "Error: Unknown section [%s] in configuration file (line %d). "
+			                 "BitlBee configuration must be put in a [settings] section!\n", ini->section, ini->line );
 			return 0;
 		}
Index: debian/bitlbee.init
===================================================================
--- debian/bitlbee.init	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ debian/bitlbee.init	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -32,6 +32,4 @@
 fi
 
-[ "$BITLBEE_DISABLED" = "1" ] && exit 0
-
 
 #
@@ -41,7 +39,15 @@
 	# Make sure BitlBee can actually write its PID...
 	touch /var/run/bitlbee.pid
-	chown bitlbee /var/run/bitlbee.pid
+	chown bitlbee: /var/run/bitlbee.pid
 	
-	start-stop-daemon --start --quiet --pidfile $PIDFILE \
+	# Clean up after the bug between 1.2-5 and 1.2.1-2 where BitlBee ran
+	# as root. (#494656 and #495877) Fixing this in the postinst script
+	# is not enough since the user will restart his BitlBee after up-
+	# grading the package, and the BitlBee running as root will then
+	# save its settings, re-setting ownership of the file to root.
+	# TODO: Remove this after a few revisions.
+	find /var/lib/bitlbee -uid 0 -name '*.xml' -exec chown bitlbee: {} \;
+
+	start-stop-daemon --start --quiet \
 		--exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $BITLBEE_OPTS
 }
@@ -58,4 +64,6 @@
 case "$1" in
   start)
+	[ "$BITLBEE_DISABLED" = "1" ] && exit 0
+
 	echo -n "Starting $DESC: $NAME"
 	d_start
Index: debian/changelog
===================================================================
--- debian/changelog	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ debian/changelog	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -1,7 +1,41 @@
-bitlbee (1.2-6) UNRELEASED; urgency=low
-
-  * Add Homepage and Vcs-Bzr fields.
-
- -- Jelmer Vernooij <jelmer@samba.org>  Sun, 11 May 2008 14:18:16 +0200
+bitlbee (1.2.2-1) unstable; urgency=critical
+
+  * New upstream version.
+  * Fixes a security issue (account hijacking), hence the high priority.
+  * Using a patch to set the User setting in bitlbee.conf properly to keep
+    upstream and Debian properly separated in my bzr tree.
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Wed, 27 Aug 2008 23:59:50 +0100
+
+bitlbee (1.2.1-3) unstable; urgency=high
+
+  * chown /var/lib/bitlbee/*.xml to bitlbee:bitlbee to clean up after
+    1.2-5 and the bugfix in 1.2.1-2. (Closes: #495877)
+  * Moved BITLBEE_DISABLED check to only check when trying to *start*
+    the daemon. (Closes: #488611)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 23 Aug 2008 18:53:54 +0100
+
+bitlbee (1.2.1-2) unstable; urgency=low
+
+  * Properly set the User= line to something sensible so BitlBee won't
+    run as root anymore. 1.2-5 was a bad upload. :-( (Closes: #494656)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Tue, 12 Aug 2008 00:36:03 +0100
+
+bitlbee (1.2.1-1.1) unstable; urgency=low
+
+  * Non-Maintainer Upload
+  * Use invoke-rc.d as per policy. (Closes: #492637) [Thanks to Matt
+    Kraai]
+
+ -- Don Armstrong <don@debian.org>  Wed, 06 Aug 2008 06:57:18 -0700
+
+bitlbee (1.2.1-1) unstable; urgency=low
+
+  * New upstream release.
+  * Add Homepage and Vcs-Bzr fields. (From Jelmer.)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net>  Thu, 26 Jun 2008 00:07:50 +0100
 
 bitlbee (1.2-5) unstable; urgency=low
Index: debian/patches/bitlbee.conf.diff
===================================================================
--- debian/patches/bitlbee.conf.diff	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
+++ debian/patches/bitlbee.conf.diff	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -0,0 +1,13 @@
+=== modified file 'bitlbee.conf'
+--- debian/bitlbee/etc/bitlbee/bitlbee.conf	2008-08-26 22:33:54 +0000
++++ debian/bitlbee/etc/bitlbee/bitlbee.conf	2008-08-27 23:18:13 +0000
+@@ -23,7 +23,7 @@
+ ## If BitlBee is started by root as a daemon, it can drop root privileges,
+ ## and change to the specified user.
+ ##
+-# User = bitlbee
++User = bitlbee
+ 
+ ## DaemonPort/DaemonInterface:
+ ##
+
Index: debian/postinst
===================================================================
--- debian/postinst	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ debian/postinst	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -64,10 +64,21 @@
 fi
 
+if ! grep -qi '^User *= *' /etc/bitlbee/bitlbee.conf; then
+	echo 'Updating configuration file, enabling User-setting...'
+	if ! sed -i -e 's/# *User *= *.*/User = bitlbee/i' /etc/bitlbee/bitlbee.conf; then
+		echo 'Failed! BitlBee may run as root now, please check your configs.'
+	fi
+fi
+
 if [ -n "$2" -a "$BITLBEE_UPGRADE_DONT_RESTART" != "1" ]; then
-	/etc/init.d/bitlbee restart
+	if which invoke-rc.d >/dev/null 2>&1; then
+		invoke-rc.d bitlbee restart
+	else
+		/etc/init.d/bitlbee restart
+	fi
 fi
 
 ## If we're upgrading, we'll probably skip this next part
-if [ -d $CONFDIR ] && chown -R bitlbee $CONFDIR; then
+if [ -d $CONFDIR ] && chown -R bitlbee: $CONFDIR; then
 	echo 'BitlBee (probably) already installed, skipping user/configdir installation'
 	exit 0
@@ -91,4 +102,8 @@
 
 if [ -z "$2" ]; then
-	/etc/init.d/bitlbee start
+	if which invoke-rc.d >/dev/null 2>&1; then
+		invoke-rc.d bitlbee start
+	else
+		/etc/init.d/bitlbee start
+	fi
 fi
Index: debian/prerm
===================================================================
--- debian/prerm	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ debian/prerm	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -10,4 +10,8 @@
 	fi
 else
-	/etc/init.d/bitlbee stop || exit 0
+	if which invoke-rc.d >/dev/null 2>&1; then
+		invoke-rc.d bitblee stop || exit 0
+	else
+		/etc/init.d/bitlbee stop || exit 0
+	fi
 fi
Index: debian/rules
===================================================================
--- debian/rules	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ debian/rules	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -55,4 +55,5 @@
 	cp utils/* debian/bitlbee/usr/share/doc/bitlbee/examples/
 	cp debian/bitlbee.init debian/bitlbee/etc/init.d/bitlbee
+	patch -p0 < debian/patches/bitlbee.conf.diff
 	cd debian/bitlbee/usr/share/; \
 		gzip -9 doc/bitlbee/changelog.Debian doc/bitlbee/changelog doc/bitlbee/user-guide.txt \
Index: doc/user-guide/Makefile
===================================================================
--- doc/user-guide/Makefile	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ doc/user-guide/Makefile	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -13,5 +13,5 @@
 
 %.html: %.db.xml
-	xsltproc --output $@ http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $<
+	xsltproc --output $@ http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl $<
 
 %.pdf: %.db.xml
Index: doc/user-guide/commands.xml
===================================================================
--- doc/user-guide/commands.xml	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ doc/user-guide/commands.xml	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -125,5 +125,5 @@
 			<description>
 				<para>
-					This command gives you a list of all the accounts known by BitlBee, including the numbers you'll need for most account commands.
+					This command gives you a list of all the accounts known by BitlBee.
 				</para>
 			</description>
@@ -138,13 +138,96 @@
 			<description>
 				<para>
-					This account can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing <emphasis>account set &lt;account id&gt;</emphasis>.
-				</para>
-				
-				<para>
-					For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>. For details about the syntax of this command, see <emphasis>help set</emphasis>.
+					This command can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing <emphasis>account set &lt;account id&gt;</emphasis>.
+				</para>
+				
+				<para>
+					For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
 				</para>
 				
 				<para>
 					The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
+				</para>
+			</description>
+		</bitlbee-command>
+	</bitlbee-command>
+
+	<bitlbee-command name="chat">
+		<short-description>Chatroom list maintenance</short-description>
+		<syntax>chat &lt;action&gt; [&lt;arguments&gt;]</syntax>
+
+		<description>
+
+			<para>
+				Available actions: add, del, list, with and set. See <emphasis>help chat &lt;action&gt;</emphasis> for more information.
+			</para>
+
+		</description>
+
+		<bitlbee-command name="add">
+			<syntax>chat add &lt;account&gt; &lt;room&gt; [&lt;channel&gt;]</syntax>
+
+			<description>
+				<para>
+					Add a chatroom to the list of chatrooms you're interested in. BitlBee needs this list to map room names to a proper IRC channel name.
+				</para>
+
+				<para>
+					After adding a room to your list, you can simply use the IRC /join command to enter the room. Also, you can tell BitlBee to automatically join the room when you log in. (See <emphasis>chat set</emphasis>)
+				</para>
+			</description>
+
+		</bitlbee-command>
+
+		<bitlbee-command name="del">
+			<syntax>chat del &lt;chat id&gt;</syntax>
+
+			<description>
+				<para>
+					This commands deletes an chatroom from your list.
+				</para>
+
+				<para>
+					The room ID can be a number (see <emphasis>chat list</emphasis>), or (part of) the name of the room/channel.
+				</para>
+			</description>
+		</bitlbee-command>
+
+		<bitlbee-command name="list">
+			<syntax>chat list</syntax>
+
+			<description>
+				<para>
+					This command gives you a list of all the chatrooms known by BitlBee.
+				</para>
+			</description>
+		</bitlbee-command>
+
+		<bitlbee-command name="with">
+			<syntax>chat with &lt;nickname&gt;</syntax>
+
+			<description>
+				<para>
+					While most <emphasis>chat</emphasis> subcommands are about named chatrooms, this command can be used to open an unnamed groupchat with one or more persons. This command is what <emphasis>/join #nickname</emphasis> used to do in older BitlBee versions.
+				</para>
+			</description>
+		</bitlbee-command>
+
+		<bitlbee-command name="set">
+			<syntax>chat set &lt;chat id&gt;</syntax>
+			<syntax>chat set &lt;chat id&gt;/&lt;setting&gt;</syntax>
+			<syntax>chat set &lt;chat id&gt;/&lt;setting&gt; &lt;value&gt;</syntax>
+			<syntax>chat set -del &lt;chat id&gt;/&lt;setting&gt;</syntax>
+
+			<description>
+				<para>
+					This command can be used to change various settings for chatrooms.
+				</para>
+				
+				<para>
+					For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
+				</para>
+				
+				<para>
+					The room ID can be a number (see <emphasis>chat list</emphasis>), or (part of) the name of the room/channel.
 				</para>
 			</description>
@@ -452,4 +535,14 @@
 	</bitlbee-setting>
 
+	<bitlbee-setting name="auto_join" type="boolean" scope="chat">
+		<default>false</default>
+
+		<description>
+			<para>
+				With this option enabled, BitlBee will automatically join this chatroom when you log in.
+			</para>
+		</description>
+	</bitlbee-setting>
+
 	<bitlbee-setting name="auto_reconnect" type="boolean" scope="both">
 		<default>false</default>
@@ -712,4 +805,13 @@
 	</bitlbee-setting>
 
+	<bitlbee-setting name="nick" type="string" scope="chat">
+
+		<description>
+			<para>
+				You can use this option to set your nickname in a chatroom. You won't see this nickname yourself, but other people in the room will. By default, BitlBee will use your username as the chatroom nickname.
+			</para>
+		</description>
+	</bitlbee-setting>
+
 	<bitlbee-setting name="password" type="string" scope="both">
 		<description>
@@ -791,5 +893,5 @@
 	<bitlbee-setting name="resource_select" type="string" scope="account">
 		<default>priority</default>
-		<possible-values>priority, time</possible-values>
+		<possible-values>priority, activity</possible-values>
 
 		<description>
@@ -799,5 +901,5 @@
 
 			<para>
-				Normally it's set to <emphasis>priority</emphasis> which means messages will always be delivered to the buddy's resource with the highest priority. If the setting is set to <emphasis>time</emphasis>, messages will be delivered to the resource that was last used to send you a message (or the resource that most recently connected).
+				Normally it's set to <emphasis>priority</emphasis> which means messages will always be delivered to the buddy's resource with the highest priority. If the setting is set to <emphasis>activity</emphasis>, messages will be delivered to the resource that was last used to send you a message (or the resource that most recently connected).
 			</para>
 		</description>
@@ -1081,27 +1183,3 @@
 
 	</bitlbee-command>
-
-	<bitlbee-command name="join_chat">
-		<short-description>Join a named groupchat/conference room</short-description>
-		<syntax>join_chat &lt;connection&gt; &lt;room name&gt; [&lt;channel name&gt;] [&lt;room nickname&gt;] [&lt;password&gt;]</syntax>
-
-		<description>
-			<para>
-				On most IM-networks groupchats can be started using the /join command. (<emphasis>/join #foo</emphasis> to start a chatroom with you and <emphasis>foo</emphasis>) This doesn't work with names groupchats though (which exist on Jabber networks and AIM, for example), instead you can use this command.
-			</para>
-
-			<para>
-				The first two arguments are required. <emphasis>room name</emphasis> is the name of the chatroom on the IM-network. <emphasis>channel name</emphasis> is the IRC channel name BitlBee should map this to. <emphasis>room nickname</emphasis> is the nickname you want to have in this channel. If you don't give these options, BitlBee will do the right guesses.
-			</para>
-
-			<para>
-				The following command will join you to the chatroom called <emphasis>bitlbee@conference.bitlbee.org</emphasis>. The channel will be called <emphasis>&amp;bitlbee-help</emphasis> because <emphasis>&amp;bitlbee</emphasis> will already be in use. Your nickname will be <emphasis>help-me</emphasis>.
-			</para>
-		</description>
-
-		<ircexample>
-			<ircline nick="wilmer">join_chat jabber bitlbee@conference.bitlbee.org &amp;bitlbee-help help-me</ircline>
-		</ircexample>
-
-	</bitlbee-command>
 </chapter>
Index: doc/user-guide/help.xml
===================================================================
--- doc/user-guide/help.xml	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ doc/user-guide/help.xml	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -1,3 +1,3 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
+<?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 
Index: doc/user-guide/misc.xml
===================================================================
--- doc/user-guide/misc.xml	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ doc/user-guide/misc.xml	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -69,13 +69,13 @@
 
 <para>
-If you want to start a groupchat with the person <emphasis>lisa_msn</emphasis> in it, just join the channel <emphasis>#lisa_msn</emphasis>. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and lisa_msn in it.
+To open a groupchat, use the <emphasis>chat with</emphasis> command. For example,  to start a groupchat with the person <emphasis>lisa_msn</emphasis> in it, just type <emphasis>chat with lisa_msn</emphasis>. BitlBee will create a new virtual channel with root, you and lisa_msn in it.
 </para>
 
 <para>
-Of course a channel with only two people isn't really exciting yet. So the next step is to invite some other people to the channel. For this, you can use the <emphasis>/invite</emphasis> command of your IRC client. Please do keep in mind that all the people have to be on the same network and contact list! You can't invite Yahoo! buddies into an MSN groupchat.
+Then, just use the ordinary IRC <emphasis>/invite</emphasis> command to invite more people. Please do keep in mind that all the people have to be on the same network and contact list! You can't invite Yahoo! buddies into an MSN groupchat.
 </para>
 
 <para>
-Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>join_chat</emphasis> command to join them. See <emphasis>help join_chat</emphasis> for more information.
+Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>chat add</emphasis> command to join them. See <emphasis>help chat_add</emphasis> for more information.
 </para>
 
Index: doc/user-guide/user-guide.xml
===================================================================
--- doc/user-guide/user-guide.xml	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ doc/user-guide/user-guide.xml	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -1,3 +1,3 @@
-<?xml version="1.0" encoding="iso-8859-1"?>
+<?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 
Index: irc.c
===================================================================
--- irc.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ irc.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -146,4 +146,5 @@
 	irc_connection_list = g_slist_append( irc_connection_list, irc );
 
+	
 	s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
 	s = set_add( &irc->set, "auto_reconnect", "false", set_eval_bool, irc );
@@ -418,8 +419,6 @@
 			}
 			
-			if( lines[i] )
-			{
-				if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
-					continue;
+			if( lines[i] && ( cmd = irc_parse_line( lines[i] ) ) )
+			{
 				irc_exec( irc, cmd );
 				g_free( cmd );
@@ -496,5 +495,5 @@
 	if( line[0] == ':' )
 	{
-		for( i = 0; line[i] != ' '; i ++ );
+		for( i = 0; line[i] && line[i] != ' '; i ++ );
 		line = line + i;
 	}
@@ -821,5 +820,7 @@
 	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=(ohv)@%%+ CHANTYPES=#& CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server", CMODES, MAX_NICK_LENGTH - 1 );
+	irc_reply( irc,   5, "PREFIX=(ohv)@%%+ 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';
@@ -1085,5 +1086,5 @@
 	user_t *u = NULL;
 	
-	if( *nick == '#' || *nick == '&' )
+	if( strchr( CTYPES, *nick ) )
 	{
 		if( !( c = irc_chat_by_channel( irc, nick ) ) )
Index: irc.h
===================================================================
--- irc.h	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ irc.h	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -40,4 +40,5 @@
 #define CMODE "t"
 #define UMODE "s"
+#define CTYPES "&#"
 
 typedef enum
@@ -49,9 +50,4 @@
 	USTATUS_SHUTDOWN = 8
 } irc_status_t;
-
-typedef struct channel
-{
-	char *name;
-} channel_t;
 
 typedef struct irc
@@ -89,4 +85,5 @@
 	struct query *queries;
 	struct account *accounts;
+	struct chat *chatrooms;
 	
 	struct __USER *users;
@@ -104,5 +101,4 @@
 
 #include "user.h"
-// #include "nick.h"
 
 extern GSList *irc_connection_list;
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ irc_commands.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -125,5 +125,5 @@
 static void irc_cmd_mode( irc_t *irc, char **cmd )
 {
-	if( *cmd[1] == '#' || *cmd[1] == '&' )
+	if( strchr( CTYPES, *cmd[1] ) )
 	{
 		if( cmd[2] )
@@ -193,30 +193,12 @@
 	else if( cmd[1] )
 	{
-		if( ( cmd[1][0] == '#' || cmd[1][0] == '&' ) && cmd[1][1] )
-		{
-			user_t *u = user_find( irc, cmd[1] + 1 );
-			
-			if( u && u->ic && u->ic->acc->prpl->chat_with )
-			{
-				irc_reply( irc, 403, "%s :Initializing groupchat in a different channel", cmd[1] );
-				
-				if( !u->ic->acc->prpl->chat_with( u->ic, u->handle ) )
-				{
-					irc_usermsg( irc, "Could not open a groupchat with %s.", u->nick );
-				}
-			}
-			else if( u )
-			{
-				irc_reply( irc, 403, "%s :Groupchats are not possible with %s", cmd[1], cmd[1]+1 );
-			}
-			else
-			{
-				irc_reply( irc, 403, "%s :No such nick", 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] );
-		}
 	}
 }
Index: lib/events_libevent.c
===================================================================
--- lib/events_libevent.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ lib/events_libevent.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -37,7 +37,9 @@
 
 static void b_main_restart();
-static guint id_next = 1;
+static guint id_next = 1; /* Next ID to be allocated to an event handler. */
+static guint id_cur = 0; /* Event ID that we're currently handling. */
+static guint id_dead; /* Set to 1 if b_event_remove removes id_cur. */
 static GHashTable *id_hash;
-static int quitting = 0;
+static int quitting = 0; /* Prepare to quit, stop handling events. */
 
 /* Since libevent doesn't handle two event handlers for one fd-condition
@@ -119,5 +121,5 @@
 	struct b_event_data *b_ev = data;
 	b_input_condition cond = 0;
-	int id;
+	gboolean st;
 	
 	if( fd >= 0 )
@@ -133,19 +135,28 @@
 	/* Since the called function might cancel this handler already
 	   (which free()s b_ev), we have to remember the ID here. */
-	id = b_ev->id;
+	id_cur = b_ev->id;
+	id_dead = 0;
 	
 	if( quitting )
 	{
-		b_event_remove( id );
+		b_event_remove( id_cur );
 		return;
 	}
 	
-	if( !b_ev->function( b_ev->data, fd, cond ) )
+	st = b_ev->function( b_ev->data, fd, cond );
+	if( id_dead )
+	{
+		/* This event was killed already, don't touch it! */
+		return;
+	}
+	else if( !st )
 	{
 		event_debug( "Handler returned FALSE: " );
-		b_event_remove( id );
+		b_event_remove( id_cur );
 	}
 	else if( fd == -1 )
 	{
+		/* fd == -1 means it was a timer. These can't be auto-repeated
+		   so it has to be recreated every time. */
 		struct timeval tv;
 		
@@ -236,4 +247,7 @@
 	if( b_ev )
 	{
+		if( id == id_cur )
+			id_dead = TRUE;
+		
 		g_hash_table_remove( id_hash, &b_ev->id );
 		if( b_ev->evinfo.ev_fd >= 0 )
Index: lib/http_client.c
===================================================================
--- lib/http_client.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ lib/http_client.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -59,6 +59,6 @@
 	if( error )
 	{
-		g_free( req );
-		return( NULL );
+		http_free( req );
+		return NULL;
 	}
 	
@@ -160,8 +160,5 @@
 	
 	req->func( req );
-	
-	g_free( req->request );
-	g_free( req );
-	
+	http_free( req );
 	return FALSE;
 }
@@ -444,10 +441,14 @@
 	
 	req->func( req );
-	
+	http_free( req );
+	return FALSE;
+}
+
+void http_free( struct http_request *req )
+{
 	g_free( req->request );
 	g_free( req->reply_headers );
 	g_free( req->status_string );
 	g_free( req );
-	
-	return FALSE;
-}
+}
+
Index: lib/http_client.h
===================================================================
--- lib/http_client.h	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ lib/http_client.h	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -81,2 +81,4 @@
 void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data );
 void *http_dorequest_url( char *url_string, http_input_function func, gpointer data );
+
+void http_free( struct http_request *req );
Index: lib/ini.c
===================================================================
--- lib/ini.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ lib/ini.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2005 Wilmer van der Gaast and others                *
+  * Copyright 2002-2008 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -28,63 +28,114 @@
 ini_t *ini_open( char *file )
 {
-	ini_t *ini = g_new0( ini_t, 1 );
+	int fd;
+	ini_t *ini = NULL;
+	struct stat fi;
 	
-	if( ( ini->fp = fopen( file, "r" ) ) == NULL )
+	if( ( fd = open( file, O_RDONLY ) ) != -1 &&
+	    fstat( fd, &fi ) == 0 &&
+	    fi.st_size <= 16384 &&
+	    ( ini = g_malloc( sizeof( ini_t ) + fi.st_size + 1 ) ) &&
+	    read( fd, ini->file, fi.st_size ) == fi.st_size )
 	{
-		g_free( ini );
-		return( NULL );
+		memset( ini, 0, sizeof( ini_t ) );
+		ini->size = fi.st_size;
+		ini->file[ini->size] = 0;
+		ini->cur = ini->file;
+		ini->c_section = "";
+		return ini;
 	}
 	
-	return( ini );
+	g_free( ini );
+	if( fd >= 0 )
+		close( fd );
+
+	return NULL;
+}
+
+/* Strips leading and trailing whitespace and returns a pointer to the first
+   non-ws character of the given string. */
+static char *ini_strip_whitespace( char *in )
+{
+	char *e;
+
+	while( isspace( *in ) )
+		in++;
+
+	e = in + strlen( in ) - 1;
+	while( e > in && isspace( *e ) )
+		e--;
+	e[1] = 0;
+	
+	return in;
 }
 
 int ini_read( ini_t *file )
 {
-	char key[MAX_STRING], s[MAX_STRING], *t;
-	int i;
+	char *s;
 	
-	while( !feof( file->fp ) )
+	while( file->cur && file->cur < file->file + file->size )
 	{
-		*s = 0;
-		fscanf( file->fp, "%127[^\n#]s", s );
-		fscanf( file->fp, "%*[^\n]s" );
-		fgetc( file->fp );		/* Skip newline		*/
-		file->line ++;
-		if( strchr( s, '=' ) )
+		char *e, *next;
+		
+		file->line++;
+
+		/* Find the end of line */
+		if( ( e = strchr( file->cur, '\n' ) ) != NULL )
 		{
-			sscanf( s, "%[^ =]s", key );
-			if( ( t = strchr( key, '.' ) ) )
+			*e = 0;
+			next = e + 1;
+		}
+		else
+		{
+			/* No more lines. */
+			e = file->cur + strlen( file->cur );
+			next = NULL;
+		}
+		
+		/* Comment? */
+		if( ( s = strchr( file->cur, '#' ) ) != NULL )
+			*s = 0;
+		
+		file->cur = ini_strip_whitespace( file->cur );
+		
+		if( *file->cur == '[' )
+		{
+			file->cur++;
+			if( ( s = strchr( file->cur, ']' ) ) != NULL )
 			{
-				*t = 0;
-				strcpy( file->section, key );
-				t ++;
+				*s = 0;
+				file->c_section = file->cur;
+			}
+		}
+		else if( ( s = strchr( file->cur, '=' ) ) != NULL )
+		{
+			*s = 0;
+			file->key = ini_strip_whitespace( file->cur );
+			file->value = ini_strip_whitespace( s + 1 );
+			
+			if( ( s = strchr( file->key, '.' ) ) != NULL )
+			{
+				*s = 0;
+				file->section = file->key;
+				file->key = s + 1;
 			}
 			else
 			{
-				strcpy( file->section, file->c_section );
-				t = key;
+				file->section = file->c_section;
 			}
-			sscanf( t, "%s", file->key );
-			t = strchr( s, '=' ) + 1;
-			for( i = 0; t[i] == ' '; i ++ );
-			strcpy( file->value, &t[i] );
-			for( i = strlen( file->value ) - 1; file->value[i] == 32; i -- )
-				file->value[i] = 0;
 			
-			return( 1 );
+			file->cur = next;
+			return 1;
 		}
-		else if( ( t = strchr( s, '[' ) ) )
-		{
-			strcpy( file->c_section, t + 1 );
-			t = strchr( file->c_section, ']' );
-			*t = 0;
-		}
+		/* else: noise/comment/etc, let's just ignore it. */
+
+		file->cur = next;
 	}
-	return( 0 );
+	
+	return 0;
 }
 
 void ini_close( ini_t *file )
 {
-	fclose( file->fp );
 	g_free( file );
 }
Index: lib/ini.h
===================================================================
--- lib/ini.h	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ lib/ini.h	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -29,10 +29,12 @@
 typedef struct
 {
-	FILE *fp;
 	int line;
-	char c_section[MAX_STRING];
-	char section[MAX_STRING];
-	char key[MAX_STRING];
-	char value[MAX_STRING];
+	char *c_section;
+	char *section;
+	char *key;
+	char *value;
+	int size;
+	char *cur, *tok;
+	char file[];
 } ini_t;
 
Index: lib/proxy.c
===================================================================
--- lib/proxy.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ lib/proxy.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -558,5 +558,4 @@
 		return proxy_connect_socks5(host, port, phb);
 	
-	if (phb->host) g_free(phb);
 	g_free(phb);
 	return -1;
Index: lib/xmltree.c
===================================================================
--- lib/xmltree.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ lib/xmltree.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -472,5 +472,5 @@
 }
 
-struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children )
+struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children )
 {
 	struct xt_node *node, *c;
Index: lib/xmltree.h
===================================================================
--- lib/xmltree.h	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ lib/xmltree.h	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -90,5 +90,5 @@
 char *xt_find_attr( struct xt_node *node, const char *key );
 
-struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children );
+struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children );
 void xt_add_child( struct xt_node *parent, struct xt_node *child );
 void xt_add_attr( struct xt_node *node, const char *key, const char *value );
Index: protocols/jabber/conference.c
===================================================================
--- protocols/jabber/conference.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ protocols/jabber/conference.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -26,5 +26,5 @@
 static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_node *node, struct xt_node *orig );
 
-struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password )
+struct groupchat *jabber_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password )
 {
 	struct jabber_chat *jc;
@@ -36,7 +36,7 @@
 	node = xt_new_node( "x", NULL, NULL );
 	xt_add_attr( node, "xmlns", XMLNS_MUC );
-	node = jabber_make_packet( "presence", NULL, roomjid, node );
 	if( password )
 		xt_add_child( node, xt_new_node( "password", password, NULL ) );
+	node = jabber_make_packet( "presence", NULL, roomjid, node );
 	jabber_cache_add( ic, node, jabber_chat_join_failed );
 	
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ protocols/jabber/jabber.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -425,5 +425,5 @@
 }
 
-static struct groupchat *jabber_chat_join_( struct im_connection *ic, char *room, char *nick, char *password )
+static struct groupchat *jabber_chat_join_( struct im_connection *ic, const char *room, const char *nick, const char *password )
 {
 	if( strchr( room, '@' ) == NULL )
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ protocols/jabber/jabber.h	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -240,5 +240,5 @@
 
 /* conference.c */
-struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password );
+struct groupchat *jabber_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password );
 struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name );
 void jabber_chat_free( struct groupchat *c );
Index: protocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ protocols/jabber/presence.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -49,6 +49,7 @@
 		if( !( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT | GET_BUDDY_CREAT ) ) )
 		{
-			if( set_getbool( &ic->irc->set, "debug" ) )
-				imcb_log( ic, "Warning: Could not handle presence information from JID: %s", from );
+			/*
+			imcb_log( ic, "Warning: Could not handle presence information from JID: %s", from );
+			*/
 			return XT_HANDLED;
 		}
@@ -106,6 +107,7 @@
 		if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL )
 		{
-			if( set_getbool( &ic->irc->set, "debug" ) )
-				imcb_log( ic, "Warning: Received presence information from unknown JID: %s", from );
+			/*
+			imcb_log( ic, "Warning: Received presence information from unknown JID: %s", from );
+			*/
 			return XT_HANDLED;
 		}
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ protocols/nogaim.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -249,4 +249,6 @@
 void imcb_connected( struct im_connection *ic )
 {
+	irc_t *irc = ic->irc;
+	struct chat *c;
 	user_t *u;
 	
@@ -271,4 +273,13 @@
 	   exponential backoff timer. */
 	ic->acc->auto_reconnect_delay = 0;
+	
+	for( c = irc->chatrooms; c; c = c->next )
+	{
+		if( c->acc != ic->acc )
+			continue;
+		
+		if( set_getbool( &c->set, "auto_join" ) )
+			chat_join( irc, c, NULL );
+	}
 }
 
@@ -309,4 +320,7 @@
 	ic->acc->prpl->logout( ic );
 	b_event_remove( ic->inpa );
+	
+	g_free( ic->away );
+	ic->away = NULL;
 	
 	u = irc->users;
@@ -716,5 +730,5 @@
 }
 
-struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle )
+struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle )
 {
 	struct groupchat *c;
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ protocols/nogaim.h	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -213,5 +213,5 @@
 	 * not implement this. */
 	struct groupchat *
-	     (* chat_join)	(struct im_connection *, char *room, char *nick, char *password);
+	     (* chat_join)	(struct im_connection *, const char *room, const char *nick, const char *password);
 	/* Change the topic, if supported. Note that BitlBee expects the IM
 	   server to confirm the topic change with a regular topic change
@@ -243,5 +243,5 @@
  * the account_t parameter. */
 G_MODULE_EXPORT struct im_connection *imcb_new( account_t *acc );
-G_MODULE_EXPORT void imcb_free( struct im_connection *ic );
+G_MODULE_EXPORT void imc_free( struct im_connection *ic );
 /* Once you're connected, you should call this function, so that the user will
  * see the success. */
@@ -294,5 +294,5 @@
  *   the user her/himself. At that point the group chat will be visible to the
  *   user, too. */
-G_MODULE_EXPORT struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle );
+G_MODULE_EXPORT struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle );
 G_MODULE_EXPORT void imcb_chat_add_buddy( struct groupchat *b, char *handle );
 /* To remove a handle from a group chat. Reason can be NULL. */
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ protocols/oscar/oscar.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -91,5 +91,5 @@
 	GSList *oscar_chats;
 
-	gboolean killme;
+	gboolean killme, no_reconnect;
 	gboolean icq;
 	GSList *evilhack;
@@ -181,4 +181,5 @@
 static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_logout     (aim_session_t *, aim_frame_t *, ...);
 static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
@@ -294,5 +295,5 @@
 			aim_rxdispatch(odata->sess);
                                if (odata->killme)
-                                       imc_logout(ic, TRUE);
+                                       imc_logout(ic, !odata->no_reconnect);
 		} else {
 			if ((conn->type == AIM_CONN_TYPE_BOS) ||
@@ -520,4 +521,5 @@
 		case 0x18:
 			/* connecting too frequently */
+			od->no_reconnect = TRUE;
 			imcb_error(ic, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
 			break;
@@ -572,4 +574,5 @@
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parsemtn, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, gaim_parse_logout, 0);
 
 	((struct oscar_data *)ic->proto_data)->conn = bosconn;
@@ -747,4 +750,28 @@
 
 	aim_send_login(sess, fr->conn, ic->acc->user, ic->acc->pass, &info, key);
+
+	return 1;
+}
+
+static int gaim_parse_logout(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct im_connection *ic = sess->aux_data;
+	struct oscar_data *odata = ic->proto_data;
+	int code;
+	va_list ap;
+
+	va_start(ap, fr);
+	code = va_arg(ap, int);
+	va_end(ap);
+	
+	imcb_error( ic, "Connection aborted by server: %s", code == 1 ?
+	                "someone else logged in with your account" :
+	                "unknown reason" );
+	
+	/* Tell BitlBee to disable auto_reconnect if code == 1, since that
+	   means a concurrent login somewhere else. */
+	odata->no_reconnect = code == 1;
+	
+	/* DO NOT log out here! Just tell the callback to do it. */
+	odata->killme = TRUE;
 
 	return 1;
@@ -1939,6 +1966,5 @@
 	aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
 
-	if (ic->away)
-		g_free(ic->away);
+	g_free(ic->away);
 	ic->away = NULL;
 
@@ -1960,21 +1986,19 @@
 static void oscar_set_away_icq(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
 {
-    const char *msg = NULL;
+	const char *msg = NULL;
 	gboolean no_message = FALSE;
 
 	/* clean old states */
-    if (ic->away) {
-		g_free(ic->away);
-		ic->away = NULL;
-    }
+	g_free(ic->away);
+	ic->away = NULL;
 	od->sess->aim_icq_state = 0;
 
 	/* if no message, then use an empty message */
-    if (message) {
-        msg = message;
-    } else {
-        msg = "";
+	if (message) {
+		msg = message;
+	} else {
+		msg = "";
 		no_message = TRUE;
-    }
+	}
 
 	if (!g_strcasecmp(state, "Online")) {
@@ -1982,25 +2006,25 @@
 	} else if (!g_strcasecmp(state, "Away")) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
-        ic->away = g_strdup(msg);
+		ic->away = g_strdup(msg);
 		od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
 	} else if (!g_strcasecmp(state, "Do Not Disturb")) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
-        ic->away = g_strdup(msg);
+		ic->away = g_strdup(msg);
 		od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
 	} else if (!g_strcasecmp(state, "Not Available")) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
-        ic->away = g_strdup(msg);
+		ic->away = g_strdup(msg);
 		od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
 	} else if (!g_strcasecmp(state, "Occupied")) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
-        ic->away = g_strdup(msg);
+		ic->away = g_strdup(msg);
 		od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
 	} else if (!g_strcasecmp(state, "Free For Chat")) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
-        ic->away = g_strdup(msg);
+		ic->away = g_strdup(msg);
 		od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
 	} else if (!g_strcasecmp(state, "Invisible")) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
-        ic->away = g_strdup(msg);
+		ic->away = g_strdup(msg);
 	} else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
 	 	if (no_message) {
@@ -2008,5 +2032,5 @@
 		} else {
 			aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
-            ic->away = g_strdup(msg);
+			ic->away = g_strdup(msg);
 			od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
 		}
@@ -2020,5 +2044,5 @@
 	struct oscar_data *od = (struct oscar_data *)ic->proto_data;
 
-    oscar_set_away_aim(ic, od, state, message);
+	oscar_set_away_aim(ic, od, state, message);
 	if (od->icq)
 		oscar_set_away_icq(ic, od, state, message);
@@ -2581,5 +2605,5 @@
 }
 
-struct groupchat *oscar_chat_join(struct im_connection * ic, char * room, char * nick, char * password )
+struct groupchat *oscar_chat_join(struct im_connection * ic, const char * room, const char * nick, const char * password )
 {
 	struct oscar_data * od = (struct oscar_data *)ic->proto_data;
Index: protocols/yahoo/yahoo.c
===================================================================
--- protocols/yahoo/yahoo.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ protocols/yahoo/yahoo.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -197,11 +197,12 @@
 {
 	struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data;
-	
-	ic->away = NULL;
+	char *away;
+	
+	away = NULL;
 	
 	if( state && msg && g_strcasecmp( state, msg ) != 0 )
 	{
 		yd->current_status = YAHOO_STATUS_CUSTOM;
-		ic->away = "";
+		away = "";
 	}
 	else if( state )
@@ -212,9 +213,9 @@
 		msg = NULL;
 		
-		ic->away = "";
+		away = "";
 		if( g_strcasecmp( state, "Available" ) == 0 )
 		{
 			yd->current_status = YAHOO_STATUS_AVAILABLE;
-			ic->away = NULL;
+			away = NULL;
 		}
 		else if( g_strcasecmp( state, "Be Right Back" ) == 0 )
@@ -242,5 +243,5 @@
 			yd->current_status = YAHOO_STATUS_AVAILABLE;
 			
-			ic->away = NULL;
+			away = NULL;
 		}
 	}
@@ -248,5 +249,5 @@
 		yd->current_status = YAHOO_STATUS_AVAILABLE;
 	
-	yahoo_set_away( yd->y2_id, yd->current_status, msg, ic->away != NULL ? 2 : 0 );
+	yahoo_set_away( yd->y2_id, yd->current_status, msg, away != NULL ? 2 : 0 );
 }
 
@@ -791,7 +792,20 @@
 {
 	struct byahoo_conf_invitation *inv = data;
-	
-	yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name );
-	imcb_chat_add_buddy( inv->c, inv->ic->acc->user );
+	struct groupchat *b;
+	
+	for( b = inv->ic->groupchats; b; b = b->next )
+		if( b == inv->c )
+			break;
+	
+	if( b != NULL )
+	{
+		yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name );
+		imcb_chat_add_buddy( inv->c, inv->ic->acc->user );
+	}
+	else
+	{
+		imcb_log( inv->ic, "Duplicate/corrupted invitation to `%s'.", inv->name );
+	}
+	
 	g_free( inv->name );
 	g_free( inv );
Index: root_commands.c
===================================================================
--- root_commands.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ root_commands.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -79,4 +79,16 @@
 }
 
+#define MIN_ARGS( x, y... )                                                    \
+	do                                                                     \
+	{                                                                      \
+		int blaat;                                                     \
+		for( blaat = 0; blaat <= x; blaat ++ )                         \
+			if( cmd[blaat] == NULL )                               \
+			{                                                      \
+				irc_usermsg( irc, "Not enough parameters given (need %d).", x ); \
+				return y;                                      \
+			}                                                      \
+	} while( 0 )
+
 void root_command( irc_t *irc, char *cmd[] )
 {	
@@ -89,9 +101,6 @@
 		if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 )
 		{
-			if( !cmd[commands[i].required_parameters] )
-			{
-				irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters );
-				return;
-			}
+			MIN_ARGS( commands[i].required_parameters );
+			
 			commands[i].execute( irc, cmd );
 			return;
@@ -251,4 +260,119 @@
 }
 
+typedef set_t** (*cmd_set_findhead)( irc_t*, char* );
+typedef int (*cmd_set_checkflags)( irc_t*, set_t *set );
+
+static int cmd_set_real( irc_t *irc, char **cmd, cmd_set_findhead findhead, cmd_set_checkflags checkflags )
+{
+	char *set_full = NULL, *set_name = NULL, *tmp;
+	set_t **head;
+	
+	if( cmd[1] && g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
+	{
+		MIN_ARGS( 2, 0 );
+		set_full = cmd[2];
+	}
+	else
+		set_full = cmd[1];
+	
+	if( findhead == NULL )
+	{
+		set_name = set_full;
+		
+		head = &irc->set;
+	}
+	else 
+	{
+		char *id;
+		
+		if( ( tmp = strchr( set_full, '/' ) ) )
+		{
+			id = g_strndup( set_full, ( tmp - set_full ) );
+			set_name = tmp + 1;
+		}
+		else
+		{
+			id = g_strdup( set_full );
+		}
+		
+		if( ( head = findhead( irc, id ) ) == NULL )
+		{
+			g_free( id );
+			irc_usermsg( irc, "Could not find setting." );
+			return 0;
+		}
+		g_free( id );
+	}
+	
+	if( cmd[1] && cmd[2] && set_name )
+	{
+		set_t *s = set_find( head, set_name );
+		int st;
+		
+		if( checkflags && checkflags( irc, s ) == 0 )
+			return 0;
+		
+		if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
+			st = set_reset( head, set_name );
+		else
+			st = set_setstr( head, set_name, cmd[2] );
+		
+		if( set_getstr( head, set_name ) == NULL )
+		{
+			if( st )
+				irc_usermsg( irc, "Setting changed successfully" );
+			else
+				irc_usermsg( irc, "Failed to change setting" );
+		}
+		else
+		{
+			cmd_showset( irc, head, set_name );
+		}
+	}
+	else if( set_name )
+	{
+		cmd_showset( irc, head, set_name );
+	}
+	else
+	{
+		set_t *s = *head;
+		while( s )
+		{
+			cmd_showset( irc, &s, s->key );
+			s = s->next;
+		}
+	}
+	
+	return 1;
+}
+
+static set_t **cmd_account_set_findhead( irc_t *irc, char *id )
+{
+	account_t *a;
+	
+	if( ( a = account_get( irc, id ) ) )
+		return &a->set;
+	else
+		return NULL;
+}
+
+static int cmd_account_set_checkflags( irc_t *irc, set_t *s )
+{
+	account_t *a = s->data;
+	
+	if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY )
+	{
+		irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "off" );
+		return 0;
+	}
+	else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY )
+	{
+		irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "on" );
+		return 0;
+	}
+	
+	return 1;
+}
+
 static void cmd_account( irc_t *irc, char **cmd )
 {
@@ -265,11 +389,7 @@
 		struct prpl *prpl;
 		
-		if( cmd[2] == NULL || cmd[3] == NULL || cmd[4] == NULL )
-		{
-			irc_usermsg( irc, "Not enough parameters" );
-			return;
-		}
-		
-		prpl = find_protocol(cmd[2]);
+		MIN_ARGS( 4 );
+		
+		prpl = find_protocol( cmd[2] );
 		
 		if( prpl == NULL )
@@ -295,9 +415,7 @@
 	else if( g_strcasecmp( cmd[1], "del" ) == 0 )
 	{
-		if( !cmd[2] )
-		{
-			irc_usermsg( irc, "Not enough parameters given (need %d)", 2 );
-		}
-		else if( !( a = account_get( irc, cmd[2] ) ) )
+		MIN_ARGS( 2 );
+
+		if( !( a = account_get( irc, cmd[2] ) ) )
 		{
 			irc_usermsg( irc, "Invalid account" );
@@ -427,90 +545,11 @@
 	else if( g_strcasecmp( cmd[1], "set" ) == 0 )
 	{
-		char *acc_handle, *set_name = NULL, *tmp;
-		
-		if( !cmd[2] )
-		{
-			irc_usermsg( irc, "Not enough parameters given (need %d)", 2 );
-			return;
-		}
-		
-		if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 )
-			acc_handle = g_strdup( cmd[3] );
-		else
-			acc_handle = g_strdup( cmd[2] );
-		
-		if( !acc_handle )
-		{
-			irc_usermsg( irc, "Not enough parameters given (need %d)", 3 );
-			return;
-		}
-		
-		if( ( tmp = strchr( acc_handle, '/' ) ) )
-		{
-			*tmp = 0;
-			set_name = tmp + 1;
-		}
-		
-		if( ( a = account_get( irc, acc_handle ) ) == NULL )
-		{
-			g_free( acc_handle );
-			irc_usermsg( irc, "Invalid account" );
-			return;
-		}
-		
-		if( cmd[3] && set_name )
-		{
-			set_t *s = set_find( &a->set, set_name );
-			int st;
-			
-			if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY )
-			{
-				g_free( acc_handle );
-				irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "off" );
-				return;
-			}
-			else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY )
-			{
-				g_free( acc_handle );
-				irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "on" );
-				return;
-			}
-			
-			if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 )
-				st = set_reset( &a->set, set_name );
-			else
-				st = set_setstr( &a->set, set_name, cmd[3] );
-			
-			if( set_getstr( &a->set, set_name ) == NULL )
-			{
-				if( st )
-					irc_usermsg( irc, "Setting changed successfully" );
-				else
-					irc_usermsg( irc, "Failed to change setting" );
-			}
-			else
-			{
-				cmd_showset( irc, &a->set, set_name );
-			}
-		}
-		else if( set_name )
-		{
-			cmd_showset( irc, &a->set, set_name );
-		}
-		else
-		{
-			set_t *s = a->set;
-			while( s )
-			{
-				cmd_showset( irc, &s, s->key );
-				s = s->next;
-			}
-		}
-		
-		g_free( acc_handle );
-	}
-	else
-	{
-		irc_usermsg( irc, "Unknown command: account %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[1] );
+		MIN_ARGS( 2 );
+		
+		cmd_set_real( irc, cmd + 1, cmd_account_set_findhead, cmd_account_set_checkflags );
+	}
+	else
+	{
+		irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[1] );
 	}
 }
@@ -523,4 +562,5 @@
 	if( g_strcasecmp( cmd[1], "-tmp" ) == 0 )
 	{
+		MIN_ARGS( 3 );
 		add_on_server = 0;
 		cmd ++;
@@ -842,52 +882,5 @@
 static void cmd_set( irc_t *irc, char **cmd )
 {
-	char *set_name = cmd[1];
-	
-	if( cmd[1] && cmd[2] )
-	{
-		int st;
-		
-		if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
-		{
-			st = set_reset( &irc->set, cmd[2] );
-			set_name = cmd[2];
-		}
-		else
-		{
-			st = set_setstr( &irc->set, cmd[1], cmd[2] );
-		}
-		
-		/* Normally we just show the variable's new/unchanged
-		   value as feedback to the user, but this has always
-		   caused confusion when changing the password. Give
-		   other feedback instead: */
-		if( set_getstr( &irc->set, set_name ) == NULL )
-		{
-			if( st )
-				irc_usermsg( irc, "Setting changed successfully" );
-			else
-				irc_usermsg( irc, "Failed to change setting" );
-		}
-		else
-		{
-			cmd_showset( irc, &irc->set, set_name );
-		}
-	}
-	else if( set_name )
-	{
-		cmd_showset( irc, &irc->set, set_name );
-
-		if( strchr( set_name, '/' ) )
-			irc_usermsg( irc, "Warning: / found in setting name, you're probably looking for the `account set' command." );
-	}
-	else
-	{
-		set_t *s = irc->set;
-		while( s )
-		{
-			cmd_showset( irc, &s, s->key );
-			s = s->next;
-		}
-	}
+	cmd_set_real( irc, cmd, NULL, NULL );
 }
 
@@ -1012,4 +1005,120 @@
 static void cmd_join_chat( irc_t *irc, char **cmd )
 {
+	irc_usermsg( irc, "This command is now obsolete. "
+	                  "Please try the `chat' command instead." );
+}
+
+static set_t **cmd_chat_set_findhead( irc_t *irc, char *id )
+{
+	struct chat *c;
+	
+	if( ( c = chat_get( irc, id ) ) )
+		return &c->set;
+	else
+		return NULL;
+}
+
+static void cmd_chat( irc_t *irc, char **cmd )
+{
+	account_t *acc;
+	struct chat *c;
+	
+	if( g_strcasecmp( cmd[1], "add" ) == 0 )
+	{
+		char *channel, *s;
+		
+		MIN_ARGS( 3 );
+		
+		if( !( acc = account_get( irc, cmd[2] ) ) )
+		{
+			irc_usermsg( irc, "Invalid account" );
+			return;
+		}
+		
+		if( cmd[4] == NULL )
+		{
+			channel = g_strdup( cmd[3] );
+			if( ( s = strchr( channel, '@' ) ) )
+				*s = 0;
+		}
+		else
+		{
+			channel = g_strdup( cmd[4] );
+		}
+		
+		if( strchr( CTYPES, channel[0] ) == NULL )
+		{
+			s = g_strdup_printf( "%c%s", CTYPES[0], channel );
+			g_free( channel );
+			channel = s;
+		}
+		
+		if( ( c = chat_add( irc, acc, cmd[3], channel ) ) )
+			irc_usermsg( irc, "Chatroom added successfully." );
+		else
+			irc_usermsg( irc, "Could not add chatroom." );
+		
+		g_free( channel );
+	}
+	else if( g_strcasecmp( cmd[1], "list" ) == 0 )
+	{
+		int i = 0;
+		
+		if( strchr( irc->umode, 'b' ) )
+			irc_usermsg( irc, "Chatroom list:" );
+		
+		for( c = irc->chatrooms; c; c = c->next )
+		{
+			irc_usermsg( irc, "%2d. %s(%s) %s, %s", i, c->acc->prpl->name,
+			                  c->acc->user, c->handle, c->channel );
+			
+			i ++;
+		}
+		irc_usermsg( irc, "End of chatroom list" );
+	}
+	else if( g_strcasecmp( cmd[1], "set" ) == 0 )
+	{
+		cmd_set_real( irc, cmd + 1, cmd_chat_set_findhead, NULL );
+	}
+	else if( g_strcasecmp( cmd[1], "del" ) == 0 )
+	{
+		MIN_ARGS( 2 );
+		
+		if( ( c = chat_get( irc, cmd[2] ) ) )
+		{
+			chat_del( irc, c );
+		}
+		else
+		{
+			irc_usermsg( irc, "Could not remove chat." );
+		}
+	}
+	else if( g_strcasecmp( cmd[1], "with" ) == 0 )
+	{
+		user_t *u;
+		
+		MIN_ARGS( 2 );
+		
+		if( ( u = user_find( irc, cmd[2] ) ) && u->ic && u->ic->acc->prpl->chat_with )
+		{
+			if( !u->ic->acc->prpl->chat_with( u->ic, u->handle ) )
+			{
+				irc_usermsg( irc, "(Possible) failure while trying to open "
+				                  "a groupchat with %s.", u->nick );
+			}
+		}
+		else
+		{
+			irc_usermsg( irc, "Can't open a groupchat with %s.", cmd[2] );
+		}
+	}
+	else
+	{
+		irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] );
+	}
+
+
+
+#if 0
 	account_t *a;
 	struct im_connection *ic;
@@ -1037,5 +1146,5 @@
 	if( cmd[3] )
 	{
-		if( cmd[3][0] != '#' && cmd[3][0] != '&' )
+		if( strchr( CTYPES, cmd[3][0] ) == NULL )
 			channel = g_strdup_printf( "&%s", cmd[3] );
 		else
@@ -1080,4 +1189,5 @@
 		g_free( channel );
 	}
+#endif
 }
 
@@ -1103,4 +1213,5 @@
 	{ "join_chat",      2, cmd_join_chat,      0 },
 	{ "otr",            1, cmd_otr,            0 },	
+	{ "chat",           1, cmd_chat,           0 },
 	{ NULL }
 };
Index: set.c
===================================================================
--- set.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ set.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -267,5 +267,5 @@
 		mode=3;
 	else
-		return NULL;
+		return SET_INVALID;
 
 	/* sorry for calling them op/deop - too lazy for search+replace :P */
Index: storage.c
===================================================================
--- storage.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ storage.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -121,5 +121,4 @@
 			return status;
 		}
-		
 		if (status != STORAGE_NO_SUCH_USER) 
 			return status;
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ storage_xml.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -38,9 +38,4 @@
 #endif
 
-#if !GLIB_CHECK_VERSION(2,8,0)
-/* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */
-#define g_access access
-#endif
-
 typedef enum
 {
@@ -59,4 +54,6 @@
 	char *current_setting;
 	account_t *current_account;
+	struct chat *current_chat;
+	set_t **current_set_head;
 	char *given_nick;
 	char *given_pass;
@@ -177,5 +174,14 @@
 		
 		if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) )
+		{
+			if( xd->current_chat != NULL )
+				xd->current_set_head = &xd->current_chat->set;
+			else if( xd->current_account != NULL )
+				xd->current_set_head = &xd->current_account->set;
+			else
+				xd->current_set_head = &xd->irc->set;
+			
 			xd->current_setting = g_strdup( setting );
+		}
 		else
 			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
@@ -199,4 +205,21 @@
 		}
 	}
+	else if( g_strcasecmp( element_name, "chat" ) == 0 )
+	{
+		char *handle, *channel;
+		
+		handle = xml_attr( attr_names, attr_values, "handle" );
+		channel = xml_attr( attr_names, attr_values, "channel" );
+		
+		if( xd->current_account && handle && channel )
+		{
+			xd->current_chat = chat_add( xd->irc, xd->current_account, handle, channel );
+		}
+		else
+		{
+			g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+			             "Missing attributes for %s element", element_name );
+		}
+	}
 	else
 	{
@@ -219,4 +242,8 @@
 		xd->current_account = NULL;
 	}
+	else if( g_strcasecmp( element_name, "chat" ) == 0 )
+	{
+		xd->current_chat = NULL;
+	}
 }
 
@@ -225,5 +252,4 @@
 	char text[text_len+1];
 	struct xml_parsedata *xd = data;
-	irc_t *irc = xd->irc;
 	
 	strncpy( text, text_orig, text_len );
@@ -238,6 +264,5 @@
 	else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 && xd->current_setting )
 	{
-		set_setstr( xd->current_account ? &xd->current_account->set : &irc->set,
-		            xd->current_setting, (char*) text );
+		set_setstr( xd->current_set_head, xd->current_setting, (char*) text );
 		g_free( xd->current_setting );
 		xd->current_setting = NULL;
@@ -402,5 +427,5 @@
 	
 	for( set = irc->set; set; set = set->next )
-		if( set->value && set->def )
+		if( set->value )
 			if( !xml_printf( fd, 1, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) )
 				goto write_error;
@@ -411,4 +436,5 @@
 		char *pass_b64;
 		int pass_len;
+		struct chat *c;
 		
 		pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password, 12 );
@@ -429,5 +455,5 @@
 		
 		for( set = acc->set; set; set = set->next )
-			if( set->value && set->def && !( set->flags & ACC_SET_NOSAVE ) )
+			if( set->value && !( set->flags & ACC_SET_NOSAVE ) )
 				if( !xml_printf( fd, 2, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) )
 					goto write_error;
@@ -443,4 +469,23 @@
 			goto write_error;
 		
+		for( c = irc->chatrooms; c; c = c->next )
+		{
+			if( c->acc != acc )
+				continue;
+			
+			if( !xml_printf( fd, 2, "<chat handle=\"%s\" channel=\"%s\" type=\"%s\">\n",
+			                        c->handle, c->channel, "room" ) )
+				goto write_error;
+			
+			for( set = c->set; set; set = set->next )
+				if( set->value && !( set->flags & ACC_SET_NOSAVE ) )
+					if( !xml_printf( fd, 3, "<setting name=\"%s\">%s</setting>\n",
+					                        set->key, set->value ) )
+						goto write_error;
+
+			if( !xml_printf( fd, 2, "</chat>\n" ) )
+				goto write_error;
+		}
+		
 		if( !xml_printf( fd, 1, "</account>\n" ) )
 			goto write_error;
Index: tests/Makefile
===================================================================
--- tests/Makefile	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ tests/Makefile	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -11,5 +11,5 @@
 distclean: clean
 
-main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o
+main_objs = account.o bitlbee.o chat.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o
 
 test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o check_jabber_sasl.o check_jabber_util.o
Index: tests/check_set.c
===================================================================
--- tests/check_set.c	(revision 823de9d44f262ea2364ac8ec6a1e18e0f7dab658)
+++ tests/check_set.c	(revision 673a54c5a78afd1dd41b4cd8811df5ab65042583)
@@ -96,11 +96,4 @@
 END_TEST
 
-START_TEST(test_setstr_implicit)
-	void *data = "data";
-	set_t *s = NULL, *t;
-	set_setstr(&s, "name", "bloe");
-	fail_unless(set_find(&s, "name") != NULL);
-END_TEST
-
 START_TEST(test_set_get_int_unknown)
 	set_t *s = NULL;
@@ -126,5 +119,4 @@
 	tcase_add_test (tc_core, test_setint);
 	tcase_add_test (tc_core, test_setstr);
-	tcase_add_test (tc_core, test_setstr_implicit);
 	return s;
 }
