Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision 70ac4771ebf3358d7bec9bd6fe37cde4c23223bc)
+++ protocols/msn/ns.c	(revision 584867592546f43f857645e02169d135f0df25e8)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -27,6 +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 );
@@ -34,6 +35,5 @@
 static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
 
-static void msn_auth_got_passport_token( struct msn_auth_data *mad );
-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 )
@@ -73,5 +73,5 @@
 	md->handler->rxq = g_new0( char, 1 );
 	
-	g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId );
+	g_snprintf( s, sizeof( s ), "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER );
 	if( msn_write( ic, s, strlen( s ) ) )
 	{
@@ -113,5 +113,5 @@
 	if( strcmp( cmd[0], "VER" ) == 0 )
 	{
-		if( cmd[2] && strncmp( cmd[2], "MSNP8", 5 ) != 0 )
+		if( cmd[2] && strncmp( cmd[2], MSNP_VER, 5 ) != 0 )
 		{
 			imcb_error( ic, "Unsupported protocol" );
@@ -127,5 +127,5 @@
 	{
 		/* We don't give a damn about the information we just received */
-		g_snprintf( buf, sizeof( buf ), "USR %d TWN I %s\r\n", ++md->trId, ic->acc->user );
+		g_snprintf( buf, sizeof( buf ), "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user );
 		return( msn_write( ic, buf, strlen( buf ) ) );
 	}
@@ -135,5 +135,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 +156,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;
@@ -220,25 +220,13 @@
 	else if( strcmp( cmd[0], "USR" ) == 0 )
 	{
-		if( num_parts == 5 && strcmp( cmd[2], "TWN" ) == 0 && strcmp( cmd[3], "S" ) == 0 )
-		{
-			/* Time for some Passport black magic... */
-			if( !passport_get_token( msn_auth_got_passport_token, ic, ic->acc->user, ic->acc->pass, cmd[4] ) )
-			{
-				imcb_error( ic, "Error while contacting Passport server" );
-				imc_logout( ic, TRUE );
-				return( 0 );
-			}
-		}
-		else if( num_parts >= 7 && strcmp( cmd[2], "OK" ) == 0 )
-		{
-			if( num_parts == 7 )
-				msn_ns_got_display_name( ic, cmd[4] );
-			else
-				imcb_log( ic, "Warning: Friendly name in server response was corrupted" );
-			
+		if( num_parts >= 6 && strcmp( cmd[2], "SSO" ) == 0 &&
+		    strcmp( cmd[3], "S" ) == 0 )
+		{
+			msn_soap_passport_sso_request( ic, cmd[4], cmd[5] );
+		}
+		else if( strcmp( cmd[2], "OK" ) == 0 )
+		{
 			imcb_log( ic, "Authenticated, getting buddy list" );
-			
-			g_snprintf( buf, sizeof( buf ), "SYN %d 0\r\n", ++md->trId );
-			return( msn_write( ic, buf, strlen( buf ) ) );
+			msn_soap_memlist_request( ic );
 		}
 		else
@@ -251,5 +239,5 @@
 	else if( strcmp( cmd[0], "MSG" ) == 0 )
 	{
-		if( num_parts != 4 )
+		if( num_parts < 4 )
 		{
 			imcb_error( ic, "Syntax error" );
@@ -267,129 +255,49 @@
 		}
 	}
-	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 )
 	{
-		md5_state_t state;
-		md5_byte_t digest[16];
-		int i;
-		
-		if( num_parts != 3 )
-		{
-			imcb_error( ic, "Syntax error" );
-			imc_logout( ic, TRUE );
-			return( 0 );
-		}
-		
-		md5_init( &state );
-		md5_append( &state, (const md5_byte_t *) cmd[2], strlen( cmd[2] ) );
-		md5_append( &state, (const md5_byte_t *) QRY_CODE, strlen( QRY_CODE ) );
-		md5_finish( &state, digest );
-		
-		g_snprintf( buf, sizeof( buf ), "QRY %d %s %d\r\n", ++md->trId, QRY_NAME, 32 );
-		for( i = 0; i < 16; i ++ )
-			g_snprintf( buf + strlen( buf ), 3, "%02x", digest[i] );
+		char *resp;
+		
+		if( num_parts < 3 )
+		{
+			imcb_error( ic, "Syntax error" );
+			imc_logout( ic, TRUE );
+			return( 0 );
+		}
+		
+		resp = msn_p11_challenge( cmd[2] );
+		g_snprintf( buf, sizeof( buf ), "QRY %d %s %zd\r\n%s",
+		            ++md->trId, MSNP11_PROD_ID,
+		            strlen( resp ), resp );
+		g_free( resp );
 		
 		return( msn_write( ic, buf, strlen( buf ) ) );
@@ -399,13 +307,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] );
@@ -433,13 +341,13 @@
 		const struct msn_away_state *st;
 		
-		if( num_parts != 5 )
-		{
-			imcb_error( ic, "Syntax error" );
-			imc_logout( ic, TRUE );
-			return( 0 );
-		}
-		
-		http_decode( cmd[3] );
-		imcb_rename_buddy( ic, cmd[2], cmd[3] );
+		if( num_parts < 5 )
+		{
+			imcb_error( ic, "Syntax error" );
+			imc_logout( ic, TRUE );
+			return( 0 );
+		}
+		
+		http_decode( cmd[4] );
+		imcb_rename_buddy( ic, cmd[2], cmd[4] );
 		
 		st = msn_away_state_by_code( cmd[1] );
@@ -462,5 +370,5 @@
 		int session, port;
 		
-		if( num_parts != 7 )
+		if( num_parts < 7 )
 		{
 			imcb_error( ic, "Syntax error" );
@@ -506,5 +414,5 @@
 	else if( strcmp( cmd[0], "ADD" ) == 0 )
 	{
-		if( num_parts == 6 && strcmp( cmd[2], "RL" ) == 0 )
+		if( num_parts >= 6 && strcmp( cmd[2], "RL" ) == 0 )
 		{
 			GSList *l;
@@ -565,38 +473,4 @@
 		return( 0 );
 	}
-#if 0
-	/* Discard this one completely for now since I don't care about the ack
-	   and since MSN servers can apparently screw up the formatting. */
-	else if( strcmp( cmd[0], "REA" ) == 0 )
-	{
-		if( num_parts != 5 )
-		{
-			imcb_error( ic, "Syntax error" );
-			imc_logout( ic, TRUE );
-			return( 0 );
-		}
-		
-		if( g_strcasecmp( cmd[3], ic->acc->user ) == 0 )
-		{
-			set_t *s;
-			
-			http_decode( cmd[4] );
-			strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) );
-			ic->displayname[sizeof(ic->displayname)-1] = 0;
-			
-			if( ( s = set_find( &ic->acc->set, "display_name" ) ) )
-			{
-				g_free( s->value );
-				s->value = g_strdup( cmd[4] );
-			}
-		}
-		else
-		{
-			/* This is not supposed to happen, but let's handle it anyway... */
-			http_decode( cmd[4] );
-			imcb_rename_buddy( ic, cmd[3], cmd[4] );
-		}
-	}
-#endif
 	else if( strcmp( cmd[0], "IPG" ) == 0 )
 	{
@@ -655,4 +529,23 @@
 			}
 		}
+	}
+	else if( strcmp( cmd[0], "GCF" ) == 0 )
+	{
+		/* Coming up is cmd[2] bytes of stuff we're supposed to
+		   censore. Meh. */
+		md->handler->msglen = atoi( cmd[2] );
+	}
+	else if( strcmp( cmd[0], "UBX" ) == 0 )
+	{
+		/* Status message. Parser coming soon. */
+		if( num_parts >= 4 )
+			md->handler->msglen = atoi( cmd[3] );
+	}
+	else if( strcmp( cmd[0], "NOT" ) == 0 )
+	{
+		/* Some kind of notification, not sure if it still exists
+		   but we have to skip the payload or stuff breaks. */
+		if( num_parts >= 3 )
+			md->handler->msglen = atoi( cmd[2] );
 	}
 	else if( isdigit( cmd[0][0] ) )
@@ -765,11 +658,23 @@
 		}
 	}
+	else if( strcmp( cmd[0], "UBX" ) == 0 )
+	{
+		struct xt_node *psm;
+		char *psm_text = NULL;
+		
+		psm = xt_from_string( msg );
+		if( psm && strcmp( psm->name, "Data" ) == 0 &&
+		    ( psm = xt_find_node( psm->children, "PSM" ) ) )
+			psm_text = psm->text;
+		
+		imcb_buddy_status_msg( ic, cmd[1], psm_text );
+		xt_free_node( psm );
+	}
 	
 	return( 1 );
 }
 
