Index: Makefile
===================================================================
--- Makefile	(revision ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ Makefile	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -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 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 ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ account.c	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -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 ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ bitlbee.h	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -129,4 +129,5 @@
 #include "account.h"
 #include "nick.h"
+#include "chat.h"
 #include "conf.h"
 #include "log.h"
Index: chat.c
===================================================================
--- chat.c	(revision ced1e17474366772791186f865bd45d4decfcaf2)
+++ chat.c	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -0,0 +1,188 @@
+  /********************************************************************\
+  * 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( !chat_chanok( channel ) )
+		return NULL;
+	
+	if( 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 )
+{
+	struct groupchat *gc;
+	char *nick = set_getstr( &c->set, "nick" );
+	
+	if( nick == NULL )
+		nick = irc->nick;
+	
+	if( ( gc = c->acc->prpl->chat_join( c->acc->ic, c->handle, nick, NULL ) ) )
+	{
+		g_free( gc->channel );
+		gc->channel = g_strdup( c->channel );
+		return 1;
+	}
+	
+	return 0;
+}
Index: chat.h
===================================================================
--- chat.h	(revision ced1e17474366772791186f865bd45d4decfcaf2)
+++ chat.h	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -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 );
Index: doc/user-guide/commands.xml
===================================================================
--- doc/user-guide/commands.xml	(revision ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ doc/user-guide/commands.xml	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -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>.
+					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>. For details about the syntax of this command, see <emphasis>help set</emphasis>.
+					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>
@@ -303,4 +386,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>
@@ -486,4 +579,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="ops" type="string" scope="global">
 		<default>both</default>
@@ -854,27 +956,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/misc.xml
===================================================================
--- doc/user-guide/misc.xml	(revision ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ doc/user-guide/misc.xml	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -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: irc.c
===================================================================
--- irc.c	(revision ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ irc.c	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -781,5 +781,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=(ov)@+ 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=(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';
@@ -1022,5 +1024,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 ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ irc.h	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -38,4 +38,5 @@
 #define CMODE "t"
 #define UMODE "s"
+#define CTYPES "&#"
 
 typedef enum
@@ -47,9 +48,4 @@
 	USTATUS_SHUTDOWN = 8
 } irc_status_t;
-
-typedef struct channel
-{
-	char *name;
-} channel_t;
 
 typedef struct irc
@@ -87,4 +83,5 @@
 	struct query *queries;
 	struct account *accounts;
+	struct chat *chatrooms;
 	
 	struct __USER *users;
@@ -100,5 +97,4 @@
 
 #include "user.h"
-// #include "nick.h"
 
 extern GSList *irc_connection_list;
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ irc_commands.c	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -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,25 +193,16 @@
 	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;
+		user_t *u;
+		
+		if( strchr( CTYPES, cmd[1][0] ) == NULL || cmd[1][1] == 0 )
+		{
+			irc_reply( irc, 403, "%s :No such channel", cmd[1] );
+			return;
+		}
+		
+		if( ( c = chat_bychannel( irc, cmd[1] ) ) )
+		{
+			chat_join( irc, c );
 		}
 		else
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ protocols/nogaim.c	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -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 );
+	}
 }
 
Index: root_commands.c
===================================================================
--- root_commands.c	(revision ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ root_commands.c	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -78,4 +78,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[] )
 {	
@@ -88,9 +100,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;
@@ -250,4 +259,112 @@
 }
 
+typedef set_t** (*cmd_set_findhead)( irc_t*, char* );
+
+static int cmd_set_real( irc_t *irc, char **cmd, cmd_set_findhead findhead )
+{
+	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;
+		
+		/*
+		FIXME: Make these work again. Probably a gross hack.
+		
+		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;
+		}
+		*/
+		
+		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 void cmd_account( irc_t *irc, char **cmd )
 {
@@ -264,11 +381,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 )
@@ -290,9 +403,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" );
@@ -422,90 +533,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 );
+	}
+	else
+	{
+		irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[1] );
 	}
 }
@@ -837,52 +869,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 );
 }
 
@@ -1007,4 +992,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 );
+	}
+	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;
@@ -1032,5 +1133,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
@@ -1075,4 +1176,5 @@
 		g_free( channel );
 	}
+#endif
 }
 
@@ -1097,4 +1199,5 @@
 	{ "qlist",          0, cmd_qlist,          0 },
 	{ "join_chat",      2, cmd_join_chat,      0 },
+	{ "chat",           1, cmd_chat,           0 },
 	{ NULL }
 };
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision ba3a8a578430418050a7fda3c949d65434b23cb3)
+++ storage_xml.c	(revision ced1e17474366772791186f865bd45d4decfcaf2)
@@ -54,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;
@@ -172,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,
@@ -194,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
 	{
@@ -214,4 +242,8 @@
 		xd->current_account = NULL;
 	}
+	else if( g_strcasecmp( element_name, "chat" ) == 0 )
+	{
+		xd->current_chat = NULL;
+	}
 }
 
@@ -220,5 +252,4 @@
 	char text[text_len+1];
 	struct xml_parsedata *xd = data;
-	irc_t *irc = xd->irc;
 	
 	strncpy( text, text_orig, text_len );
@@ -233,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;
@@ -397,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;
@@ -406,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 );
@@ -424,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;
@@ -438,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;
