Index: protocols/jabber/conference.c
===================================================================
--- protocols/jabber/conference.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/jabber/conference.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -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/io.c
===================================================================
--- protocols/jabber/io.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/jabber/io.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -375,36 +375,20 @@
 	
 	if( ( c = xt_find_node( node->children, "bind" ) ) )
-	{
-		reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
-		xt_add_attr( reply, "xmlns", XMLNS_BIND );
-		reply = jabber_make_packet( "iq", "set", NULL, reply );
-		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
-		
-		if( !jabber_write_packet( ic, reply ) )
-			return XT_ABORT;
-		
-		jd->flags |= JFLAG_WAIT_BIND;
-	}
+		jd->flags |= JFLAG_WANT_BIND;
 	
 	if( ( c = xt_find_node( node->children, "session" ) ) )
-	{
-		reply = xt_new_node( "session", NULL, NULL );
-		xt_add_attr( reply, "xmlns", XMLNS_SESSION );
-		reply = jabber_make_packet( "iq", "set", NULL, reply );
-		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
-		
-		if( !jabber_write_packet( ic, reply ) )
-			return XT_ABORT;
-		
-		jd->flags |= JFLAG_WAIT_SESSION;
-	}
+		jd->flags |= JFLAG_WANT_SESSION;
 	
 	/* This flag is already set if we authenticated via SASL, so now
 	   we can resume the session in the new stream, if we don't have
 	   to bind/initialize the session. */
-	if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
+	if( jd->flags & JFLAG_AUTHENTICATED && ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )
 	{
 		if( !jabber_get_roster( ic ) )
 			return XT_ABORT;
+	}
+	else if( jd->flags & JFLAG_AUTHENTICATED )
+	{
+		return jabber_pkt_bind_sess( ic, NULL, NULL );
 	}
 	
@@ -441,4 +425,5 @@
 	imcb_log( ic, "Converting stream to TLS" );
 	
+	jd->flags |= JFLAG_STARTTLS_DONE;
 	jd->ssl = ssl_starttls( jd->fd, jabber_connected_ssl, ic );
 	
@@ -531,7 +516,8 @@
 		jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, ic );
 	
-	greet = g_strdup_printf( "<?xml version='1.0' ?>"
-	                         "<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
-	                          "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", jd->server );
+	greet = g_strdup_printf( "%s<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
+	                          "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">", 
+	                          ( jd->flags & JFLAG_STARTTLS_DONE ) ? "" : "<?xml version='1.0' ?>",
+	                          jd->server );
 	
 	st = jabber_write( ic, greet, strlen( greet ) );
Index: protocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/jabber/iq.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -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;
 		}
@@ -297,8 +298,8 @@
 {
 	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c;
+	struct xt_node *c, *reply = NULL;
 	char *s;
 	
-	if( ( c = xt_find_node( node->children, "bind" ) ) )
+	if( node && ( c = xt_find_node( node->children, "bind" ) ) )
 	{
 		c = xt_find_node( c->children, "jid" );
@@ -307,12 +308,31 @@
 			imcb_log( ic, "Server changed session resource string to `%s'", s + 1 );
 		
-		jd->flags &= ~JFLAG_WAIT_BIND;
-	}
-	else
-	{
-		jd->flags &= ~JFLAG_WAIT_SESSION;
-	}
-	
-	if( ( jd->flags & ( JFLAG_WAIT_BIND | JFLAG_WAIT_SESSION ) ) == 0 )
+		jd->flags &= ~JFLAG_WANT_BIND;
+	}
+	else if( node && ( c = xt_find_node( node->children, "session" ) ) )
+	{
+		jd->flags &= ~JFLAG_WANT_SESSION;
+	}
+	
+	if( jd->flags & JFLAG_WANT_BIND )
+	{
+		reply = xt_new_node( "bind", NULL, xt_new_node( "resource", set_getstr( &ic->acc->set, "resource" ), NULL ) );
+		xt_add_attr( reply, "xmlns", XMLNS_BIND );
+	}
+	else if( jd->flags & JFLAG_WANT_SESSION )
+	{
+		reply = xt_new_node( "session", NULL, NULL );
+		xt_add_attr( reply, "xmlns", XMLNS_SESSION );
+	}
+	
+	if( reply != NULL )
+	{
+		reply = jabber_make_packet( "iq", "set", NULL, reply );
+		jabber_cache_add( ic, reply, jabber_pkt_bind_sess );
+		
+		if( !jabber_write_packet( ic, reply ) )
+			return XT_ABORT;
+	}
+	else if( ( jd->flags & ( JFLAG_WANT_BIND | JFLAG_WANT_SESSION ) ) == 0 )
 	{
 		if( !jabber_get_roster( ic ) )
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/jabber/jabber.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -70,5 +70,5 @@
 	
 	s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
-	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
+	s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
 	
 	s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc );
@@ -80,4 +80,6 @@
 	s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc );
 	s->flags |= ACC_SET_OFFLINE_ONLY;
+	
+	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE;
 }
 
@@ -364,8 +366,9 @@
 	while( bud )
 	{
-		imcb_log( ic, "Buddy %s (%d) information:\nAway state: %s\nAway message: %s",
-		                   bud->full_jid, bud->priority,
-		                   bud->away_state ? bud->away_state->full_name : "(none)",
-		                   bud->away_message ? : "(none)" );
+		imcb_log( ic, "Buddy %s (%d) information:", bud->full_jid, bud->priority );
+		if( bud->away_state )
+			imcb_log( ic, "Away state: %s", bud->away_state->full_name );
+		imcb_log( ic, "Status message: %s", bud->away_message ? : "(none)" );
+		
 		bud = bud->next;
 	}
@@ -377,9 +380,10 @@
 {
 	struct jabber_data *jd = ic->proto_data;
-	struct jabber_away_state *state;
-	
-	/* Save all this info. We need it, for example, when changing the priority setting. */
-	state = (void *) jabber_away_state_by_name( state_txt );
-	jd->away_state = state ? state : (void *) jabber_away_state_list; /* Fall back to "Away" if necessary. */
+	
+	/* state_txt == NULL -> Not away.
+	   Unknown state -> fall back to the first defined away state. */
+	jd->away_state = state_txt ? jabber_away_state_by_name( state_txt )
+	                 ? : jabber_away_state_list : NULL;
+	
 	g_free( jd->away_message );
 	jd->away_message = ( message && *message ) ? g_strdup( message ) : NULL;
@@ -425,5 +429,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 c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/jabber/jabber.h	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -27,7 +27,7 @@
 #include <glib.h>
 
-#include "xmltree.h"
 #include "bitlbee.h"
 #include "md5.h"
+#include "xmltree.h"
 
 extern GSList *jabber_connections;
@@ -40,10 +40,11 @@
 	JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after
 	                                   SASL or TLS). */
-	JFLAG_WAIT_SESSION = 8,	        /* Set if we sent a <session> tag and need a reply
+	JFLAG_WANT_SESSION = 8,	        /* Set if the server wants a <session/> tag
 	                                   before we continue. */