-static void msn_auth_got_passport_token( struct msn_auth_data *mad )
+void msn_auth_got_passport_token( struct im_connection *ic, char *token )
 {
-	struct im_connection *ic = mad->data;
 	struct msn_data *md;
 	
@@ -779,60 +684,84 @@
 	
 	md = ic->proto_data;
-	if( mad->token )
+	
 	{
 		char buf[1024];
 		
-		g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token );
+		g_snprintf( buf, sizeof( buf ), "USR %d SSO S %s %s\r\n", ++md->trId, md->tokens[0], token );
 		msn_write( ic, buf, strlen( buf ) );
 	}
-	else
-	{
-		imcb_error( ic, "Error during Passport authentication: %s", mad->error );
-		imc_logout( ic, TRUE );
-	}
 }
 
-static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name )
+void msn_auth_got_contact_list( struct im_connection *ic )
 {
-	set_t *s;
-	
-	if( ( s = set_find( &ic->acc->set, "display_name" ) ) == NULL )
-		return FALSE; /* Shouldn't happen.. */
-	
-	http_decode( name );
-	
-	if( s->value && strcmp( s->value, name ) == 0 )
-	{
-		return TRUE;
-		/* The names match, nothing to worry about. */
-	}
-	else if( s->value != NULL &&
-	         ( strcmp( name, ic->acc->user ) == 0 ||
-	           set_getbool( &ic->acc->set, "local_display_name" ) ) )
-	{
-		/* The server thinks our display name is our e-mail address
-		   which is probably wrong, or the user *wants* us to do this:
-		   Always use the locally set display_name. */
-		return msn_set_display_name( ic, s->value );
-	}
-	else
-	{
-		if( s->value && *s->value )
-			imcb_log( ic, "BitlBee thinks your display name is `%s' but "
-			              "the MSN server says it's `%s'. Using the MSN "
-			              "server's name. Set local_display_name to true "
-			              "to use the local name.", s->value, name );
-		
-		if( g_utf8_validate( name, -1, NULL ) )
-		{
-			g_free( s->value );
-			s->value = g_strdup( name );
-		}
-		else
-		{
-			imcb_log( ic, "Warning: Friendly name in server response was corrupted" );
-		}
-		
-		return TRUE;
-	}
+	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 );
+}
