Index: Makefile
===================================================================
--- Makefile	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ Makefile	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ account.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ bitlbee.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -33,5 +33,5 @@
 
 #define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "1.2.3"
+#define BITLBEE_VERSION "1.2.4"
 #define VERSION BITLBEE_VERSION
 
@@ -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 c5bc47b3197d20ec2d73e3024b932db30dfa3533)
+++ chat.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 c5bc47b3197d20ec2d73e3024b932db30dfa3533)
+++ chat.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ conf.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -308,5 +308,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... */
@@ -315,6 +315,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: doc/CHANGES
===================================================================
--- doc/CHANGES	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ doc/CHANGES	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -3,4 +3,21 @@
 
 http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on
+
+Version 1.2.4:
+- Most important change (and main reason for releasing now): Upgraded Yahoo!
+  module to a newer version to get it working again.
+- join_chat command replaced with the much better chat command:
+  * Similar to how account replaced login/slist/logout. Add a chatroom once,
+    then just /join it like any other room. Also automatic joining at login
+    time is now possible.
+  * Note that the old way of starting groupchats (/join #nickname) is now
+    also deprecated, use "chat with" instead.
+  * See "help chat" and "help chat add" for more information.
+- Rewrote bitlbee.conf parser to be less dumb.
+- Fixed compatibility (hopefully) with AIM mobile messages, certain kinds
+  of Google Talk chatrooms.
+- Fixed numerous stability/reliability bugs over the last year.
+
+Finished 17 Oct 2009
 
 Version 1.2.3:
Index: doc/user-guide/commands.xml
===================================================================
--- doc/user-guide/commands.xml	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ doc/user-guide/commands.xml	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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,100 @@
 			<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>
+
+				<para>
+					Password-protected rooms work exactly like on IRC, by passing the password as an extra argument to /join.
+				</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 +390,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 +583,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>
@@ -580,5 +686,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>
@@ -588,5 +694,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>
@@ -854,27 +960,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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ doc/user-guide/misc.xml	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ irc.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -407,8 +407,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 );
@@ -485,5 +483,5 @@
 	if( line[0] == ':' )
 	{
-		for( i = 0; line[i] != ' '; i ++ );
+		for( i = 0; line[i] && line[i] != ' '; i ++ );
 		line = line + i;
 	}
@@ -781,5 +779,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 +1022,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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ irc.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ irc_commands.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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] );
-		}
 	}
 }
@@ -433,6 +415,6 @@
 			if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) )
 			{
+				g_hash_table_remove( irc->watches, okey );
 				g_free( okey );
-				g_hash_table_remove( irc->watches, okey );
 				
 				irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
Index: lib/events_libevent.c
===================================================================
--- lib/events_libevent.c	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ lib/events_libevent.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ lib/http_client.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ lib/http_client.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ lib/ini.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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,118 @@
 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 = "";
+		
+		close( fd );
+		
+		return ini;
 	}
+
+	if( fd >= 0 )
+		close( fd );
 	
-	return( ini );
+	ini_close( ini );
+
+	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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ lib/ini.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ lib/proxy.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ lib/xmltree.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ lib/xmltree.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/jabber/conference.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 );
 	
@@ -234,6 +234,8 @@
 			    ( strcmp( s, XMLNS_MUC_USER ) == 0 ) )
 			{
-				c = xt_find_node( c->children, "item" );
-				if( ( s = xt_find_attr( c, "jid" ) ) )
+				struct xt_node *item;
+				
+				item = xt_find_node( c->children, "item" );
+				if( ( s = xt_find_attr( item, "jid" ) ) )
 				{
 					/* Yay, found what we need. :-) */
@@ -283,10 +285,13 @@
 	else if( type ) /* type can only be NULL or "unavailable" in this function */
 	{
-		s = strchr( bud->ext_jid, '/' );
-		if( s ) *s = 0;
-		imcb_chat_remove_buddy( chat, bud->ext_jid, NULL );
-		if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS )
-			imcb_remove_buddy( ic, bud->ext_jid, NULL );
-		if( s ) *s = '/';
+		if( ( bud->flags & JBFLAG_IS_CHATROOM ) && bud->ext_jid )
+		{
+			s = strchr( bud->ext_jid, '/' );
+			if( s ) *s = 0;
+			imcb_chat_remove_buddy( chat, bud->ext_jid, NULL );
+			if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS )
+				imcb_remove_buddy( ic, bud->ext_jid, NULL );
+			if( s ) *s = '/';
+		}
 		
 		if( bud == jc->me )
Index: protocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/jabber/iq.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -51,8 +51,9 @@
 	{
 		if( !( ( c = xt_find_node( node->children, "query" ) ) ||
-		       ( c = xt_find_node( node->children, "ping" ) ) ) || /* O_o WHAT is wrong with just <query/> ????? */
+		       ( c = xt_find_node( node->children, "ping" ) ) ) ||
 		    !( s = xt_find_attr( c, "xmlns" ) ) )
 		{
-			imcb_log( ic, "Warning: Received incomplete IQ-%s packet", type );
+			/* Sigh. Who decided to suddenly invent new elements
+			   instead of just sticking with <query/>? */
 			return XT_HANDLED;
 		}
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/jabber/jabber.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/jabber/jabber.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/jabber/presence.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/nogaim.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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;
@@ -492,7 +506,6 @@
 }
 
