Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision 7f34ce254c1a1587e5560456dd11acb29345206d)
+++ protocols/msn/msn.c	(revision ca7de3ac72f0ef10613badecb7eb6cc9a10f996b)
@@ -68,4 +68,5 @@
 	md->ic = ic;
 	md->away_state = msn_away_state_list;
+	md->domaintree = g_tree_new( msn_domaintree_cmp );
 	
 	msn_connections = g_slist_append( msn_connections, ic );
@@ -106,4 +107,7 @@
 		g_free( md->tokens[1] );
 		g_free( md->lock_key );
+		
+		g_tree_destroy( md->domaintree );
+		md->domaintree = NULL;
 		
 		while( md->grpq )
@@ -336,9 +340,13 @@
 static void msn_buddy_data_add( bee_user_t *bu )
 {
+	struct msn_data *md = bu->ic->proto_data;
 	bu->data = g_new0( struct msn_buddy_data, 1 );
+	g_tree_insert( md->domaintree, bu->handle, bu );
 }
 
 static void msn_buddy_data_free( bee_user_t *bu )
 {
+	struct msn_data *md = bu->ic->proto_data;
+	g_tree_remove( md->domaintree, bu->handle );
 	g_free( bu->data );
 }
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision 7f34ce254c1a1587e5560456dd11acb29345206d)
+++ protocols/msn/msn.h	(revision ca7de3ac72f0ef10613badecb7eb6cc9a10f996b)
@@ -89,4 +89,5 @@
 	int groupcount;
 	char **grouplist;
+	GTree *domaintree;
 };
 
@@ -153,5 +154,5 @@
 typedef enum
 {
-	MSN_BUDDY_FL = 1,
+	MSN_BUDDY_FL = 1,   /* Warning: FL,AL,BL *must* be 1,2,4. */
 	MSN_BUDDY_AL = 2,
 	MSN_BUDDY_BL = 4,
@@ -187,4 +188,5 @@
 gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond );
 void msn_auth_got_passport_token( struct im_connection *ic, char *token );
+void msn_auth_got_contact_list( struct im_connection *ic );
 
 /* msn_util.c */
@@ -201,4 +203,5 @@
 gboolean msn_set_display_name( struct im_connection *ic, const char *rawname );
 char *msn_p11_challenge( char *challenge );
+gint msn_domaintree_cmp( gconstpointer a_, gconstpointer b_ );
 
 /* tables.c */
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision 7f34ce254c1a1587e5560456dd11acb29345206d)
+++ protocols/msn/msn_util.c	(revision ca7de3ac72f0ef10613badecb7eb6cc9a10f996b)
@@ -528,2 +528,14 @@
 	return output;
 }
+
+gint msn_domaintree_cmp( gconstpointer a_, gconstpointer b_ )
+{
+	const char *a = a_, *b = b_;
+	gint ret;
+	
+	if( !( a = strchr( a, '@' ) ) || !( b = strchr( b, '@' ) ) ||
+	    ( ret = strcmp( a, b ) ) == 0 )
+		ret = strcmp( a_, b_ );
+	
+	return ret;
+}
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision 7f34ce254c1a1587e5560456dd11acb29345206d)
+++ protocols/msn/ns.c	(revision ca7de3ac72f0ef10613badecb7eb6cc9a10f996b)
@@ -27,7 +27,7 @@
 #include "nogaim.h"
 #include "msn.h"
-#include "passport.h"
 #include "md5.h"
 #include "soap.h"
+#include "xmltree.h"
 
 static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond );
@@ -36,4 +36,5 @@
 
 static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name );
+static void msn_ns_send_adl( struct im_connection *ic );
 
 gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )
@@ -135,5 +136,5 @@
 		int port;
 		
-		if( num_parts == 6 && strcmp( cmd[2], "NS" ) == 0 )
+		if( num_parts >= 6 && strcmp( cmd[2], "NS" ) == 0 )
 		{
 			b_event_remove( ic->inpa );
@@ -156,5 +157,5 @@
 			md->fd = proxy_connect( server, port, msn_ns_connected, ic );
 		}
-		else if( num_parts == 6 && strcmp( cmd[2], "SB" ) == 0 )
+		else if( num_parts >= 6 && strcmp( cmd[2], "SB" ) == 0 )
 		{
 			struct msn_switchboard *sb;
@@ -261,107 +262,32 @@
 		}
 	}