-	JFLAG_WAIT_BIND = 16,           /* ... for <bind> tag. */
+	JFLAG_WANT_BIND = 16,           /* ... for <bind> tag. */
 	JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this
 	                                   activates all XEP-85 related code. */
 	JFLAG_XMLCONSOLE = 64,          /* If the user added an xmlconsole buddy. */
+	JFLAG_STARTTLS_DONE = 128,      /* If a plaintext session was converted to TLS. */
 } jabber_flags_t;
 
@@ -84,5 +85,5 @@
 	/* After changing one of these two (or the priority setting), call
 	   presence_send_update() to inform the server about the changes. */
-	struct jabber_away_state *away_state;
+	const struct jabber_away_state *away_state;
 	char *away_message;
 	
@@ -241,5 +242,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/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/jabber/jabber_util.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -37,8 +37,8 @@
 		/* Priority is a signed 8-bit integer, according to RFC 3921. */
 		if( i < -128 || i > 127 )
-			return NULL;
+			return SET_INVALID;
 	}
 	else
-		return NULL;
+		return SET_INVALID;
 	
 	/* Only run this stuff if the account is online ATM,
@@ -228,8 +228,7 @@
 {
 	{ "away",  "Away" },
-	{ "chat",  "Free for Chat" },
+	{ "chat",  "Free for Chat" },   /* WTF actually uses this? */
 	{ "dnd",   "Do not Disturb" },
 	{ "xa",    "Extended Away" },
-	{ "",      "Online" },
 	{ "",      NULL }
 };
@@ -238,4 +237,7 @@
 {
 	int i;
+	
+	if( code == NULL )
+		return NULL;
 	
 	for( i = 0; jabber_away_state_list[i].full_name; i ++ )
@@ -249,4 +251,7 @@
 {
 	int i;
+	
+	if( name == NULL )
+		return NULL;
 	
 	for( i = 0; jabber_away_state_list[i].full_name; i ++ )
Index: protocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/jabber/presence.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -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;
 		}
@@ -188,11 +190,10 @@
 		int is_away = 0;
 
-		if( send_presence->away_state && !( *send_presence->away_state->code == 0 ||
-		    strcmp( send_presence->away_state->code, "chat" ) == 0 ) )
+		if( send_presence->away_state &&
+		    strcmp( send_presence->away_state->code, "chat" ) != 0 )
 			is_away = OPT_AWAY;
 
 		imcb_buddy_status( ic, send_presence->bare_jid, OPT_LOGGED_IN | is_away,
-		                   ( is_away && send_presence->away_state ) ?
-		                   send_presence->away_state->full_name : NULL,
+		                   is_away ? send_presence->away_state->full_name : NULL,
 		                   send_presence->away_message );
 	}
@@ -207,6 +208,4 @@
 	struct jabber_data *jd = ic->proto_data;
 	struct xt_node *node, *cap;
-	char *show = jd->away_state->code;
-	char *status = jd->away_message;
 	struct groupchat *c;
 	int st;
@@ -214,8 +213,8 @@
 	node = jabber_make_packet( "presence", NULL, NULL, NULL );
 	xt_add_child( node, xt_new_node( "priority", set_getstr( &ic->acc->set, "priority" ), NULL ) );
-	if( show && *show )
-		xt_add_child( node, xt_new_node( "show", show, NULL ) );
-	if( status )
-		xt_add_child( node, xt_new_node( "status", status, NULL ) );
+	if( jd->away_state )
+		xt_add_child( node, xt_new_node( "show", jd->away_state->code, NULL ) );
+	if( jd->away_message )
+		xt_add_child( node, xt_new_node( "status", jd->away_message, NULL ) );
 	
 	/* This makes the packet slightly bigger, but clients interested in
Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/msn/msn.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -117,5 +117,4 @@
 {
 	struct msn_switchboard *sb;
-	struct msn_data *md = ic->proto_data;
 	
 	if( ( sb = msn_sb_by_handle( ic, who ) ) )
@@ -126,5 +125,4 @@
 	{
 		struct msn_message *m;
-		char buf[1024];
 		
 		/* Create a message. We have to arrange a usable switchboard, and send the message later. */
@@ -133,38 +131,5 @@
 		m->text = g_strdup( message );
 		
-		/* FIXME: *CHECK* the reliability of using spare sb's! */
-		if( ( sb = msn_sb_spare( ic ) ) )
-		{
-			debug( "Trying to use a spare switchboard to message %s", who );
-			
-			sb->who = g_strdup( who );
-			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
-			if( msn_sb_write( sb, buf, strlen( buf ) ) )
-			{
-				/* He/She should join the switchboard soon, let's queue the message. */
-				sb->msgq = g_slist_append( sb->msgq, m );
-				return( 1 );
-			}
-		}
-		
-		debug( "Creating a new switchboard to message %s", who );
-		
-		/* If we reach this line, there was no spare switchboard, so let's make one. */
-		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
-		if( !msn_write( ic, buf, strlen( buf ) ) )
-		{
-			g_free( m->who );
-			g_free( m->text );
-			g_free( m );
-			
-			return( 0 );
-		}
-		
-		/* And queue the message to md. We'll pick it up when the switchboard comes up. */
-		md->msgq = g_slist_append( md->msgq, m );
-		
-		/* FIXME: If the switchboard creation fails, the message will not be sent. */
-		
-		return( 1 );
+		return msn_sb_write_msg( ic, m );
 	}
 	
@@ -178,6 +143,7 @@
 	
 	if( l == NULL )
-		for( i = 0; msn_away_state_list[i].number > -1; i ++ )
-			l = g_list_append( l, (void*) msn_away_state_list[i].name );
+		for( i = 0; *msn_away_state_list[i].code; i ++ )
+			if( *msn_away_state_list[i].name )
+				l = g_list_append( l, (void*) msn_away_state_list[i].name );
 	
 	return l;
@@ -188,15 +154,12 @@
 	char buf[1024];
 	struct msn_data *md = ic->proto_data;
-	const struct msn_away_state *st;
-	
-	if( strcmp( state, GAIM_AWAY_CUSTOM ) == 0 )
-		st = msn_away_state_by_name( "Away" );
+	
+	if( state )
+		md->away_state = msn_away_state_by_name( state ) ? :
+		                 msn_away_state_list + 1;
 	else
-		st = msn_away_state_by_name( state );
-	
-	if( !st ) st = msn_away_state_list;
-	md->away_state = st;
-	
-	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code );
+		md->away_state = msn_away_state_list;
+	
+	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, md->away_state->code );
 	msn_write( ic, buf, strlen( buf ) );
 }