-/* prpl.c */
-
-struct show_got_added_data
+
+struct imcb_ask_cb_data
 {
 	struct im_connection *ic;
@@ -500,23 +513,61 @@
 };
 
-void show_got_added_no( void *data )
-{
-	g_free( ((struct show_got_added_data*)data)->handle );
+static void imcb_ask_auth_cb_no( void *data )
+{
+	struct imcb_ask_cb_data *cbd = data;
+	
+	cbd->ic->acc->prpl->auth_deny( cbd->ic, cbd->handle );
+	
+	g_free( cbd->handle );
+	g_free( cbd );
+}
+
+static void imcb_ask_auth_cb_yes( void *data )
+{
+	struct imcb_ask_cb_data *cbd = data;
+	
+	cbd->ic->acc->prpl->auth_allow( cbd->ic, cbd->handle );
+	
+	g_free( cbd->handle );
+	g_free( cbd );
+}
+
+void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname )
+{
+	struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 );
+	char *s, *realname_ = NULL;
+	
+	if( realname != NULL )
+		realname_ = g_strdup_printf( " (%s)", realname );
+	
+	s = g_strdup_printf( "The user %s%s wants to add you to his/her buddy list.",
+	                     handle, realname_ ?: "" );
+	
+	g_free( realname_ );
+	
+	data->ic = ic;
+	data->handle = g_strdup( handle );
+	query_add( ic->irc, ic, s, imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, data );
+}
+
+
+static void imcb_ask_add_cb_no( void *data )
+{
+	g_free( ((struct imcb_ask_cb_data*)data)->handle );
 	g_free( data );
 }
 
-void show_got_added_yes( void *data )
-{
-	struct show_got_added_data *sga = data;
-	
-	sga->ic->acc->prpl->add_buddy( sga->ic, sga->handle, NULL );
-	/* imcb_add_buddy( sga->ic, NULL, sga->handle, sga->handle ); */
-	
-	return show_got_added_no( data );
-}
-
-void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname )
-{
-	struct show_got_added_data *data = g_new0( struct show_got_added_data, 1 );
+static void imcb_ask_add_cb_yes( void *data )
+{
+	struct imcb_ask_cb_data *cbd = data;
+	
+	cbd->ic->acc->prpl->add_buddy( cbd->ic, cbd->handle, NULL );
+	
+	return imcb_ask_add_cb_no( data );
+}
+
+void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname )
+{
+	struct imcb_ask_cb_data *data = g_new0( struct imcb_ask_cb_data, 1 );
 	char *s;
 	
@@ -529,5 +580,5 @@
 	data->ic = ic;
 	data->handle = g_strdup( handle );
-	query_add( ic->irc, ic, s, show_got_added_yes, show_got_added_no, data );
+	query_add( ic->irc, ic, s, imcb_ask_add_cb_yes, imcb_ask_add_cb_no, data );
 }
 
@@ -699,5 +750,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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/nogaim.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -209,5 +209,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
@@ -224,4 +224,8 @@
 	 * - Most protocols will just want to set this to g_strcasecmp().*/
 	int (* handle_cmp) (const char *who1, const char *who2);
+
+	/* Implement these callbacks if you want to use imcb_ask_auth() */
+	void (* auth_allow)	(struct im_connection *, const char *who);
+	void (* auth_deny)	(struct im_connection *, const char *who);
 };
 
@@ -239,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. */
@@ -252,4 +256,5 @@
 /* To tell the user an error, ie. before logging out when an error occurs. */
 G_MODULE_EXPORT void imcb_error( struct im_connection *ic, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
+
 /* To ask a your about something.
  * - 'msg' is the question.
@@ -258,5 +263,11 @@
  */
 G_MODULE_EXPORT void imcb_ask( struct im_connection *ic, char *msg, void *data, query_callback doit, query_callback dont );
-G_MODULE_EXPORT void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname );
+
+/* Two common questions you may want to ask:
+ * - X added you to his contact list, allow?
+ * - X is not in your contact list, want to add?
+ */
+G_MODULE_EXPORT void imcb_ask_auth( struct im_connection *ic, const char *handle, const char *realname );
+G_MODULE_EXPORT void imcb_ask_add( struct im_connection *ic, const char *handle, const char *realname );
 
 /* Buddy management */
@@ -290,5 +301,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/aim.h
===================================================================
--- protocols/oscar/aim.h	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/oscar/aim.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -144,4 +144,15 @@
 }
 
+#define AIM_CLIENTINFO_KNOWNGOOD_5_1_3036 { \
+	"AOL Instant Messenger, version 5.1.3036/WIN32", \
+	0x0109, \
+	0x0005, \
+	0x0001, \
+	0x0000, \
+	0x0bdc, \
+	"us", \
+	"en", \
+}
+
 /*
  * I would make 4.1.2010 the default, but they seem to have found
@@ -152,5 +163,5 @@
  * around. (see login.c::memrequest())
  */