-	else if( strcmp( cmd[0], "SYN" ) == 0 )
-	{
-		if( num_parts == 5 )
-		{
-			int i, groupcount;
-			
-			groupcount = atoi( cmd[4] );
-			if( groupcount > 0 )
-			{
-				/* valgrind says this is leaking memory, I'm guessing
-				   that this happens during server redirects. */
-				if( md->grouplist )
-				{
-					for( i = 0; i < md->groupcount; i ++ )
-						g_free( md->grouplist[i] );
-					g_free( md->grouplist );
-				}
-				
-				md->groupcount = groupcount;
-				md->grouplist = g_new0( char *, md->groupcount );
-			}
-			
-			md->buddycount = atoi( cmd[3] );
-			if( !*cmd[3] || md->buddycount == 0 )
-				msn_logged_in( ic );
-		}
-		else
-		{
-			/* Hrrm... This SYN reply doesn't really look like something we expected.
-			   Let's assume everything is okay. */
-			
-			msn_logged_in( ic );
-		}
-	}
-	else if( strcmp( cmd[0], "LST" ) == 0 )
-	{
-		int list;
-		
-		if( num_parts != 4 && num_parts != 5 )
-		{
-			imcb_error( ic, "Syntax error" );
-			imc_logout( ic, TRUE );
-			return( 0 );
-		}
-		
-		http_decode( cmd[2] );
-		list = atoi( cmd[3] );
-		
-		if( list & 1 ) /* FL */
-		{
-			char *group = NULL;
-			int num;
-			
-			if( cmd[4] != NULL && sscanf( cmd[4], "%d", &num ) == 1 && num < md->groupcount )
-				group = md->grouplist[num];
-			
-			imcb_add_buddy( ic, cmd[1], group );
-			imcb_rename_buddy( ic, cmd[1], cmd[2] );
-		}
-		if( list & 2 ) /* AL */
-		{
-			ic->permit = g_slist_append( ic->permit, g_strdup( cmd[1] ) );
-		}
-		if( list & 4 ) /* BL */
-		{
-			ic->deny = g_slist_append( ic->deny, g_strdup( cmd[1] ) );
-		}
-		if( list & 8 ) /* RL */
-		{
-			if( ( list & 6 ) == 0 )
-				msn_buddy_ask( ic, cmd[1], cmd[2] );
-		}
-		
-		if( --md->buddycount == 0 )
-		{
-			if( ic->flags & OPT_LOGGED_IN )
-			{
-				imcb_log( ic, "Successfully transferred to different server" );
-				g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 );
-				return( msn_write( ic, buf, strlen( buf ) ) );
-			}
-			else
-			{
-				msn_logged_in( ic );
-			}
-		}
-	}
-	else if( strcmp( cmd[0], "LSG" ) == 0 )
-	{
-		int num;
-		
-		if( num_parts != 4 )
-		{
-			imcb_error( ic, "Syntax error" );
-			imc_logout( ic, TRUE );
-			return( 0 );
-		}
-		
-		http_decode( cmd[2] );
-		num = atoi( cmd[1] );
-		
-		if( num < md->groupcount )
-			md->grouplist[num] = g_strdup( cmd[2] );
+	else if( strcmp( cmd[0], "BLP" ) == 0 )
+	{
+		msn_ns_send_adl( ic );
+	}
+	else if( strcmp( cmd[0], "ADL" ) == 0 )
+	{
+		if( num_parts >= 3 && strcmp( cmd[2], "OK" ) == 0 )
+		{
+			char buf[1024];
+			char *fn_raw = set_getstr( &ic->acc->set, "display_name" );
+			char *fn;
+			
+			if( fn_raw == NULL )
+				fn_raw = ic->acc->user;
+			fn = g_malloc( strlen( fn_raw ) * 3 + 1 );
+			strcpy( fn, fn_raw );
+			http_encode( fn );
+			
+			g_snprintf( buf, sizeof( buf ), "PRP %d MFN %s\r\n",
+			            ++md->trId, fn );
+			g_free( fn );
+			
+			msn_write( ic, buf, strlen( buf ) );
+		}
+	}
+	else if( strcmp( cmd[0], "PRP" ) == 0 )
+	{
+		imcb_connected( ic );
 	}
 	else if( strcmp( cmd[0], "CHL" ) == 0 )
@@ -393,13 +319,13 @@
 		const struct msn_away_state *st;
 		
-		if( num_parts != 6 )
-		{
-			imcb_error( ic, "Syntax error" );
-			imc_logout( ic, TRUE );
-			return( 0 );
-		}
-		
-		http_decode( cmd[4] );
-		imcb_rename_buddy( ic, cmd[3], cmd[4] );
+		if( num_parts < 6 )
+		{
+			imcb_error( ic, "Syntax error" );
+			imc_logout( ic, TRUE );
+			return( 0 );
+		}
+		
+		http_decode( cmd[5] );
+		imcb_rename_buddy( ic, cmd[3], cmd[5] );
 		
 		st = msn_away_state_by_code( cmd[2] );
@@ -787,4 +713,78 @@
 }
 