@@ -256,6 +219,4 @@
 {
 	struct msn_switchboard *sb;
-	struct msn_data *md = ic->proto_data;
-	char buf[1024];
 	
 	if( ( sb = msn_sb_by_handle( ic, who ) ) )
@@ -267,20 +228,4 @@
 	{
 		struct msn_message *m;
-		
-		if( ( sb = msn_sb_spare( ic ) ) )
-		{
-			debug( "Trying to reuse an existing switchboard as a groupchat with %s", who );
-			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
-			if( msn_sb_write( sb, buf, strlen( buf ) ) )
-				return msn_sb_to_chat( sb );
-		}
-		
-		/* If the stuff above failed for some reason: */
-		debug( "Creating a new switchboard to groupchat with %s", who );
-		
-		/* Request a new switchboard. */
-		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
-		if( !msn_write( ic, buf, strlen( buf ) ) )
-			return( 0 );
 		
 		/* Create a magic message. This is quite hackish, but who cares? :-P */
@@ -289,8 +234,6 @@
 		m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE );
 		
-		/* Queue the magic message and cross your fingers. */
-		md->msgq = g_slist_append( md->msgq, m );
-		
-		/* FIXME: Can I try to return something here already? */
+		msn_sb_write_msg( ic, m );
+
 		return NULL;
 	}
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/msn/msn.h	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -23,4 +23,7 @@
   Suite 330, Boston, MA  02111-1307  USA
 */
+
+#ifndef _MSN_H
+#define _MSN_H
 
 /* Some hackish magicstrings to make special-purpose messages/switchboards.
@@ -94,5 +97,4 @@
 struct msn_away_state
 {
-	int number;
 	char code[4];
 	char name[16];
@@ -176,2 +178,5 @@
 void msn_sb_destroy( struct msn_switchboard *sb );
 gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond );
+int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m );
+
+#endif //_MSN_H
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/msn/msn_util.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -171,7 +171,7 @@
 		
 		/* End of headers? */
-		if( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||
-		    strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ||
-		    strncmp( text + i - 2, "\r\r", 2 ) == 0 )
+		if( ( i >= 4 && strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ) ||
+		    ( i >= 2 && ( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||   
+		                  strncmp( text + i - 2, "\r\r", 2 ) == 0 ) ) )
 		{
 			break;
@@ -374,5 +374,5 @@
 	*list = NULL;
 	
-	imcb_log( ic, ret->str );
+	imcb_log( ic, "%s", ret->str );
 	g_string_free( ret, TRUE );
 }
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/msn/ns.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -420,9 +420,10 @@
 		{
 			/* FIXME: Warn/Bomb about unknown away state? */
-			st = msn_away_state_list;
-		}
-		
-		imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN |
-		                   ( st->number ? OPT_AWAY : 0 ), st->name, NULL );
+			st = msn_away_state_list + 1;
+		}
+		
+		imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN | 
+		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),
+		                   st->name, NULL );
 	}
 	else if( strcmp( cmd[0], "FLN" ) == 0 )
@@ -449,9 +450,10 @@
 		{
 			/* FIXME: Warn/Bomb about unknown away state? */
-			st = msn_away_state_list;
-		}
-		
-		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN |
-		                   ( st->number ? OPT_AWAY : 0 ), st->name, NULL );
+			st = msn_away_state_list + 1;
+		}
+		
+		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN | 
+		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),
+		                   st->name, NULL );
 	}
 	else if( strcmp( cmd[0], "RNG" ) == 0 )
@@ -663,6 +665,6 @@
 				}
 				
-				if( arg1 ) g_free( arg1 );
-				if( mtype ) g_free( mtype );
+				g_free( arg1 );
+				g_free( mtype );
 			}
 			else if( g_strncasecmp( ct, "text/x-msmsgsprofile", 20 ) == 0 )
@@ -672,23 +674,28 @@
 			else if( g_strncasecmp( ct, "text/x-msmsgsinitialemailnotification", 37 ) == 0 )
 			{
-				char *inbox = msn_findheader( body, "Inbox-Unread:", blen );
-				char *folders = msn_findheader( body, "Folders-Unread:", blen );
-				
-				if( inbox && folders && set_getbool( &ic->acc->set, "mail_notifications" ) )
+				if( set_getbool( &ic->acc->set, "mail_notifications" ) )
 				{
-					imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
+					char *inbox = msn_findheader( body, "Inbox-Unread:", blen );
+					char *folders = msn_findheader( body, "Folders-Unread:", blen );
+
+					if( inbox && folders )
+						imcb_log( ic, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
+					
+					g_free( inbox );
+					g_free( folders );
 				}
-				
-				g_free( inbox );
-				g_free( folders );
 			}
 			else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 )
 			{
-				char *from = msn_findheader( body, "From-Addr:", blen );
-				char *fromname = msn_findheader( body, "From:", blen );
-				
-				if( from && fromname && set_getbool( &ic->acc->set, "mail_notifications" ) )
+				if( set_getbool( &ic->acc->set, "mail_notifications" ) )
 				{
-					imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from );
+					char *from = msn_findheader( body, "From-Addr:", blen );
+					char *fromname = msn_findheader( body, "From:", blen );
+					
+					if( from && fromname )
+						imcb_log( ic, "Received an e-mail message from %s <%s>.", fromname, from );
+
+					g_free( from );
+					g_free( fromname );
 				}
 			}
Index: protocols/msn/sb.c
===================================================================
--- protocols/msn/sb.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/msn/sb.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -44,4 +44,46 @@
 		return( 0 );
 	}
+	
+	return( 1 );
+}
+
+int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m )
+{
+	struct msn_data *md = ic->proto_data;
+	struct msn_switchboard *sb;
+	char buf[1024];
+
+	/* FIXME: *CHECK* the reliability of using spare sb's! */
+	if( ( sb = msn_sb_spare( ic ) ) )
+	{
+		debug( "Trying to use a spare switchboard to message %s", m->who );
+		
+		sb->who = g_strdup( m->who );
+		g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, m->who );
+		if( msn_sb_write( sb, buf, strlen( buf ) ) )
+		{
+			/* He/She should join the switchboard soon, let's queue the message. */
+			sb->msgq = g_slist_append( sb->msgq, m );
+			return( 1 );
+		}
+	}
+	
+	debug( "Creating a new switchboard to message %s", m->who );
+	
+	/* If we reach this line, there was no spare switchboard, so let's make one. */
+	g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
+	if( !msn_write( ic, buf, strlen( buf ) ) )
+	{
+		g_free( m->who );
+		g_free( m->text );
+		g_free( m );
+		
+		return( 0 );
+	}
+	
+	/* And queue the message to md. We'll pick it up when the switchboard comes up. */
+	md->msgq = g_slist_append( md->msgq, m );
+	
+	/* FIXME: If the switchboard creation fails, the message will not be sent. */
 	
 	return( 1 );