-#define AIM_CLIENTINFO_KNOWNGOOD AIM_CLIENTINFO_KNOWNGOOD_3_5_1670
+#define AIM_CLIENTINFO_KNOWNGOOD AIM_CLIENTINFO_KNOWNGOOD_5_1_3036
 
 #ifndef TRUE
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/oscar/oscar.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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/libyahoo2.c
===================================================================
--- protocols/yahoo/libyahoo2.c	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/yahoo/libyahoo2.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -89,4 +89,5 @@
 
 #include "base64.h"
+#include "http_client.h"
 
 #ifdef USE_STRUCT_CALLBACKS
@@ -169,4 +170,5 @@
 	YAHOO_SERVICE_GOTGROUPRENAME, /* < 1, 36(old), 37(new) */
 	YAHOO_SERVICE_SYSMESSAGE = 0x14,
+	YAHOO_SERVICE_SKINNAME = 0x15,
 	YAHOO_SERVICE_PASSTHROUGH2 = 0x16,
 	YAHOO_SERVICE_CONFINVITE = 0x18,
@@ -192,4 +194,5 @@
 	YAHOO_SERVICE_LIST,
 	YAHOO_SERVICE_AUTH = 0x57,
+	YAHOO_SERVICE_AUTHBUDDY = 0x6d,
 	YAHOO_SERVICE_ADDBUDDY = 0x83,
 	YAHOO_SERVICE_REMBUDDY,
@@ -197,4 +200,5 @@
 	YAHOO_SERVICE_REJECTCONTACT,
 	YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */
+	YAHOO_SERVICE_Y7_PING = 0x8A, /* 0 - id and that's it?? */
 	YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/
 	YAHOO_SERVICE_CHATGOTO,
@@ -202,4 +206,5 @@
 	YAHOO_SERVICE_CHATLEAVE,
 	YAHOO_SERVICE_CHATEXIT = 0x9b,
+	YAHOO_SERVICE_CHATADDINVITE = 0x9d,
 	YAHOO_SERVICE_CHATLOGOUT = 0xa0,
 	YAHOO_SERVICE_CHATPING,
@@ -209,5 +214,17 @@
 	YAHOO_SERVICE_PICTURE = 0xbe,
 	YAHOO_SERVICE_PICTURE_UPDATE = 0xc1,
-	YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2
+	YAHOO_SERVICE_PICTURE_UPLOAD = 0xc2,
+	YAHOO_SERVICE_Y6_VISIBILITY=0xc5,
+	YAHOO_SERVICE_Y6_STATUS_UPDATE=0xc6,
+	YAHOO_PHOTOSHARE_INIT=0xd2,	
+	YAHOO_SERVICE_CONTACT_YMSG13=0xd6,
+	YAHOO_PHOTOSHARE_PREV=0xd7,
+	YAHOO_PHOTOSHARE_KEY=0xd8,
+	YAHOO_PHOTOSHARE_TRANS=0xda,
+	YAHOO_FILE_TRANSFER_INIT_YMSG13=0xdc,
+	YAHOO_FILE_TRANSFER_GET_YMSG13=0xdd,
+	YAHOO_FILE_TRANSFER_PUT_YMSG13=0xde,
+	YAHOO_SERVICE_YMSG15_STATUS=0xf0,
+	YAHOO_SERVICE_YMSG15_BUDDY_LIST=0xf1,
 };
 
@@ -733,5 +750,5 @@
 
 	memcpy(data + pos, "YMSG", 4); pos += 4;
-	pos += yahoo_put16(data + pos, 0x000c);
+	pos += yahoo_put16(data + pos, YAHOO_PROTO_VER);
 	pos += yahoo_put16(data + pos, 0x0000);
 	pos += yahoo_put16(data + pos, pktlen + extra_pad);
@@ -747,5 +764,5 @@
 		yahoo_send_data(yid->fd, data, len);
 	else
-	yahoo_add_to_send_queue(yid, data, len);
+		yahoo_add_to_send_queue(yid, data, len);
 	FREE(data);
 }
@@ -1467,7 +1484,13 @@
 
 		if (u->name != NULL) {
-			if (pkt->service == YAHOO_SERVICE_LOGOFF || u->flags == 0) {
+			if (pkt->service == YAHOO_SERVICE_LOGOFF) { /* || u->flags == 0) { Not in YMSG16 */
 				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
 			} else {
+				/* Key 47 always seems to be 1 for YMSG16 */
+				if(!u->state)
+					u->away = 0;
+				else
+					u->away = 1;
+
 				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, u->state, u->msg, u->away, u->idle, u->mobile);
 			}
@@ -1477,4 +1500,62 @@
 		y_list_free_1(t);
 		FREE(u);
+	}
+}
+
+static void yahoo_process_buddy_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	YList *l;
+	int last_packet = 0;
+	char *cur_group = NULL;
+	struct yahoo_buddy *newbud = NULL;
+
+	/* we could be getting multiple packets here */
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+
+		switch(pair->key) {
+		case 300:
+		case 301:
+		case 302:
+		case 303:
+			if ( 315 == atoi(pair->value) )
+				last_packet = 1;
+			break;
+		case 65:
+			g_free(cur_group);
+			cur_group = strdup(pair->value);
+			break;
+		case 7:
+			newbud = y_new0(struct yahoo_buddy, 1);
+			newbud->id = strdup(pair->value);
+			if(cur_group)
+				newbud->group = strdup(cur_group);
+			else {
+				struct yahoo_buddy *lastbud = (struct yahoo_buddy *)y_list_nth(
+								yd->buddies, y_list_length(yd->buddies)-1)->data;
+				newbud->group = strdup(lastbud->group);
+			}
+
+			yd->buddies = y_list_append(yd->buddies, newbud);
+
+			break;
+		}
+	}
+	
+	g_free(cur_group);
+
+	/* we could be getting multiple packets here */
+	if (last_packet)
+		return;
+
+	YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
+
+	/*** We login at the very end of the packet communication */
+	if (!yd->logged_in) {
+		yd->logged_in = TRUE;
+		if(yd->current_status < 0)
+			yd->current_status = yd->initial_status;
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
 	}
 }
