Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ bitlbee.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -36,5 +36,5 @@
 
 #define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "1.2.3"
+#define BITLBEE_VERSION "1.2.4"
 #define VERSION BITLBEE_VERSION
 
Index: doc/CHANGES
===================================================================
--- doc/CHANGES	(revision fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ doc/CHANGES	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -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 fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ doc/user-guide/commands.xml	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -175,4 +175,8 @@
 					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>
 
Index: irc_commands.c
===================================================================
--- irc_commands.c	(revision fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ irc_commands.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -415,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/ini.c
===================================================================
--- lib/ini.c	(revision fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ lib/ini.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -43,10 +43,14 @@
 		ini->cur = ini->file;
 		ini->c_section = "";
+		
+		close( fd );
+		
 		return ini;
 	}
-	
-	g_free( ini );
+
 	if( fd >= 0 )
 		close( fd );
+	
+	ini_close( ini );
 
 	return NULL;
Index: protocols/jabber/conference.c
===================================================================
--- protocols/jabber/conference.c	(revision fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/jabber/conference.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -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 fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/jabber/iq.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -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/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/nogaim.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -506,7 +506,6 @@
 }
 
-/* prpl.c */
-
-struct show_got_added_data
+
+struct imcb_ask_cb_data
 {
 	struct im_connection *ic;
@@ -514,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;
 	
@@ -543,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 );
 }
 
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/nogaim.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -228,4 +228,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);
 };
 
@@ -256,4 +260,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.
@@ -262,5 +267,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 */
Index: protocols/oscar/aim.h
===================================================================
--- protocols/oscar/aim.h	(revision fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/oscar/aim.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -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/yahoo/libyahoo2.c
===================================================================
--- protocols/yahoo/libyahoo2.c	(revision fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/yahoo/libyahoo2.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -91,4 +91,5 @@
 
 #include "base64.h"
+#include "http_client.h"
 
 #ifdef USE_STRUCT_CALLBACKS
@@ -171,4 +172,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,
@@ -194,4 +196,5 @@
 	YAHOO_SERVICE_LIST,
 	YAHOO_SERVICE_AUTH = 0x57,
+	YAHOO_SERVICE_AUTHBUDDY = 0x6d,
 	YAHOO_SERVICE_ADDBUDDY = 0x83,
 	YAHOO_SERVICE_REMBUDDY,
@@ -199,4 +202,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,
@@ -204,4 +208,5 @@
 	YAHOO_SERVICE_CHATLEAVE,
 	YAHOO_SERVICE_CHATEXIT = 0x9b,
+	YAHOO_SERVICE_CHATADDINVITE = 0x9d,
 	YAHOO_SERVICE_CHATLOGOUT = 0xa0,
 	YAHOO_SERVICE_CHATPING,
@@ -211,5 +216,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,
 };
 
@@ -735,5 +752,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);
@@ -749,5 +766,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);
 }
@@ -1469,7 +1486,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);
 			}
@@ -1479,4 +1502,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);
 	}
 }
@@ -1551,5 +1632,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);
 
@@ -2228,4 +2309,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)
 {
@@ -2255,4 +2522,7 @@
 		case 1:
 			yahoo_process_auth_0x0b(yid, seed, sn);
+			break;
+		case 2:
+			yahoo_process_auth_0x10(yid, seed, sn);
 			break;
 		default:
@@ -2410,5 +2680,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). */
@@ -2417,4 +2687,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);
 }
 
@@ -2630,5 +2920,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;
@@ -2652,4 +2942,5 @@
 {
 	DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
+	yahoo_dump_unhandled(pkt);
 	switch (pkt->service)
 	{
@@ -2663,4 +2954,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;
@@ -2676,4 +2969,5 @@
 		yahoo_process_mail(yid, pkt);
 		break;
+	case YAHOO_SERVICE_REJECTCONTACT:
 	case YAHOO_SERVICE_NEWCONTACT:
 		yahoo_process_contact(yid, pkt);
@@ -2716,4 +3010,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);
@@ -2744,5 +3041,4 @@
 	case YAHOO_SERVICE_CHATLOGOFF:
 	case YAHOO_SERVICE_CHATMSG:
-	case YAHOO_SERVICE_REJECTCONTACT:
 	case YAHOO_SERVICE_PEERTOPEER:
 		WARNING(("unhandled service 0x%02x", pkt->service));
@@ -2758,4 +3054,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));
@@ -3541,5 +3839,5 @@
 	yahoo_process_webcam_connection,
 	yahoo_process_chatcat_connection,
-	yahoo_process_search_connection
+	yahoo_process_search_connection,
 };
 
@@ -3559,5 +3857,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;
 
@@ -3762,5 +4060,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");
@@ -3777,5 +4075,5 @@
 	struct yahoo_data *yd;
 	struct yahoo_packet *pkt = NULL;
-	int service;
+	int old_status;
 	char s[4];
 
@@ -3785,5 +4083,7 @@
 	yd = yid->yd;
 
-	if (msg) {
+	old_status = yd->current_status;
+
+	if (msg && strncmp(msg,"Invisible",9)) {
 		yd->current_status = YAHOO_STATUS_CUSTOM;
 	} else {
@@ -3791,30 +4091,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);
+	}
 }
 
@@ -3831,5 +4136,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;
@@ -4064,10 +4372,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);
@@ -4091,4 +4411,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 fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/yahoo/yahoo.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -254,18 +254,21 @@
 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;
@@ -346,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 );
 }
 
@@ -374,4 +391,7 @@
 	ret->handle_cmp = g_strcasecmp;
 	
+	ret->auth_allow = byahoo_auth_allow;
+	ret->auth_deny = byahoo_auth_deny;
+	
 	register_protocol(ret);
 }
@@ -453,7 +473,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 );
 }
 
@@ -923,9 +941,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 fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/yahoo/yahoo2.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -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 fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/yahoo/yahoo2_callbacks.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -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 fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ protocols/yahoo/yahoo2_types.h	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -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 fc34fb5d0b717d90edfc7ed78f11166eb23c536e)
+++ root_commands.c	(revision 3f81999c20852f14a5fb27a6ef6c5ea44db61a4d)
@@ -310,5 +310,5 @@
 		int st;
 		
-		if( checkflags && checkflags( irc, s ) == 0 )
+		if( s && checkflags && checkflags( irc, s ) == 0 )
 			return 0;
 		
@@ -1079,4 +1079,6 @@
 	else if( g_strcasecmp( cmd[1], "set" ) == 0 )
 	{
+		MIN_ARGS( 2 );
+		
 		cmd_set_real( irc, cmd + 1, cmd_chat_set_findhead, NULL );
 	}