Index: protocols/msn/tables.c
===================================================================
--- protocols/msn/tables.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/msn/tables.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -29,25 +29,14 @@
 const struct msn_away_state msn_away_state_list[] =
 {
-	{  0, "NLN", "Available" },
-	{  1, "BSY", "Busy" },
-	{  3, "IDL", "Idle" },
-	{  5, "BRB", "Be Right Back" },
-	{  7, "AWY", "Away" },
-	{  9, "PHN", "On the Phone" },
-	{ 11, "LUN", "Out to Lunch" },
-	{ 13, "HDN", "Hidden" },
-	{ -1, "",    "" }
+	{ "NLN", "" },
+	{ "AWY", "Away" },
+	{ "BSY", "Busy" },
+	{ "IDL", "Idle" },
+	{ "BRB", "Be Right Back" },
+	{ "PHN", "On the Phone" },
+	{ "LUN", "Out to Lunch" },
+	{ "HDN", "Hidden" },
+	{ "",    "" }
 };
-
-const struct msn_away_state *msn_away_state_by_number( int number )
-{
-	int i;
-	
-	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
-		if( msn_away_state_list[i].number == number )
-			return( msn_away_state_list + i );
-	
-	return( NULL );
-}
 
 const struct msn_away_state *msn_away_state_by_code( char *code )
@@ -55,9 +44,9 @@
 	int i;
 	
-	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+	for( i = 0; *msn_away_state_list[i].code; i ++ )
 		if( g_strcasecmp( msn_away_state_list[i].code, code ) == 0 )
 			return( msn_away_state_list + i );
 	
-	return( NULL );
+	return NULL;
 }
 
@@ -66,9 +55,9 @@
 	int i;
 	
-	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+	for( i = 0; *msn_away_state_list[i].code; i ++ )
 		if( g_strcasecmp( msn_away_state_list[i].name, name ) == 0 )
 			return( msn_away_state_list + i );
 	
-	return( NULL );
+	return NULL;
 }
 
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/nogaim.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2006 Wilmer van der Gaast and others                *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -33,6 +33,8 @@
 
 #define BITLBEE_CORE
+#include <ctype.h>
+
 #include "nogaim.h"
-#include <ctype.h>
+#include "chat.h"
 
 static int remove_chat_buddy_silent( struct groupchat *b, const char *handle );
@@ -249,4 +251,6 @@
 void imcb_connected( struct im_connection *ic )
 {
+	irc_t *irc = ic->irc;
+	struct chat *c;
 	user_t *u;
 	
@@ -264,7 +268,19 @@
 	ic->flags |= OPT_LOGGED_IN;
 	
-	/* Also necessary when we're not away, at least for some of the
-	   protocols. */
-	imc_set_away( ic, u->away );
+	/* Necessary to send initial presence status, even if we're not away. */
+	imc_away_send_update( ic );
+	
+	/* Apparently we're connected successfully, so reset the
+	   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 );
+	}
 }
 
@@ -290,4 +306,5 @@
 	user_t *t, *u;
 	account_t *a;
+	int delay;
 	
 	/* Nested calls might happen sometimes, this is probably the best
@@ -305,4 +322,7 @@
 	b_event_remove( ic->inpa );
 	
+	g_free( ic->away );
+	ic->away = NULL;
+	
 	u = irc->users;
 	while( u )
@@ -329,8 +349,7 @@
 	}
 	else if( allow_reconnect && set_getbool( &irc->set, "auto_reconnect" ) &&
-	         set_getbool( &a->set, "auto_reconnect" ) )
-	{
-		int delay = set_getint( &irc->set, "auto_reconnect_delay" );
-		
+	         set_getbool( &a->set, "auto_reconnect" ) &&
+	         ( delay = account_reconnect_delay( a ) ) > 0 )
+	{
 		imcb_log( ic, "Reconnecting in %d seconds..", delay );
 		a->reconnect = b_timeout_add( delay * 1000, auto_reconnect, a );
@@ -429,4 +448,5 @@
 {
 	user_t *u = user_findhandle( ic, handle );
+	char *set;
 	
 	if( !u || !realname ) return;
@@ -441,4 +461,21 @@
 			imcb_log( ic, "User `%s' changed name to `%s'", u->nick, u->realname );
 	}
+	
+	set = set_getstr( &ic->acc->set, "nick_source" );
+	if( strcmp( set, "handle" ) != 0 )
+	{
+		char *name = g_strdup( realname );
+		
+		if( strcmp( set, "first_name" ) == 0 )
+		{
+			int i;
+			for( i = 0; name[i] && !isspace( name[i] ); i ++ ) {}
+			name[i] = '\0';
+		}
+		
+		imcb_buddy_nick_hint( ic, handle, name );
+		
+		g_free( name );
+	}
 }
 
@@ -453,5 +490,5 @@
 /* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM
    modules to suggest a nickname for a handle. */
-void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick )
+void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick )
 {
 	user_t *u = user_findhandle( ic, handle );
@@ -488,7 +525,6 @@
 }
 
-/* prpl.c */
-
-struct show_got_added_data
+
+struct imcb_ask_cb_data
 {
 	struct im_connection *ic;
@@ -496,23 +532,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;
 	
@@ -525,5 +599,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 );
 }
 
@@ -924,12 +998,8 @@
 	int st;
 	
-	if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
-		st = 1;
-	else if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
-		st = 0;
-	else if( sscanf( value, "%d", &st ) != 1 )
-		return( NULL );
-	
-	st = st != 0;
+	if( !is_bool( value ) )
+		return SET_INVALID;
+	
+	st = bool2int( value );
 	
 	/* Horror.... */
@@ -975,5 +1045,5 @@
 	}
 	
-	return( set_eval_bool( set, value ) );
+	return value;
 }
 
@@ -1017,49 +1087,28 @@
 }
 
-static char *imc_away_alias_find( GList *gcm, char *away );
-
-int imc_set_away( struct im_connection *ic, char *away )
-{
-	GList *m, *ms;
-	char *s;
-	
-	if( !away ) away = "";
-	ms = m = ic->acc->prpl->away_states( ic );
-	
-	while( m )
-	{
-		if( *away )
-		{
-			if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
-				break;
-		}
-		else
-		{
-			if( g_strcasecmp( m->data, "Available" ) == 0 )
-				break;
-			if( g_strcasecmp( m->data, "Online" ) == 0 )
-				break;
-		}
-		m = m->next;
-	}
-	
-	if( m )
-	{
-		ic->acc->prpl->set_away( ic, m->data, *away ? away : NULL );
-	}
-	else
-	{
-		s = imc_away_alias_find( ms, away );
-		if( s )
-		{
-			ic->acc->prpl->set_away( ic, s, away );
-			if( set_getbool( &ic->irc->set, "debug" ) )
-				imcb_log( ic, "Setting away state to %s", s );
-		}
-		else
-			ic->acc->prpl->set_away( ic, GAIM_AWAY_CUSTOM, away );
-	}
-	
-	return( 1 );
+static char *imc_away_state_find( GList *gcm, char *away, char **message );
+
+int imc_away_send_update( struct im_connection *ic )
+{
+	char *away, *msg = NULL;
+	
+	away = set_getstr( &ic->acc->set, "away" ) ?
+	     : set_getstr( &ic->irc->set, "away" );
+	if( away && *away )
+	{
+		GList *m = ic->acc->prpl->away_states( ic );
+		msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
+		away = imc_away_state_find( m, away, &msg ) ? : m->data;
+	}
+	else if( ic->acc->flags & ACC_FLAG_STATUS_MESSAGE )
+	{
+		away = NULL;
+		msg = set_getstr( &ic->acc->set, "status" ) ?
+		    : set_getstr( &ic->irc->set, "status" );
+	}
+	
+	ic->acc->prpl->set_away( ic, away, msg );
+	
+	return 1;
 }
 
@@ -1076,14 +1125,31 @@
 };
 