@@ -1549,5 +1630,5 @@
 			} 
 
-			if(yd->cookie_y && yd->cookie_t && yd->cookie_c)
+			if(yd->cookie_y && yd->cookie_t)
 				YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id);
 
@@ -2226,4 +2307,190 @@
 }
 
+struct yahoo_https_auth_data
+{
+	struct yahoo_input_data *yid;
+	char *token;
+	char *chal;
+};
+
+static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had);
+static void yahoo_https_auth_token_finish(struct http_request *req);
+static void yahoo_https_auth_init(struct yahoo_https_auth_data *had);
+static void yahoo_https_auth_finish(struct http_request *req);
+
+/* Extract a value from a login.yahoo.com response. Assume CRLF-linebreaks
+   and FAIL miserably if they're not there... */
+static char *yahoo_ha_find_key(char *response, char *key)
+{
+	char *s, *end;
+	int len = strlen(key);
+	
+	s = response;
+	do {
+		if (strncmp(s, key, len) == 0 && s[len] == '=') {
+			s += len + 1;
+			if ((end = strchr(s, '\r')))
+				return g_strndup(s, end - s);
+			else
+				return g_strdup(s);
+		}
+		
+		if ((s = strchr(s, '\n')))
+			s ++;
+	} while (s && *s);
+	
+	return NULL;
+}
+
+static enum yahoo_status yahoo_https_status_parse(int code)
+{
+	switch (code)
+	{
+		case 1212: return YAHOO_LOGIN_PASSWD;
+		case 1213: return YAHOO_LOGIN_LOCK;
+		case 1235: return YAHOO_LOGIN_UNAME;
+		default: return (enum yahoo_status) code;
+	}
+}
+
+static void yahoo_process_auth_0x10(struct yahoo_input_data *yid, const char *seed, const char *sn)
+{
+	struct yahoo_https_auth_data *had = g_new0(struct yahoo_https_auth_data, 1);
+	
+	had->yid = yid;
+	had->chal = g_strdup(seed);
+	
+	yahoo_https_auth_token_init(had);
+}
+
+static void yahoo_https_auth_token_init(struct yahoo_https_auth_data *had)
+{
+	struct yahoo_input_data *yid = had->yid;
+	struct yahoo_data *yd = yid->yd;
+	struct http_request *req;
+	char *login, *passwd, *chal;
+	char *url;
+	
+	login = g_strndup(yd->user, 3 * strlen(yd->user));
+	http_encode(login);
+	passwd = g_strndup(yd->password, 3 * strlen(yd->password));
+	http_encode(passwd);
+	chal = g_strndup(had->chal, 3 * strlen(had->chal));
+	http_encode(chal);
+	
+	url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts=%d&login=%s&passwd=%s&chal=%s",
+	                       (int) time(NULL), login, passwd, chal);
+	
+	req = http_dorequest_url(url, yahoo_https_auth_token_finish, had);
+	
+	g_free(url);
+	g_free(chal);
+	g_free(passwd);
+	g_free(login);
+}
+
+static void yahoo_https_auth_token_finish(struct http_request *req)
+{
+	struct yahoo_https_auth_data *had = req->data;
+	struct yahoo_input_data *yid = had->yid;
+	struct yahoo_data *yd = yid->yd;
+	int st;
+	
+	if (req->status_code != 200) {
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 2000 + req->status_code, NULL);
+		goto fail;
+	}
+	
+	if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) {
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, yahoo_https_status_parse(st), NULL);
+		goto fail;
+	}
+	
+	if ((had->token = yahoo_ha_find_key(req->reply_body, "ymsgr")) == NULL) {
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 3001, NULL);
+		goto fail;
+	}
+	
+	return yahoo_https_auth_init(had);
+	
+fail:
+	g_free(had->token);
+	g_free(had->chal);
+	g_free(had);
+}
+
+static void yahoo_https_auth_init(struct yahoo_https_auth_data *had)
+{
+	struct http_request *req;
+	char *url;
+	
+	url = g_strdup_printf("https://login.yahoo.com/config/pwtoken_login?src=ymsgr&ts=%d&token=%s",
+	                      (int) time(NULL), had->token);
+	
+	req = http_dorequest_url(url, yahoo_https_auth_finish, had);
+	
+	g_free(url);
+}
+
+static void yahoo_https_auth_finish(struct http_request *req)
+{
+	struct yahoo_https_auth_data *had = req->data;
+	struct yahoo_input_data *yid = had->yid;
+	struct yahoo_data *yd = yid->yd;
+	struct yahoo_packet *pack;
+	char *crumb;
+	int st;
+	
+	md5_byte_t result[16];
+	md5_state_t ctx;
+	
+	unsigned char yhash[32];
+
+	if (req->status_code != 200) {
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 2000 + req->status_code, NULL);
+		goto fail;
+	}
+	
+	if (sscanf(req->reply_body, "%d", &st) != 1 || st != 0) {
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, yahoo_https_status_parse(st), NULL);
+		goto fail;
+	}
+	
+	if ((yd->cookie_y = yahoo_ha_find_key(req->reply_body, "Y")) == NULL ||
+	    (yd->cookie_t = yahoo_ha_find_key(req->reply_body, "T")) == NULL ||
+	    (crumb = yahoo_ha_find_key(req->reply_body, "crumb")) == NULL) {
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, 3002, NULL);
+		goto fail;
+	}
+	
+	md5_init(&ctx);  
+	md5_append(&ctx, (unsigned char*) crumb, 11);
+	md5_append(&ctx, (unsigned char*) had->chal, strlen(had->chal));
+	md5_finish(&ctx, result);
+	to_y64(yhash, result, 16);
+
+	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
+	yahoo_packet_hash(pack, 1, yd->user);
+	yahoo_packet_hash(pack, 0, yd->user);
+	yahoo_packet_hash(pack, 277, yd->cookie_y);
+	yahoo_packet_hash(pack, 278, yd->cookie_t);
+	yahoo_packet_hash(pack, 307, (char*) yhash);
+	yahoo_packet_hash(pack, 244, "524223");
+	yahoo_packet_hash(pack, 2, yd->user);
+	yahoo_packet_hash(pack, 2, "1");
+	yahoo_packet_hash(pack, 98, "us");
+	yahoo_packet_hash(pack, 135, "7.5.0.647");
+	
+	yahoo_send_packet(yid, pack, 0);
+		
+	yahoo_packet_free(pack);
+	
+fail:
+	g_free(crumb);
+	g_free(had->token);
+	g_free(had->chal);
+	g_free(had);
+}
+
 static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
 {
@@ -2253,4 +2520,7 @@
 		case 1:
 			yahoo_process_auth_0x0b(yid, seed, sn);
+			break;
+		case 2:
+			yahoo_process_auth_0x10(yid, seed, sn);
 			break;
 		default:
@@ -2408,5 +2678,5 @@
 		
 		yd->buddies = y_list_append(yd->buddies, bud);
-		
+	
 		/* Possibly called already, but at least the call above doesn't
 		   seem to happen every time (not anytime I tried). */
@@ -2415,4 +2685,24 @@
 
 /*	YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */
+}
+
+static void yahoo_process_contact_ymsg13(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	char* who=NULL;
+	char* me=NULL;	
+	char* msg=NULL;
+	YList *l;
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 4)
+			who = pair->value;
+		else if (pair->key == 5)
+			me = pair->value;
+		else
+			DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value));
+	}
+
+	if(pkt->status==3)
+		YAHOO_CALLBACK(ext_yahoo_contact_auth_request)(yid->yd->client_id, me, who, msg);
 }
 