+void msn_auth_got_contact_list( struct im_connection *ic )
+{
+	char buf[64];
+	struct msn_data *md;
+	
+	/* Dead connection? */
+	if( g_slist_find( msn_connections, ic ) == NULL )
+		return;
+	
+	md = ic->proto_data;
+	
+	
+	g_snprintf( buf, sizeof( buf ), "BLP %d %s\r\n", ++md->trId, "BL" );
+	msn_write( ic, buf, strlen( buf ) );
+}
+
+static gboolean msn_ns_send_adl_1( gpointer key, gpointer value, gpointer data )
+{
+	struct xt_node *adl = data, *d, *c;
+	struct bee_user *bu = value;
+	struct msn_buddy_data *bd = bu->data;
+	char handle[strlen(bu->handle)];
+	char *domain;
+	char l[4];
+	
+	strcpy( handle, bu->handle );
+	if( ( domain = strchr( handle, '@' ) ) == NULL ) /* WTF */
+		return FALSE; 
+	*domain = '\0';
+	domain ++;
+	
+	if( ( d = adl->children ) == NULL ||
+	    g_strcasecmp( xt_find_attr( d, "n" ), domain ) != 0 )
+	{
+		d = xt_new_node( "d", NULL, NULL );
+		xt_add_attr( d, "n", domain );
+		xt_insert_child( adl, d );
+	}
+	
+	g_snprintf( l, sizeof( l ), "%d", bd->flags & 7 );
+	c = xt_new_node( "c", NULL, NULL );
+	xt_add_attr( c, "n", handle );
+	xt_add_attr( c, "l", l );
+	xt_add_attr( c, "t", "1" ); /* 1 means normal, 4 means mobile? */
+	xt_insert_child( d, c );
+	
+	return FALSE;
+}
+
+static void msn_ns_send_adl( struct im_connection *ic )
+{
+	struct xt_node *adl;
+	struct msn_data *md;
+	char *adls, buf[64];
+	
+	/* Dead connection? */
+	if( g_slist_find( msn_connections, ic ) == NULL )
+		return;
+	
+	md = ic->proto_data;
+	
+	adl = xt_new_node( "ml", NULL, NULL );
+	xt_add_attr( adl, "l", "1" );
+	g_tree_foreach( md->domaintree, msn_ns_send_adl_1, adl );
+	adls = xt_to_string( adl );
+	
+	g_snprintf( buf, sizeof( buf ), "ADL %d %zd\r\n", ++md->trId, strlen( adls ) );
+	if( msn_write( ic, buf, strlen( buf ) ) )
+		msn_write( ic, adls, strlen( adls ) );
+	
+	g_free( adls );
+	xt_free_node( adl );
+}
+
 static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name )
 {
Index: protocols/msn/soap.c
===================================================================
--- protocols/msn/soap.c	(revision 7f34ce254c1a1587e5560456dd11acb29345206d)
+++ protocols/msn/soap.c	(revision ca7de3ac72f0ef10613badecb7eb6cc9a10f996b)
@@ -578,4 +578,5 @@
 static int msn_soap_addressbook_handle_response( struct msn_soap_req_data *soap_req )
 {
+	msn_auth_got_contact_list( soap_req->ic );
 	return MSN_SOAP_OK;
 }