-static char *imc_away_alias_find( GList *gcm, char *away )
+static char *imc_away_state_find( GList *gcm, char *away, char **message )
 {
 	GList *m;
 	int i, j;
 	
+	for( m = gcm; m; m = m->next )
+		if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
+		{
+			/* At least the Yahoo! module works better if message
+			   contains no data unless it adds something to what
+			   we have in state already. */
+			if( strlen( m->data ) == strlen( away ) )
+				*message = NULL;
+			
+			return m->data;
+		}
+	
 	for( i = 0; *imc_away_alias_list[i]; i ++ )
 	{
+		int keep_message;
+		
 		for( j = 0; imc_away_alias_list[i][j]; j ++ )
 			if( g_strncasecmp( away, imc_away_alias_list[i][j], strlen( imc_away_alias_list[i][j] ) ) == 0 )
+			{
+				keep_message = strlen( away ) != strlen( imc_away_alias_list[i][j] );
 				break;
+			}
 		
 		if( !imc_away_alias_list[i][j] )	/* If we reach the end, this row */
@@ -1093,15 +1159,20 @@
 		for( j = 0; imc_away_alias_list[i][j]; j ++ )
 		{
-			m = gcm;
-			while( m )
-			{
+			for( m = gcm; m; m = m->next )
 				if( g_strcasecmp( imc_away_alias_list[i][j], m->data ) == 0 )
-					return( imc_away_alias_list[i][j] );
-				m = m->next;
-			}
-		}
-	}
-	
-	return( NULL );
+				{
+					if( !keep_message )
+						*message = NULL;
+					
+					return imc_away_alias_list[i][j];
+				}
+		}
+		
+		/* No need to look further, apparently this state doesn't
+		   have any good alias for this protocol. */
+		break;
+	}
+	
+	return NULL;
 }
 
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/nogaim.h	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -39,4 +39,6 @@
 #define _NOGAIM_H
 
+#include <stdint.h>
+
 #include "bitlbee.h"
 #include "account.h"
@@ -47,5 +49,4 @@
 
 #define WEBSITE "http://www.bitlbee.org/"
-#define GAIM_AWAY_CUSTOM "Custom"
 
 /* Sharing flags between all kinds of things. I just hope I won't hit any
@@ -208,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
@@ -216,6 +217,6 @@
 	
 	/* You can tell what away states your protocol supports, so that
-	 * BitlBee will try to map the IRC away reasons to them, or use
-	 * GAIM_AWAY_CUSTOM when calling skype_set_away(). */
+	 * BitlBee will try to map the IRC away reasons to them. If your
+	 * protocol doesn't have any, just return one generic "Away". */
 	GList *(* away_states)(struct im_connection *ic);
 	
@@ -223,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);
 };
 
@@ -238,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. */
@@ -251,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.
@@ -257,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 */
@@ -267,5 +279,5 @@
 G_MODULE_EXPORT struct buddy *imcb_find_buddy( struct im_connection *ic, char *handle );
 G_MODULE_EXPORT void imcb_rename_buddy( struct im_connection *ic, const char *handle, const char *realname );
-G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, char *handle, char *nick );
+G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, const char *handle, const char *nick );
 
 /* Buddy activity */
@@ -302,5 +314,5 @@
 
 /* Actions, or whatever. */
-int imc_set_away( struct im_connection *ic, char *away );
+int imc_away_send_update( struct im_connection *ic );
 int imc_buddy_msg( struct im_connection *ic, char *handle, char *msg, int flags );
 int imc_chat_msg( struct groupchat *c, char *msg, int flags );
Index: protocols/oscar/aim.h
===================================================================
--- protocols/oscar/aim.h	(revision c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/oscar/aim.h	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -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 c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/oscar/oscar.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -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) ||
@@ -379,4 +380,6 @@
 		s->flags |= ACC_SET_OFFLINE_ONLY;
 	}
+	
+	acc->flags |= ACC_FLAG_AWAY_MESSAGE;
 }
 
@@ -520,4 +523,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 +576,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 +752,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;
@@ -1925,4 +1954,6 @@
 static void oscar_set_away_aim(struct im_connection *ic, struct oscar_data *od, const char *state, const char *message)
 {
+	if (state == NULL)
+		state = "";
 
 	if (!g_strcasecmp(state, _("Visible"))) {
@@ -1932,5 +1963,7 @@
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
 		return;
-	} /* else... */
+	} else if (message == NULL) {
+		message = state;
+	}
 
 	if (od->rights.maxawaymsglen == 0)
@@ -1939,6 +1972,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,53 +1992,51 @@
 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")) {
+	}
+
+	if (state == NULL) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
 	} 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);
-	} else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
+		ic->away = g_strdup(msg);
+	} else {
 	 	if (no_message) {
 			aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
 		} 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 +2050,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);
@@ -2252,18 +2282,19 @@
 {
 	struct oscar_data *od = ic->proto_data;
-	GList *m = NULL;
-
-	if (!od->icq)
-		return g_list_append(m, GAIM_AWAY_CUSTOM);
-
-	m = g_list_append(m, "Online");
-	m = g_list_append(m, "Away");
-	m = g_list_append(m, "Do Not Disturb");
-	m = g_list_append(m, "Not Available");
-	m = g_list_append(m, "Occupied");
-	m = g_list_append(m, "Free For Chat");
-	m = g_list_append(m, "Invisible");
-
-	return m;
+
+	if (od->icq) {
+		static GList *m = NULL;
+		m = g_list_append(m, "Away");
+		m = g_list_append(m, "Do Not Disturb");
+		m = g_list_append(m, "Not Available");
+		m = g_list_append(m, "Occupied");
+		m = g_list_append(m, "Free For Chat");
+		m = g_list_append(m, "Invisible");
+		return m;
+	} else {
+		static GList *m = NULL;
+		m = g_list_append(m, "Away");
+		return m;
+	}
 }
 
@@ -2581,5 +2612,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 c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/yahoo/libyahoo2.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -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);
 }
@@ -836,53 +853,4 @@
 
 	return strcmp(subject->id, object->id);