@@ -2628,5 +2918,5 @@
 
 	YList *l;
-	yahoo_dump_unhandled(pkt);
+	// yahoo_dump_unhandled(pkt);
 	for (l = pkt->hash; l; l = l->next) {
 		struct yahoo_pair *pair = l->data;
@@ -2650,4 +2940,5 @@
 {
 	DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
+	yahoo_dump_unhandled(pkt);
 	switch (pkt->service)
 	{
@@ -2661,4 +2952,6 @@
 	case YAHOO_SERVICE_IDACT:
 	case YAHOO_SERVICE_IDDEACT:
+	case YAHOO_SERVICE_Y6_STATUS_UPDATE:
+	case YAHOO_SERVICE_YMSG15_STATUS:
 		yahoo_process_status(yid, pkt);
 		break;
@@ -2674,4 +2967,5 @@
 		yahoo_process_mail(yid, pkt);
 		break;
+	case YAHOO_SERVICE_REJECTCONTACT:
 	case YAHOO_SERVICE_NEWCONTACT:
 		yahoo_process_contact(yid, pkt);
@@ -2714,4 +3008,7 @@
 		yahoo_process_buddyadd(yid, pkt);
 		break;
+	case YAHOO_SERVICE_CONTACT_YMSG13:
+		yahoo_process_contact_ymsg13(yid,pkt);
+		break;
 	case YAHOO_SERVICE_REMBUDDY:
 		yahoo_process_buddydel(yid, pkt);
@@ -2742,5 +3039,4 @@
 	case YAHOO_SERVICE_CHATLOGOFF:
 	case YAHOO_SERVICE_CHATMSG:
-	case YAHOO_SERVICE_REJECTCONTACT:
 	case YAHOO_SERVICE_PEERTOPEER:
 		WARNING(("unhandled service 0x%02x", pkt->service));
@@ -2756,4 +3052,6 @@
 		yahoo_process_picture_upload(yid, pkt);
 		break;	
+	case YAHOO_SERVICE_YMSG15_BUDDY_LIST:	/* Buddy List */
+		yahoo_process_buddy_list(yid, pkt);
 	default:
 		WARNING(("unknown service 0x%02x", pkt->service));
@@ -3539,5 +3837,5 @@
 	yahoo_process_webcam_connection,
 	yahoo_process_chatcat_connection,
-	yahoo_process_search_connection
+	yahoo_process_search_connection,
 };
 
@@ -3557,5 +3855,5 @@
 	} while(len == -1 && errno == EINTR);
 
-	if(len == -1 && errno == EAGAIN)	/* we'll try again later */
+	if(len == -1 && (errno == EAGAIN||errno == EINTR))	/* we'll try again later */
 		return 1;
 
@@ -3760,5 +4058,5 @@
 
 	yahoo_packet_hash(pkt, 5, who);
-	yahoo_packet_hash(pkt, 4, from?from:yd->user);
+	yahoo_packet_hash(pkt, 1, from?from:yd->user);
 	yahoo_packet_hash(pkt, 14, " ");
 	yahoo_packet_hash(pkt, 13, typ ? "1" : "0");
@@ -3775,5 +4073,5 @@
 	struct yahoo_data *yd;
 	struct yahoo_packet *pkt = NULL;
-	int service;
+	int old_status;
 	char s[4];
 
@@ -3783,5 +4081,7 @@
 	yd = yid->yd;
 
-	if (msg) {
+	old_status = yd->current_status;
+
+	if (msg && strncmp(msg,"Invisible",9)) {
 		yd->current_status = YAHOO_STATUS_CUSTOM;
 	} else {
@@ -3789,30 +4089,35 @@
 	}
 
-	if (yd->current_status == YAHOO_STATUS_AVAILABLE)
-		service = YAHOO_SERVICE_ISBACK;
-	else
-		service = YAHOO_SERVICE_ISAWAY;
+	/* Thank you libpurple :) */
+	if (yd->current_status == YAHOO_STATUS_INVISIBLE) {
+		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBILITY, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash(pkt, 13, "2");
+		yahoo_send_packet(yid, pkt, 0);
+		yahoo_packet_free(pkt);
+
+		return;
+	}
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_STATUS_UPDATE, yd->current_status, yd->session_id);
+	snprintf(s, sizeof(s), "%d", yd->current_status);
+	yahoo_packet_hash(pkt, 10, s);
 	 
-	if ((away == 2) && (yd->current_status == YAHOO_STATUS_AVAILABLE)) {
-		pkt = yahoo_packet_new(YAHOO_SERVICE_ISAWAY, YAHOO_STATUS_BRB, yd->session_id);
-		yahoo_packet_hash(pkt, 10, "999");
-		yahoo_packet_hash(pkt, 47, "2");
-	}else {
-		pkt = yahoo_packet_new(service, YAHOO_STATUS_AVAILABLE, yd->session_id);
-		snprintf(s, sizeof(s), "%d", yd->current_status);
-		yahoo_packet_hash(pkt, 10, s);
-		if (yd->current_status == YAHOO_STATUS_CUSTOM) {
-			yahoo_packet_hash(pkt, 19, msg);
-			yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");
-		} else {
-			yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");
-		}
-		
-		
-		
-	}
+	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
+		yahoo_packet_hash(pkt, 19, msg);
+	} else {
+		yahoo_packet_hash(pkt, 19, "");
+	}
+	
+	yahoo_packet_hash(pkt, 47, (away == 2)? "2": (away) ?"1":"0");
 
 	yahoo_send_packet(yid, pkt, 0);
 	yahoo_packet_free(pkt);
+
+	if(old_status == YAHOO_STATUS_INVISIBLE) {
+		pkt = yahoo_packet_new(YAHOO_SERVICE_Y6_VISIBILITY, YAHOO_STATUS_AVAILABLE, 0);
+		yahoo_packet_hash(pkt, 13, "1");
+		yahoo_send_packet(yid, pkt, 0);
+		yahoo_packet_free(pkt);
+	}
 }
 
@@ -3829,5 +4134,8 @@
 	LOG(("yahoo_logoff: current status: %d", yd->current_status));
 
-	if(yd->current_status != -1) {
+	if(yd->current_status != -1 && 0) {
+		/* Meh. Don't send this. The event handlers are not going to
+		   get to do this so it'll just leak memory. And the TCP
+		   connection reset will hopefully be clear enough. */
 		pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id);
 		yd->current_status = -1;
@@ -4062,10 +4370,22 @@
 		return;
 
-	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
-	yahoo_packet_hash(pkt, 1, yd->user);
-	yahoo_packet_hash(pkt, 7, who);
-	yahoo_packet_hash(pkt, 65, group);
+	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YPACKET_STATUS_DEFAULT, yd->session_id);
+
 	if (msg != NULL) /* add message/request "it's me add me" */
 		yahoo_packet_hash(pkt, 14, msg);