-}
-
-static YList * bud_str2list(char *rawlist)
-{
-	YList * l = NULL;
-
-	char **lines;
-	char **split;
-	char **buddies;
-	char **tmp, **bud;
-
-	lines = y_strsplit(rawlist, "\n", -1);
-	for (tmp = lines; *tmp; tmp++) {
-		struct yahoo_buddy *newbud;
-
-		split = y_strsplit(*tmp, ":", 2);
-		if (!split)
-			continue;
-		if (!split[0] || !split[1]) {
-			y_strfreev(split);
-			continue;
-		}
-		buddies = y_strsplit(split[1], ",", -1);
-
-		for (bud = buddies; bud && *bud; bud++) {
-			newbud = y_new0(struct yahoo_buddy, 1);
-			newbud->id = strdup(*bud);
-			newbud->group = strdup(split[0]);
-
-			if(y_list_find_custom(l, newbud, is_same_bud)) {
-				FREE(newbud->id);
-				FREE(newbud->group);
-				FREE(newbud);
-				continue;
-			}
-
-			newbud->real_name = NULL;
-
-			l = y_list_append(l, newbud);
-
-			NOTICE(("Added buddy %s to group %s", newbud->id, newbud->group));
-		}
-
-		y_strfreev(buddies);
-		y_strfreev(split);
-	}
-	y_strfreev(lines);
-
-	return l;
 }
 
@@ -1343,38 +1311,25 @@
 }
 