+	else
+		yahoo_packet_hash(pkt,14,"");
+
+	yahoo_packet_hash(pkt, 65, group);
+	yahoo_packet_hash(pkt, 97, "1");
+	yahoo_packet_hash(pkt, 1, yd->user);
+	yahoo_packet_hash(pkt, 302, "319");
+	yahoo_packet_hash(pkt, 300, "319");
+	yahoo_packet_hash(pkt, 7, who);
+	yahoo_packet_hash(pkt, 334, "0");
+	yahoo_packet_hash(pkt, 301, "319");
+	yahoo_packet_hash(pkt, 303, "319");
+
+
 	yahoo_send_packet(yid, pkt, 0);
 	yahoo_packet_free(pkt);
@@ -4089,4 +4409,47 @@
 	yahoo_send_packet(yid, pkt, 0);
 	yahoo_packet_free(pkt);
+}
+
+void yahoo_accept_buddy_ymsg13(int id,const char* me,const char* who){
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	struct yahoo_packet* pkt=NULL;
+	pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0);
+
+	yahoo_packet_hash(pkt,1,me ?: yd->user);	
+	yahoo_packet_hash(pkt,5,who);
+	yahoo_packet_hash(pkt,13,"1");
+	yahoo_packet_hash(pkt,334,"0");
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_reject_buddy_ymsg13(int id,const char* me,const char* who,const char* msg){
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	struct yahoo_packet* pkt=NULL;
+	pkt= yahoo_packet_new(YAHOO_SERVICE_CONTACT_YMSG13,YAHOO_STATUS_AVAILABLE,0);
+
+	yahoo_packet_hash(pkt,1,me ?: yd->user);	
+	yahoo_packet_hash(pkt,5,who);
+//	yahoo_packet_hash(pkt,241,YAHOO_PROTO_VER);
+	yahoo_packet_hash(pkt,13,"2");
+	yahoo_packet_hash(pkt,334,"0");
+	yahoo_packet_hash(pkt,97,"1");
+	yahoo_packet_hash(pkt,14,msg?:"");
+
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+
 }
 
Index: protocols/yahoo/yahoo.c
===================================================================
--- protocols/yahoo/yahoo.c	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/yahoo/yahoo.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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,23 +249,26 @@
 		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 );
 }
 
 static GList *byahoo_away_states( struct im_connection *ic )
 {
-	GList *m = NULL;
-
-	m = g_list_append( m, "Available" );
-	m = g_list_append( m, "Be Right Back" );
-	m = g_list_append( m, "Busy" );
-	m = g_list_append( m, "Not At Home" );
-	m = g_list_append( m, "Not At Desk" );
-	m = g_list_append( m, "Not In Office" );
-	m = g_list_append( m, "On Phone" );
-	m = g_list_append( m, "On Vacation" );
-	m = g_list_append( m, "Out To Lunch" );
-	m = g_list_append( m, "Stepped Out" );
-	m = g_list_append( m, "Invisible" );
-	m = g_list_append( m, GAIM_AWAY_CUSTOM );
+	static GList *m = NULL;
+
+	if( m == NULL )
+	{
+		m = g_list_append( m, "Available" );
+		m = g_list_append( m, "Be Right Back" );
+		m = g_list_append( m, "Busy" );
+		m = g_list_append( m, "Not At Home" );
+		m = g_list_append( m, "Not At Desk" );
+		m = g_list_append( m, "Not In Office" );
+		m = g_list_append( m, "On Phone" );
+		m = g_list_append( m, "On Vacation" );
+		m = g_list_append( m, "Out To Lunch" );
+		m = g_list_append( m, "Stepped Out" );
+		m = g_list_append( m, "Invisible" );
+		m = g_list_append( m, GAIM_AWAY_CUSTOM );
+	}
 	
 	return m;
@@ -345,4 +349,18 @@
 	
 	return c;
+}
+
+static void byahoo_auth_allow( struct im_connection *ic, const char *who )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data;
+	
+	yahoo_accept_buddy_ymsg13( yd->y2_id, NULL, who );
+}
+
+static void byahoo_auth_deny( struct im_connection *ic, const char *who )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data;
+	
+	yahoo_reject_buddy_ymsg13( yd->y2_id, NULL, who, NULL );
 }
 
@@ -372,4 +390,7 @@
 	ret->handle_cmp = g_strcasecmp;
 	
+	ret->auth_allow = byahoo_auth_allow;
+	ret->auth_deny = byahoo_auth_deny;
+	
 	register_protocol(ret);
 }
@@ -451,7 +472,5 @@
 	struct byahoo_write_ready_data *d = data;
 	
-	yahoo_write_ready( d->id, d->fd, d->data );
-	
-	return FALSE;
+	return yahoo_write_ready( d->id, d->fd, d->data );
 }
 
@@ -790,7 +809,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 );
@@ -908,9 +940,16 @@
 }
 
+void ext_yahoo_contact_auth_request( int id, const char *myid, const char *who, const char *msg )
+{
+	struct im_connection *ic = byahoo_get_ic_by_id( id );
+	
+	imcb_ask_auth( ic, who, NULL );
+}
+
 void ext_yahoo_contact_added( int id, const char *myid, const char *who, const char *msg )
 {
-	/* Groups schmoups. If I want to handle groups properly I can get the
-	   buddy data from some internal libyahoo2 structure. */
-	imcb_add_buddy( byahoo_get_ic_by_id( id ), (char*) who, NULL );
+	struct im_connection *ic = byahoo_get_ic_by_id( id );
+	
+	imcb_add_buddy( ic, (char*) who, NULL );
 }
 
Index: protocols/yahoo/yahoo2.h
===================================================================
--- protocols/yahoo/yahoo2.h	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/yahoo/yahoo2.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -217,4 +217,7 @@
 void yahoo_buddyicon_request(int id, const char *who);
 
+void yahoo_accept_buddy_ymsg13(int,const char*,const char*);
+void yahoo_reject_buddy_ymsg13(int,const char*,const char*,const char*);
+
 #include "yahoo_httplib.h"
 
Index: protocols/yahoo/yahoo2_callbacks.h
===================================================================
--- protocols/yahoo/yahoo2_callbacks.h	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/yahoo/yahoo2_callbacks.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -361,4 +361,16 @@
 
 /*
+ * Name: ext_yahoo_contact_auth_request
+ * 	Called when a contact wants to add you to his/her contact list
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	myid - the identity s/he added
+ * 	who  - who did it
+ * 	msg  - any message sent
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_auth_request)(int id, const char *myid, const char *who, const char *msg);
+
+
+/*
  * Name: ext_yahoo_contact_added
  * 	Called when a contact is added to your list
Index: protocols/yahoo/yahoo2_types.h
===================================================================
--- protocols/yahoo/yahoo2_types.h	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ protocols/yahoo/yahoo2_types.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -57,5 +57,18 @@
 	YAHOO_LOGIN_LOCK = 14,
 	YAHOO_LOGIN_DUPL = 99,
-	YAHOO_LOGIN_SOCK = -1
+	YAHOO_LOGIN_SOCK = -1,
+};
+
+enum ypacket_status {
+	YPACKET_STATUS_DISCONNECTED = -1,
+	YPACKET_STATUS_DEFAULT = 0,
+	YPACKET_STATUS_SERVERACK = 1,
+	YPACKET_STATUS_GAME     = 0x2,
+	YPACKET_STATUS_AWAY     = 0x4,
+	YPACKET_STATUS_CONTINUED = 0x5,
+	YPACKET_STATUS_INVISIBLE = 12,
+	YPACKET_STATUS_NOTIFY = 0x16, /* TYPING */
+	YPACKET_STATUS_WEBLOGIN = 0x5a55aa55,
+	YPACKET_STATUS_OFFLINE = 0x5a55aa56
 };
 
@@ -85,5 +98,5 @@
 };
 
-#define YAHOO_PROTO_VER 0x000b
+#define YAHOO_PROTO_VER 0x0010
 
 /* Yahoo style/color directives */
@@ -115,5 +128,6 @@
 	YAHOO_CONNECTION_WEBCAM,
 	YAHOO_CONNECTION_CHATCAT,
-	YAHOO_CONNECTION_SEARCH
+	YAHOO_CONNECTION_SEARCH,
+	YAHOO_CONNECTION_AUTH,
 };
 
@@ -131,5 +145,4 @@
 /* chat member attribs */
 #define YAHOO_CHAT_MALE 0x8000
-#define YAHOO_CHAT_FEMALE 0x10000
 #define YAHOO_CHAT_FEMALE 0x10000
 #define YAHOO_CHAT_DUNNO 0x400
Index: root_commands.c
===================================================================
--- root_commands.c	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ root_commands.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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,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( s && 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 )
 {
@@ -264,11 +388,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 +410,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 +540,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] );
 	}
 }
@@ -518,4 +557,5 @@
 	if( g_strcasecmp( cmd[1], "-tmp" ) == 0 )
 	{
+		MIN_ARGS( 3 );
 		add_on_server = 0;
 		cmd ++;
@@ -837,52 +877,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 );
 }
 
@@ -1007,4 +1000,122 @@
 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 )
+	{
+		MIN_ARGS( 2 );
+		
+		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;
@@ -1032,5 +1143,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 +1186,5 @@
 		g_free( channel );
 	}
+#endif
 }
 
@@ -1097,4 +1209,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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ storage_xml.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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;
Index: tests/Makefile
===================================================================
--- tests/Makefile	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ tests/Makefile	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ tests/check_set.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -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;
 }
Index: user.c
===================================================================
--- user.c	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ user.c	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -141,5 +141,5 @@
 }
 
-user_t *user_findhandle( struct im_connection *ic, char *handle )
+user_t *user_findhandle( struct im_connection *ic, const char *handle )
 {
 	user_t *u;
Index: user.h
===================================================================
--- user.h	(revision 0c41177b49c04893e0ce88dbb27f5f5b1aeb5896)
+++ user.h	(revision c5bc47b3197d20ec2d73e3024b932db30dfa3533)
@@ -56,5 +56,5 @@
 int user_del( irc_t *irc, char *nick );
 G_MODULE_EXPORT user_t *user_find( irc_t *irc, char *nick );
-G_MODULE_EXPORT user_t *user_findhandle( struct im_connection *ic, char *handle );
+G_MODULE_EXPORT user_t *user_findhandle( struct im_connection *ic, const char *handle );
 void user_rename( irc_t *irc, char *oldnick, char *newnick );
 