-
-static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+static void yahoo_process_status(struct yahoo_input_data *yid,
+	struct yahoo_packet *pkt)
 {
 	YList *l;
 	struct yahoo_data *yd = yid->yd;
 
-	struct user
-	{
-		char *name;	/* 7	name */
-		int   state;	/* 10	state */
-		int   flags;	/* 13	flags, bit 0 = pager, bit 1 = chat, bit 2 = game */
-		int   mobile;	/* 60	mobile */
-		char *msg;	/* 19	custom status message */
-		int   away;	/* 47	away (or invisible)*/
-		int   buddy_session;	/* 11	state */
-		int   f17;	/* 17	in chat? then what about flags? */
-		int   idle;	/* 137	seconds idle */
-		int   f138;	/* 138	state */
-		char *f184;	/* 184	state */
-		int   f192;	/* 192	state */
-		int   f10001;	/* 10001	state */
-		int   f10002;	/* 10002	state */
-		int   f198;	/* 198	state */
-		char *f197;	/* 197	state */
-		char *f205;	/* 205	state */
-		int   f213;	/* 213	state */
-	} *u;
+	struct yahoo_process_status_entry *u;
 
 	YList *users = 0;
-	
+
 	if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
-		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_DUPL, NULL);
-		return;
-	}
+		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
+			YAHOO_LOGIN_DUPL, NULL);
+		return;
+	}
+
+	/* Status updates may be spread accross multiple packets and not
+	   even on buddy boundaries, so keeping some state is important.
+	   So, continue where we left off, and only add a user entry to
+	   the list once it's complete (301-315 End buddy). */
+	u = yd->half_user;
 
 	for (l = pkt->hash; l; l = l->next) {
@@ -1382,93 +1337,122 @@
 
 		switch (pair->key) {
-		case 0: /* we won't actually do anything with this */
+		case 300:	/* Begin buddy */
+			if (!strcmp(pair->value, "315") && !u) {
+				u = yd->half_user = y_new0(struct yahoo_process_status_entry, 1);
+			}
+			break;
+		case 301:	/* End buddy */
+			if (!strcmp(pair->value, "315") && u) {
+				users = y_list_prepend(users, u);
+				u = yd->half_user = NULL;
+			}
+			break;
+		case 0:	/* we won't actually do anything with this */
 			NOTICE(("key %d:%s", pair->key, pair->value));
 			break;
-		case 1: /* we don't get the full buddy list here. */
+		case 1:	/* we don't get the full buddy list here. */
 			if (!yd->logged_in) {
-				yd->logged_in = TRUE;
-				if(yd->current_status < 0)
+				yd->logged_in = 1;
+				if (yd->current_status < 0)
 					yd->current_status = yd->initial_status;
-				YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
+				YAHOO_CALLBACK(ext_yahoo_login_response) (yd->
+					client_id, YAHOO_LOGIN_OK, NULL);
 			}
 			break;
-		case 8: /* how many online buddies we have */
+		case 8:	/* how many online buddies we have */
 			NOTICE(("key %d:%s", pair->key, pair->value));
 			break;
-		case 7: /* the current buddy */
-			u = y_new0(struct user, 1);
+		case 7:	/* the current buddy */
+			if (!u) {
+				/* This will only happen in case of a single level message */
+				u = y_new0(struct yahoo_process_status_entry, 1);
+				users = y_list_prepend(users, u);
+			}
 			u->name = pair->value;
-			users = y_list_prepend(users, u);
-			break;
-		case 10: /* state */
-			((struct user*)users->data)->state = strtol(pair->value, NULL, 10);
-			break;
-		case 19: /* custom status message */
-			((struct user*)users->data)->msg = pair->value;
-			break;
-		case 47: /* is it an away message or not */
-			((struct user*)users->data)->away = atoi(pair->value);
-			break;
-		case 137: /* seconds idle */
-			((struct user*)users->data)->idle = atoi(pair->value);
-			break;
-		case 11: /* this is the buddy's session id */
-			((struct user*)users->data)->buddy_session = atoi(pair->value);
-			break;
-		case 17: /* in chat? */
-			((struct user*)users->data)->f17 = atoi(pair->value);
-			break;
-		case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
-			((struct user*)users->data)->flags = atoi(pair->value);
-			break;
-		case 60: /* SMS -> 1 MOBILE USER */
+			break;
+		case 10:	/* state */
+			u->state = strtol(pair->value, NULL, 10);
+			break;
+		case 19:	/* custom status message */
+			u->msg = pair->value;
+			break;
+		case 47:	/* is it an away message or not. Not applicable for YMSG16 anymore */
+			u->away = atoi(pair->value);
+			break;
+		case 137:	/* seconds idle */
+			u->idle = atoi(pair->value);
+			break;
+		case 11:	/* this is the buddy's session id */
+			u->buddy_session = atoi(pair->value);
+			break;
+		case 17:	/* in chat? */
+			u->f17 = atoi(pair->value);
+			break;
+		case 13:	/* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */
+			u->flags = atoi(pair->value);
+			break;
+		case 60:	/* SMS -> 1 MOBILE USER */
 			/* sometimes going offline makes this 2, but invisible never sends it */
-			((struct user*)users->data)->mobile = atoi(pair->value);
+			u->mobile = atoi(pair->value);
 			break;
 		case 138:
-			((struct user*)users->data)->f138 = atoi(pair->value);
+			u->f138 = atoi(pair->value);
 			break;
 		case 184:
-			((struct user*)users->data)->f184 = pair->value;
+			u->f184 = pair->value;
 			break;
 		case 192:
-			((struct user*)users->data)->f192 = atoi(pair->value);
+			u->f192 = atoi(pair->value);
 			break;
 		case 10001:
-			((struct user*)users->data)->f10001 = atoi(pair->value);
+			u->f10001 = atoi(pair->value);
 			break;
 		case 10002:
-			((struct user*)users->data)->f10002 = atoi(pair->value);
+			u->f10002 = atoi(pair->value);
 			break;
 		case 198:
-			((struct user*)users->data)->f198 = atoi(pair->value);
+			u->f198 = atoi(pair->value);
 			break;
 		case 197:
-			((struct user*)users->data)->f197 = pair->value;
+			u->f197 = pair->value;
 			break;
 		case 205:
-			((struct user*)users->data)->f205 = pair->value;
+			u->f205 = pair->value;
 			break;
 		case 213:
-			((struct user*)users->data)->f213 = atoi(pair->value);
-			break;
-		case 16: /* Custom error message */
-			YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, pair->value, 0, E_CUSTOM);
+			u->f213 = atoi(pair->value);
+			break;
+		case 16:	/* Custom error message */
+			YAHOO_CALLBACK(ext_yahoo_error) (yd->client_id,
+				pair->value, 0, E_CUSTOM);
 			break;
 		default:
-			WARNING(("unknown status key %d:%s", pair->key, pair->value));
-			break;
-		}
-	}
-	
+			WARNING(("unknown status key %d:%s", pair->key,
+					pair->value));
+			break;
+		}
+	}
+
 	while (users) {
 		YList *t = users;
-		struct user *u = users->data;
+		struct yahoo_process_status_entry *u = users->data;
 
 		if (u->name != NULL) {
-			if (pkt->service == YAHOO_SERVICE_LOGOFF || u->flags == 0) {
-				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
+			if (pkt->service ==
+				YAHOO_SERVICE_LOGOFF
+				/*|| u->flags == 0 No flags for YMSG16 */ ) {
+				YAHOO_CALLBACK(ext_yahoo_status_changed) (yd->
+					client_id, u->name,
+					YAHOO_STATUS_OFFLINE, NULL, 1, 0, 0);
 			} else {
-				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, u->name, u->state, u->msg, u->away, u->idle, u->mobile);
+				/* 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);
 			}
 		}
@@ -1480,58 +1464,100 @@
 }
 
-static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+static void yahoo_process_buddy_list(struct yahoo_input_data *yid,
+	struct yahoo_packet *pkt)
 {
 	struct yahoo_data *yd = yid->yd;
 	YList *l;
-
-	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);
-	}
-
+	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 87: /* buddies */
-			if(!yd->rawbuddylist)
-				yd->rawbuddylist = strdup(pair->value);
-			else {
-				yd->rawbuddylist = y_string_append(yd->rawbuddylist, pair->value);
+		switch (pair->key) {
+		case 300:
+		case 301:
+		case 302:
+			break;	/* Separators. Our logic does not need them */
+		case 303:
+			if (318 == atoi(pair->value))
+				last_packet = 1;
+			break;
+		case 65:
+			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 if (yd->buddies) {
+				struct yahoo_buddy *lastbud =
+					(struct yahoo_buddy *)y_list_nth(yd->
+					buddies,
+					y_list_length(yd->buddies) - 1)->data;
+				newbud->group = strdup(lastbud->group);
+			} else
+				newbud->group = strdup("Buddies");
+
+			yd->buddies = y_list_append(yd->buddies, newbud);
+
+			break;
+		}
+	}
+
+	/* we could be getting multiple packets here */
+	if (pkt->hash && !last_packet)
+		return;
+
+	YAHOO_CALLBACK(ext_yahoo_got_buddies) (yd->client_id, yd->buddies);
+
+	/* Logged in */
+	if (!yd->logged_in) {
+		yd->logged_in = 1;
+		if (yd->current_status < 0)
+			yd->current_status = yd->initial_status;
+		YAHOO_CALLBACK(ext_yahoo_login_response) (yd->client_id,
+			YAHOO_LOGIN_OK, NULL);
+
+		/*
+		yahoo_set_away(yd->client_id, yd->initial_status, NULL,
+			(yd->initial_status == YAHOO_STATUS_AVAILABLE) ? 0 : 1);
+
+		yahoo_get_yab(yd->client_id);
+		*/
+	}
+
+}
+
+static void yahoo_process_list(struct yahoo_input_data *yid,
+	struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	YList *l;
+
+	/* 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 89:	/* identities */
+			{
+				char **identities =
+					y_strsplit(pair->value, ",", -1);
+				int i;
+				for (i = 0; identities[i]; i++)
+					yd->identities =
+						y_list_append(yd->identities,
+						strdup(identities[i]));
+				y_strfreev(identities);
 			}
-			break;
-
-		case 88: /* ignore list */
-			if(!yd->ignorelist)
-				yd->ignorelist = strdup("Ignore:");
-			yd->ignorelist = y_string_append(yd->ignorelist, pair->value);
-			break;
-
-		case 89: /* identities */
-			{
-			char **identities = y_strsplit(pair->value, ",", -1);
-			int i;
-			for(i=0; identities[i]; i++)
-				yd->identities = y_list_append(yd->identities, 
-						strdup(identities[i]));
-			y_strfreev(identities);
-			}
-			YAHOO_CALLBACK(ext_yahoo_got_identities)(yd->client_id, yd->identities);
-			break;
-		case 59: /* cookies */
-			if(yd->ignorelist) {
-				yd->ignore = bud_str2list(yd->ignorelist);
-				FREE(yd->ignorelist);
-				YAHOO_CALLBACK(ext_yahoo_got_ignore)(yd->client_id, yd->ignore);
-			}
-			if(yd->rawbuddylist) {
-				yd->buddies = bud_str2list(yd->rawbuddylist);
-				FREE(yd->rawbuddylist);
-				YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
-			}
-
-			if(pair->value[0]=='Y') {
+			YAHOO_CALLBACK(ext_yahoo_got_identities) (yd->client_id,
+				yd->identities);
+			break;
+		case 59:	/* cookies */
+			if (pair->value[0] == 'Y') {
 				FREE(yd->cookie_y);
 				FREE(yd->login_cookie);
@@ -1540,26 +1566,26 @@
 				yd->login_cookie = getlcookie(yd->cookie_y);
 
-			} else if(pair->value[0]=='T') {
+			} else if (pair->value[0] == 'T') {
 				FREE(yd->cookie_t);
 				yd->cookie_t = getcookie(pair->value);
 
-			} else if(pair->value[0]=='C') {
+			} else if (pair->value[0] == 'C') {
 				FREE(yd->cookie_c);
 				yd->cookie_c = getcookie(pair->value);
-			} 
-
-			if(yd->cookie_y && yd->cookie_t && yd->cookie_c)
-				YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id);
-
-			break;
-		case 3: /* my id */
-		case 90: /* 1 */
-		case 100: /* 0 */
-		case 101: /* NULL */
-		case 102: /* NULL */
-		case 93: /* 86400/1440 */
-			break;
-		}
-	}
+			}
+
+			break;
+		case 3:	/* my id */
+		case 90:	/* 1 */
+		case 100:	/* 0 */
+		case 101:	/* NULL */
+		case 102:	/* NULL */
+		case 93:	/* 86400/1440 */
+			break;
+		}
+	}
+
+	if (yd->cookie_y && yd->cookie_t)	/* We don't get cookie_c anymore */
+		YAHOO_CALLBACK(ext_yahoo_got_cookies) (yd->client_id);
 }
 
@@ -2226,4 +2252,202 @@
 }
 
+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;
+	struct yahoo_data *yd;
+	int st;
+	
+	if (y_list_find(inputs, had->yid) == NULL)
+		return;
+	
+	yid = had->yid;
+	yd = yid->yd;
+	
+	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;
+	struct yahoo_data *yd;
+	struct yahoo_packet *pack;
+	char *crumb = NULL;
+	int st;
+	
+	if (y_list_find(inputs, had->yid) == NULL)
+		return;
+	
+	yid = had->yid;
+	yd = yid->yd;
+	
+	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 +2477,7 @@
 		case 1:
 			yahoo_process_auth_0x0b(yid, seed, sn);
+			break;
+		case 2:
+			yahoo_process_auth_0x10(yid, seed, sn);
 			break;
 		default:
@@ -2408,5 +2635,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 +2642,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 +2875,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 +2897,5 @@
 {
 	DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
+	yahoo_dump_unhandled(pkt);
 	switch (pkt->service)
 	{
@@ -2661,4 +2909,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 +2924,5 @@
 		yahoo_process_mail(yid, pkt);
 		break;
+	case YAHOO_SERVICE_REJECTCONTACT:
 	case YAHOO_SERVICE_NEWCONTACT:
 		yahoo_process_contact(yid, pkt);
@@ -2714,4 +2965,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 +2996,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 +3009,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 +3794,5 @@
 	yahoo_process_webcam_connection,
 	yahoo_process_chatcat_connection,
-	yahoo_process_search_connection
+	yahoo_process_search_connection,
 };
 
@@ -3557,5 +3812,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 +4015,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 +4030,5 @@
 	struct yahoo_data *yd;
 	struct yahoo_packet *pkt = NULL;
-	int service;
+	int old_status;
 	char s[4];
 
@@ -3782,37 +4037,31 @@
 
 	yd = yid->yd;
-
-	if (msg) {
-		yd->current_status = YAHOO_STATUS_CUSTOM;
-	} else {
-		yd->current_status = state;
-	}
-
-	if (yd->current_status == YAHOO_STATUS_AVAILABLE)
-		service = YAHOO_SERVICE_ISBACK;
-	else
-		service = YAHOO_SERVICE_ISAWAY;
-	 
-	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");
-		}
-		
-		
-		
-	}
-
+	old_status = yd->current_status;
+	yd->current_status = state;
+
+	/* 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);
+	yahoo_packet_hash(pkt, 19, msg && state == YAHOO_STATUS_CUSTOM ? msg : "");
+	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 +4078,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 +4314,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 +4353,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 c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/yahoo/yahoo.c	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -130,4 +130,6 @@
 {
 	set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
+	
+	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE;
 }
 
@@ -198,25 +200,9 @@
 	struct byahoo_data *yd = (struct byahoo_data *) ic->proto_data;
 	
-	ic->away = NULL;
-	
-	if( state && msg && g_strcasecmp( state, msg ) != 0 )
-	{
-		yd->current_status = YAHOO_STATUS_CUSTOM;
-		ic->away = "";
-	}
-	else if( state )
-	{
-		/* Set msg to NULL since (if it isn't NULL already) it's equal
-		   to state. msg must be empty if we want to use an existing
-		   away state. */
-		msg = NULL;
-		
-		ic->away = "";
-		if( g_strcasecmp( state, "Available" ) == 0 )
-		{
-			yd->current_status = YAHOO_STATUS_AVAILABLE;
-			ic->away = NULL;
-		}
-		else if( g_strcasecmp( state, "Be Right Back" ) == 0 )
+	if( state && msg == NULL )
+	{
+		/* Use these states only if msg doesn't contain additional
+		   info since away messages are only supported with CUSTOM. */
+		if( g_strcasecmp( state, "Be Right Back" ) == 0 )
 			yd->current_status = YAHOO_STATUS_BRB;
 		else if( g_strcasecmp( state, "Busy" ) == 0 )
@@ -238,33 +224,32 @@
 		else if( g_strcasecmp( state, "Invisible" ) == 0 )
 			yd->current_status = YAHOO_STATUS_INVISIBLE;
-		else if( g_strcasecmp( state, GAIM_AWAY_CUSTOM ) == 0 )
-		{
-			yd->current_status = YAHOO_STATUS_AVAILABLE;
-			
-			ic->away = NULL;
-		}
-	}
+		else
+			yd->current_status = YAHOO_STATUS_CUSTOM;
+	}
+	else if( state )
+		yd->current_status = YAHOO_STATUS_CUSTOM;
 	else
 		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, state ? 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, "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" );
+	}
 	
 	return m;
@@ -345,4 +330,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 +371,7 @@
 	ret->handle_cmp = g_strcasecmp;
 	
+	ret->auth_allow = byahoo_auth_allow;
+	ret->auth_deny = byahoo_auth_deny;
+	
 	register_protocol(ret);
 }
@@ -451,7 +453,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 );
 }
 
@@ -665,7 +665,4 @@
 	
 	imcb_error( ic, "%s", err );
-	
-	if( fatal )
-		imc_logout( ic, TRUE );
 }
 
@@ -793,7 +790,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 );
@@ -911,9 +921,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 c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/yahoo/yahoo2.h	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -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 c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/yahoo/yahoo2_callbacks.h	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -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 c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb)
+++ protocols/yahoo/yahoo2_types.h	(revision fb009891bb60921401ffeb47b33cf35a246cbe34)
@@ -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
@@ -183,4 +196,6 @@
 
 	void  *server_settings;
+	
+	struct yahoo_process_status_entry *half_user;
 };
 
@@ -248,4 +263,25 @@
 };
 
+struct yahoo_process_status_entry {
+	char *name;	/* 7      name */
+	int state;	/* 10     state */
+	int flags;	/* 13     flags, bit 0 = pager, bit 1 = chat, bit 2 = game */
+	int mobile;	/* 60     mobile */
+	char *msg;	/* 19     custom status message */
+	int away;	/* 47     away (or invisible) */
+	int buddy_session; /* 11  state */
+	int f17;	/* 17     in chat? then what about flags? */
+	int idle;	/* 137    seconds idle */
+	int f138;	/* 138    state */
+	char *f184;	/* 184    state */
+	int f192;	/* 192    state */
+	int f10001;	/* 10001  state */
+	int f10002;	/* 10002  state */
+	int f198;	/* 198    state */
+	char *f197;	/* 197    state */
+	char *f205;	/* 205    state */
+	int f213;	/* 213    state */
+};
+
 #ifdef __cplusplus
 }
