Index: lib/misc.c
===================================================================
--- lib/misc.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ lib/misc.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -298,9 +298,8 @@
 void http_encode( char *s )
 {
-	char *t;
+	char t[strlen(s)+1];
 	int i, j;
 	
-	t = g_strdup( s );
-	
+	strcpy( t, s );
 	for( i = j = 0; t[i]; i ++, j ++ )
 	{
@@ -320,6 +319,4 @@
 	}
 	s[j] = 0;
-	
-	g_free( t );
 }
 
Index: lib/sha1.c
===================================================================
--- lib/sha1.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ lib/sha1.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -36,4 +36,5 @@
  */
 
+#include <string.h>
 #include "sha1.h"
 
@@ -374,2 +375,48 @@
 	sha1_process_block(context);
 }
+
+#define HMAC_BLOCK_SIZE 64
+
+/* BitlBee addition: */
+void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size])
+{
+	sha1_state_t sha1;
+	uint8_t hash[sha1_hash_size];
+	uint8_t key[HMAC_BLOCK_SIZE+1];
+	int i;
+	
+	if( key_len == 0 )
+		key_len = strlen( key_ );
+	if( payload_len == 0 )
+		payload_len = strlen( payload );
+	
+	/* Create K. If our current key is >64 chars we have to hash it,
+	   otherwise just pad. */
+	memset( key, 0, HMAC_BLOCK_SIZE + 1 );
+	if( key_len > HMAC_BLOCK_SIZE )
+	{
+		sha1_init( &sha1 );
+		sha1_append( &sha1, (uint8_t*) key_, key_len );
+		sha1_finish( &sha1, key );
+	}
+	else
+	{
+		memcpy( key, key_, key_len );
+	}
+	
+	/* Inner part: H(K XOR 0x36, text) */
+	sha1_init( &sha1 );
+	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
+		key[i] ^= 0x36;
+	sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
+	sha1_append( &sha1, (const uint8_t*) payload, payload_len );
+	sha1_finish( &sha1, hash );
+	
+	/* Final result: H(K XOR 0x5C, inner stuff) */
+	sha1_init( &sha1 );
+	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
+		key[i] ^= 0x36 ^ 0x5c;
+	sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
+	sha1_append( &sha1, hash, sha1_hash_size );
+	sha1_finish( &sha1, Message_Digest );
+}
Index: lib/sha1.h
===================================================================
--- lib/sha1.h	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ lib/sha1.h	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -67,4 +67,5 @@
 G_MODULE_EXPORT int sha1_append(sha1_state_t *, const uint8_t *, unsigned int);
 G_MODULE_EXPORT int sha1_finish(sha1_state_t *, uint8_t Message_Digest[sha1_hash_size]);
+G_MODULE_EXPORT void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]);
 
 #endif
Index: lib/ssl_client.h
===================================================================
--- lib/ssl_client.h	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ lib/ssl_client.h	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -78,2 +78,4 @@
    the same action as the handler that just received the SSL_AGAIN.) */
 G_MODULE_EXPORT b_input_condition ssl_getdirection( void *conn );
+
+G_MODULE_EXPORT size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res);
Index: lib/ssl_gnutls.c
===================================================================
--- lib/ssl_gnutls.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ lib/ssl_gnutls.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -189,4 +189,6 @@
 		ssl_errno = SSL_AGAIN;
 	
+	if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
+	
 	return st;
 }
@@ -207,4 +209,6 @@
 	if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
 		ssl_errno = SSL_AGAIN;
+	
+	if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
 	
 	return st;
Index: lib/ssl_openssl.c
===================================================================
--- lib/ssl_openssl.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ lib/ssl_openssl.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -116,5 +116,7 @@
 	{
 		initialized = TRUE;
-		SSLeay_add_ssl_algorithms();
+		SSL_library_init();
+		//SSLeay_add_ssl_algorithms();
+		//OpenSSL_add_all_algorithms();
 	}
 	
@@ -205,4 +207,6 @@
 	}
 	
+	if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
+	
 	return st;
 }
@@ -219,4 +223,6 @@
 	
 	st = SSL_write( ((struct scd*)conn)->ssl, buf, len );
+	
+	if( getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
 	
 	ssl_errno = SSL_OK;
@@ -272,2 +278,24 @@
 	return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ );
 }
+
+size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res)
+{
+	int output_length = 0;    
+	EVP_CIPHER_CTX ctx;
+	
+	*res = g_new0(unsigned char, 72);
+	
+	/* Don't set key or IV because we will modify the parameters */
+	EVP_CIPHER_CTX_init(&ctx);
+	EVP_CipherInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1);
+	EVP_CIPHER_CTX_set_key_length(&ctx, key_len);
+	EVP_CIPHER_CTX_set_padding(&ctx, 0);
+	/* We finished modifying parameters so now we can set key and IV */
+	EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, 1);
+	EVP_CipherUpdate(&ctx, *res, &output_length, input, input_len);
+	EVP_CipherFinal_ex(&ctx, *res, &output_length);
+	EVP_CIPHER_CTX_cleanup(&ctx);   
+	//EVP_cleanup();
+	
+	return output_length;
+}
Index: lib/xmltree.c
===================================================================
--- lib/xmltree.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ lib/xmltree.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -141,5 +141,5 @@
 /* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
    end-of-stream and 1 otherwise. */
-int xt_feed( struct xt_parser *xt, char *text, int text_len )
+int xt_feed( struct xt_parser *xt, const char *text, int text_len )
 {
 	if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
@@ -174,18 +174,18 @@
 	if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
 	{
-		for( i = 0; xt->handlers[i].func; i ++ )
+		if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ )
 		{
 			/* This one is fun! \o/ */
 			
-						/* If handler.name == NULL it means it should always match. */
+			    /* If handler.name == NULL it means it should always match. */
 			if( ( xt->handlers[i].name == NULL || 
-						/* If it's not, compare. There should always be a name. */
+			      /* If it's not, compare. There should always be a name. */
 			      g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
-						/* If handler.parent == NULL, it's a match. */
+			    /* If handler.parent == NULL, it's a match. */
 			    ( xt->handlers[i].parent == NULL ||
-						/* If there's a parent node, see if the name matches. */
+			      /* If there's a parent node, see if the name matches. */
 			      ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
-						/* If there's no parent, the handler should mention <root> as a parent. */
-			                       g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
+			      /* If there's no parent, the handler should mention <root> as a parent. */
+			                       strcmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
 			{
 				st = xt->handlers[i].func( node, xt->data );
@@ -260,4 +260,18 @@
 }
 
+struct xt_node *xt_from_string( const char *in )
+{
+	struct xt_parser *parser;
+	struct xt_node *ret;
+	
+	parser = xt_new( NULL, NULL );
+	xt_feed( parser, in, strlen( in ) );
+	ret = parser->root;
+	parser->root = NULL;
+	xt_free( parser );
+	
+	return ret;
+}
+
 static void xt_to_string_real( struct xt_node *node, GString *str )
 {
@@ -550,4 +564,24 @@
 }
 
+/* Same, but at the beginning. */
+void xt_insert_child( struct xt_node *parent, struct xt_node *child )
+{
+	struct xt_node *node, *last;
+	
+	for( node = child; node; node = node->next )
+	{
+		if( node->parent != NULL )
+		{
+			/* ERROR CONDITION: They seem to have a parent already??? */
+		}
+		
+		node->parent = parent;
+		last = node;
+	}
+	
+	last->next = parent->children;
+	parent->children = child;
+}
+
 void xt_add_attr( struct xt_node *node, const char *key, const char *value )
 {
Index: lib/xmltree.h
===================================================================
--- lib/xmltree.h	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ lib/xmltree.h	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -79,7 +79,8 @@
 struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data );
 void xt_reset( struct xt_parser *xt );
-int xt_feed( struct xt_parser *xt, char *text, int text_len );
+int xt_feed( struct xt_parser *xt, const char *text, int text_len );
 int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth );
 void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth );
+struct xt_node *xt_from_string( const char *in );
 char *xt_to_string( struct xt_node *node );
 void xt_print( struct xt_node *node );
@@ -92,4 +93,5 @@
 struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children );
 void xt_add_child( struct xt_node *parent, struct xt_node *child );
+void xt_insert_child( struct xt_node *parent, struct xt_node *child );
 void xt_add_attr( struct xt_node *node, const char *key, const char *value );
 int xt_remove_attr( struct xt_node *node, const char *key );
Index: protocols/bee.h
===================================================================
--- protocols/bee.h	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/bee.h	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -148,4 +148,5 @@
  * - 'state' and 'message' can be NULL */
 G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message );
+G_MODULE_EXPORT void imcb_buddy_status_msg( struct im_connection *ic, const char *handle, const char *message );
 G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle );
 /* Call when a handle says something. 'flags' and 'sent_at may be just 0. */
Index: protocols/bee_user.c
===================================================================
--- protocols/bee_user.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/bee_user.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -187,6 +187,11 @@
 	/* TODO(wilmer): OPT_AWAY, or just state == NULL ? */
 	bu->flags = flags;
-	bu->status = g_strdup( ( flags & OPT_AWAY ) && state == NULL ? "Away" : state );
 	bu->status_msg = g_strdup( message );
+	if( state && *state )
+		bu->status = g_strdup( state );
+	else if( flags & OPT_AWAY )
+		bu->status = g_strdup( "Away" );
+	else
+		bu->status = NULL;
 	
 	if( bee->ui->user_status )
@@ -198,4 +203,26 @@
 }
 
+/* Same, but only change the away/status message, not any away/online state info. */
+void imcb_buddy_status_msg( struct im_connection *ic, const char *handle, const char *message )
+{
+	bee_t *bee = ic->bee;
+	bee_user_t *bu, *old;
+	
+	if( !( bu = bee_user_by_handle( bee, ic, handle ) ) )
+	{
+		return;
+	}
+	
+	old = g_memdup( bu, sizeof( bee_user_t ) );
+	
+	bu->status_msg = g_strdup( message );
+	
+	if( bee->ui->user_status )
+		bee->ui->user_status( bee, bu, old );
+	
+	g_free( old->status_msg );
+	g_free( old );
+}
+
 void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle )
 {
Index: protocols/msn/Makefile
===================================================================
--- protocols/msn/Makefile	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/msn/Makefile	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -13,5 +13,5 @@
 
 # [SH] Program variables
-objects = msn.o msn_util.o ns.o passport.o sb.o tables.o
+objects = msn.o msn_util.o ns.o sb.o soap.o tables.o
 
 LFLAGS += -r
@@ -43,3 +43,3 @@
 	@$(LD) $(LFLAGS) $(objects) -o msn_mod.o
 	
-
+soap.o: soap.h soap.c
Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/msn/msn.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -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                *
   \********************************************************************/
 
@@ -25,4 +25,5 @@
 
 #include "nogaim.h"
+#include "soap.h"
 #include "msn.h"
 
@@ -35,8 +36,13 @@
 static void msn_init( account_t *acc )
 {
-	set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc );
-	set_add( &acc->set, "local_display_name", "false", set_eval_bool, acc );
+	set_t *s;
+	
+	s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc );
+	s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
+	
 	set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
 	set_add( &acc->set, "switchboard_keepalives", "false", set_eval_bool, acc );
+	
+	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE;
 }
 
@@ -68,4 +74,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 );
@@ -103,4 +110,11 @@
 			g_free( md->grouplist[--md->groupcount] );
 		g_free( md->grouplist );
+		g_free( md->tokens[0] );
+		g_free( md->tokens[1] );
+		g_free( md->tokens[2] );
+		g_free( md->lock_key );
+		
+		g_tree_destroy( md->domaintree );
+		md->domaintree = NULL;
 		
 		while( md->grpq )
@@ -174,4 +188,5 @@
 {
 	char buf[1024];
+	char *uux;
 	struct msn_data *md = ic->proto_data;
 	
@@ -182,10 +197,12 @@
 	
 	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, md->away_state->code );
-	msn_write( ic, buf, strlen( buf ) );
-}
-
-static void msn_set_my_name( struct im_connection *ic, char *info )
-{
-	msn_set_display_name( ic, info );
+	if( !msn_write( ic, buf, strlen( buf ) ) )
+		return;
+	
+	uux = g_markup_printf_escaped( "<Data><PSM>%s</PSM><CurrentMedia></CurrentMedia>"
+	                               "</Data>", message ? message : "" );
+	g_snprintf( buf, sizeof( buf ), "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux );
+	if( !msn_write( ic, buf, strlen( buf ) ) )
+		return;
 }
 
@@ -314,8 +331,7 @@
 	account_t *acc = set->data;
 	struct im_connection *ic = acc->ic;
-	
-	/* Allow any name if we're offline. */
-	if( ic == NULL )
-		return value;
+	struct msn_data *md = ic->proto_data;
+	char buf[512];
+	char *fn;
 	
 	if( strlen( value ) > 129 )
@@ -325,8 +341,30 @@
 	}
 	
-	/* Returning NULL would be better, because the server still has to
-	   confirm the name change. However, it looks a bit confusing to the
-	   user. */
-	return msn_set_display_name( ic, value ) ? value : NULL;
+	msn_soap_addressbook_set_display_name( ic, value );
+
+	fn = g_malloc( strlen( value ) * 3 + 1 );
+	strcpy( fn, value );
+	http_encode( fn );
+	g_snprintf( buf, sizeof( buf ), "PRP %d MFN %s\r\n",
+	            ++md->trId, fn );
+	g_free( fn );
+	
+	/* Note: We don't actually know if the server accepted the new name,
+	   and won't give proper feedback yet if it doesn't. */
+	return msn_write( ic, buf, strlen( buf ) ) ? value : NULL;
+}
+
+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 );
 }
 
@@ -343,5 +381,4 @@
 	ret->set_away = msn_set_away;
 	ret->get_info = msn_get_info;
-	ret->set_my_name = msn_set_my_name;
 	ret->add_buddy = msn_add_buddy;
 	ret->remove_buddy = msn_remove_buddy;
@@ -357,4 +394,7 @@
 	ret->send_typing = msn_send_typing;
 	ret->handle_cmp = g_strcasecmp;
+	ret->buddy_data_add = msn_buddy_data_add;
+	ret->buddy_data_free = msn_buddy_data_free;
+	
 	//ret->transfer_request = msn_ftp_transfer_request;
 
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/msn/msn.h	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -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                *
   \********************************************************************/
 
@@ -39,6 +39,21 @@
 #endif
 
-#define QRY_NAME "msmsgs@msnmsgr.com"
-#define QRY_CODE "Q1P7W2E4J9R8U3S5"
+/* This should be MSN Messenger 7.0.0813
+#define MSNP11_PROD_KEY "CFHUR$52U_{VIX5T"
+#define MSNP11_PROD_ID  "PROD0101{0RM?UBW"
+*/
+
+/* Some other version.
+#define MSNP11_PROD_KEY "O4BG@C7BWLYQX?5G"
+#define MSNP11_PROD_ID  "PROD01065C%ZFN6F"
+*/
+
+#define MSNP11_PROD_KEY "ILTXC!4IXB5FB*PX"
+//PK}_A_0N_K%O?A9S"
+#define MSNP11_PROD_ID  "PROD0119GSJUC$18"
+//PROD0114ES4Z%Q5W"
+#define MSNP_VER        "MSNP15"
+#define MSNP_BUILD      "8.5.1288"
+//"8.1.0178"
 
 #define MSN_SB_NEW         -24062002
@@ -69,4 +84,6 @@
 	
 	int trId;
+	char *tokens[3];
+	char *lock_key;
 	
 	GSList *msgq, *grpq;
@@ -80,4 +97,5 @@
 	int groupcount;
 	char **grouplist;
+	GTree *domaintree;
 };
 
@@ -140,4 +158,19 @@
 	int (*exec_command) ( gpointer data, char **cmd, int count );
 	int (*exec_message) ( gpointer data, char *msg, int msglen, char **cmd, int count );
+};
+
+typedef enum
+{
+	MSN_BUDDY_FL = 1,   /* Warning: FL,AL,BL *must* be 1,2,4. */
+	MSN_BUDDY_AL = 2,
+	MSN_BUDDY_BL = 4,
+	MSN_BUDDY_RL = 8,
+	MSN_BUDDY_PL = 16,
+} msn_buddy_flags_t;
+
+struct msn_buddy_data
+{
+	char *cid;
+	msn_buddy_flags_t flags;
 };
 
@@ -162,4 +195,6 @@
 /* ns.c */
 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 */
@@ -172,7 +207,8 @@
 char **msn_linesplit( char *line );
 int msn_handler( struct msn_handler_data *h );
-char *msn_http_encode( const char *input );
 void msn_msgq_purge( struct im_connection *ic, GSList **list );
 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 */
@@ -196,6 +232,3 @@
 void msn_sb_stop_keepalives( struct msn_switchboard *sb );
 
-/* invitation.c */
-void msn_ftp_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *who );
-
 #endif //_MSN_H
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/msn/msn_util.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -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                *
   \********************************************************************/
 
@@ -26,4 +26,5 @@
 #include "nogaim.h"
 #include "msn.h"
+#include "md5.h"
 #include <ctype.h>
 
@@ -32,4 +33,10 @@
 	struct msn_data *md = ic->proto_data;
 	int st;
+	
+	if( getenv( "BITLBEE_DEBUG" ) )
+	{
+		write( 2, "->NS:", 5 );
+		write( 2, s, len );
+	}
 	
 	st = write( md->fd, s, len );
@@ -54,5 +61,5 @@
 {
 	struct msn_data *md = ic->proto_data;
-	char buf[1024], *realname, groupid[8];
+	char buf[1024], realname[strlen(realname_)*3+1], groupid[8];
 	
 	*groupid = '\0';
@@ -87,7 +94,8 @@
 			if( l == NULL )
 			{
-				char *groupname = msn_http_encode( group );
+				char groupname[strlen(group)+1];
+				strcpy( groupname, group );
+				http_encode( groupname );
 				g_snprintf( buf, sizeof( buf ), "ADG %d %s %d\r\n", ++md->trId, groupname, 0 );
-				g_free( groupname );
 				return msn_write( ic, buf, strlen( buf ) );
 			}
@@ -102,7 +110,7 @@
 	}
 	
-	realname = msn_http_encode( realname_ );
+	strcpy( realname, realname_ );
+	http_encode( realname );
 	g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s%s\r\n", ++md->trId, list, who, realname, groupid );
-	g_free( realname );
 	
 	return msn_write( ic, buf, strlen( buf ) );
@@ -279,4 +287,10 @@
 	if( st <= 0 )
 		return( -1 );
+	
+	if( getenv( "BITLBEE_DEBUG" ) )
+	{
+		write( 2, "->C:", 4 );
+		write( 2, h->rxq + h->rxlen - st, st );
+	}
 	
 	while( st )
@@ -367,30 +381,4 @@
 }
 
-/* The difference between this function and the normal http_encode() function
-   is that this one escapes every 7-bit ASCII character because this is said
-   to avoid some lame server-side checks when setting a real-name. Also,
-   non-ASCII characters are not escaped because MSN servers don't seem to
-   appreciate that! */
-char *msn_http_encode( const char *input )
-{
-	char *ret, *s;
-	int i;
-	
-	ret = s = g_new0( char, strlen( input ) * 3 + 1 );
-	for( i = 0; input[i]; i ++ )
-		if( input[i] & 128 )
-		{
-			*s = input[i];
-			s ++;
-		}
-		else
-		{
-			g_snprintf( s, 4, "%%%02X", input[i] );
-			s += 3;
-		}
-	
-	return ret;
-}
-
 void msn_msgq_purge( struct im_connection *ic, GSList **list )
 {
@@ -433,13 +421,84 @@
 }
 
-gboolean msn_set_display_name( struct im_connection *ic, const char *rawname )
-{
-	char *fn = msn_http_encode( rawname );
-	struct msn_data *md = ic->proto_data;
-	char buf[1024];
-	
-	g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn );
-	g_free( fn );
-	
-	return msn_write( ic, buf, strlen( buf ) ) != 0;
-}
+/* Copied and heavily modified from http://tmsnc.sourceforge.net/chl.c */
+char *msn_p11_challenge( char *challenge )
+{
+	char *output, buf[256];
+	md5_state_t md5c;
+	unsigned char md5Hash[16], *newHash;
+	unsigned int *md5Parts, *chlStringParts, newHashParts[5];
+	long long nHigh = 0, nLow = 0;
+	int i, n;
+
+	/* Create the MD5 hash */
+	md5_init(&md5c);
+	md5_append(&md5c, (unsigned char*) challenge, strlen(challenge));
+	md5_append(&md5c, (unsigned char*) MSNP11_PROD_KEY, strlen(MSNP11_PROD_KEY));
+	md5_finish(&md5c, md5Hash);
+
+	/* Split it into four integers */
+	md5Parts = (unsigned int *)md5Hash;
+	for (i = 0; i < 4; i ++)
+	{  
+		md5Parts[i] = GUINT32_TO_LE(md5Parts[i]);
+		
+		/* & each integer with 0x7FFFFFFF */
+		/* and save one unmodified array for later */
+		newHashParts[i] = md5Parts[i];
+		md5Parts[i] &= 0x7FFFFFFF;
+	}
+	
+	/* make a new string and pad with '0' */
+	n = g_snprintf(buf, sizeof(buf)-5, "%s%s00000000", challenge, MSNP11_PROD_ID);
+	/* truncate at an 8-byte boundary */
+	buf[n&=~7] = '\0';
+	
+	/* split into integers */
+	chlStringParts = (unsigned int *)buf;
+	
+	/* this is magic */
+	for (i = 0; i < (n / 4) - 1; i += 2)
+	{
+		long long temp;
+
+		chlStringParts[i]   = GUINT32_TO_LE(chlStringParts[i]);
+		chlStringParts[i+1] = GUINT32_TO_LE(chlStringParts[i+1]);
+
+		temp  = (md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF;
+		nHigh = (md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF;
+		nLow  = nLow + nHigh + temp;
+	}
+	nHigh = (nHigh+md5Parts[1]) % 0x7FFFFFFF;
+	nLow = (nLow+md5Parts[3]) % 0x7FFFFFFF;
+	
+	newHashParts[0] ^= nHigh;
+	newHashParts[1] ^= nLow;
+	newHashParts[2] ^= nHigh;
+	newHashParts[3] ^= nLow;
+	
+	/* swap more bytes if big endian */
+	for (i = 0; i < 4; i ++)
+		newHashParts[i] = GUINT32_TO_LE(newHashParts[i]); 
+	
+	/* make a string of the parts */
+	newHash = (unsigned char *)newHashParts;
+	
+	/* convert to hexadecimal */
+	output = g_new(char, 33);
+	for (i = 0; i < 16; i ++)
+		sprintf(output + i * 2, "%02x", newHash[i]);
+	
+	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 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/msn/ns.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -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 );
+}
Index: otocols/msn/passport.c
===================================================================
--- protocols/msn/passport.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ 	(revision )
@@ -1,172 +1,0 @@
-/** passport.c
- *
- * Functions to login to Microsoft Passport service for Messenger
- * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net>
- *
- * This program is free software; you can redistribute it and/or modify             
- * it under the terms of the GNU General Public License version 2                   
- * as published by the Free Software Foundation                                     
- *                                                                                   
- * This program is distributed in the hope that is will be useful,                  
- * bit WITHOU ANY WARRANTY; without even the implied warranty of                   
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    
- * GNU General Public License for more details.                                     
- *                                                                                   
- * You should have received a copy of the GNU General Public License                
- * along with this program; if not, write to the Free Software                      
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA          
- *
- */
-
-#include "http_client.h"
-#include "passport.h"
-#include "msn.h"
-#include "bitlbee.h"
-#include "url.h"
-#include "misc.h"
-#include "xmltree.h"
-#include <ctype.h>
-#include <errno.h>
-
-static int passport_get_token_real( struct msn_auth_data *mad );
-static void passport_get_token_ready( struct http_request *req );
-
-int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie )
-{
-	struct msn_auth_data *mad = g_new0( struct msn_auth_data, 1 );
-	int i;
-	
-	mad->username = g_strdup( username );
-	mad->password = g_strdup( password );
-	mad->cookie = g_strdup( cookie );
-	
-	mad->callback = func;
-	mad->data = data;
-	
-	mad->url = g_strdup( SOAP_AUTHENTICATION_URL );
-	mad->ttl = 3; /* Max. # of redirects. */
-	
-	/* HTTP-escape stuff and s/,/&/ */
-	http_decode( mad->cookie );
-	for( i = 0; mad->cookie[i]; i ++ )
-		if( mad->cookie[i] == ',' )
-			mad->cookie[i] = '&';
-	
-	/* Microsoft doesn't allow password longer than 16 chars and silently
-	   fails authentication if you give the "full version" of your passwd. */
-	if( strlen( mad->password ) > MAX_PASSPORT_PWLEN )
-		mad->password[MAX_PASSPORT_PWLEN] = 0;
-	
-	return passport_get_token_real( mad );
-}
-
-static int passport_get_token_real( struct msn_auth_data *mad )
-{
-	char *post_payload, *post_request;
-	struct http_request *req;
-	url_t url;
-	
-	url_set( &url, mad->url );
-	
-	post_payload = g_markup_printf_escaped( SOAP_AUTHENTICATION_PAYLOAD,
-	                                        mad->username,
-	                                        mad->password,
-	                                        mad->cookie );
-	
-	post_request = g_strdup_printf( SOAP_AUTHENTICATION_REQUEST,
-	                                url.file, url.host,
-	                                (int) strlen( post_payload ),
-	                                post_payload );
-	                                
-	req = http_dorequest( url.host, url.port, 1, post_request,
-	                      passport_get_token_ready, mad );
-	
-	g_free( post_request );
-	g_free( post_payload );
-	
-	return req != NULL;
-}
-
-static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data );
-static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data );
-
-static const struct xt_handler_entry passport_xt_handlers[] = {
-	{ "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", passport_xt_extract_token },
-	{ "S:Fault",                  "S:Envelope",                 passport_xt_handle_fault  },
-	{ NULL,                       NULL,                         NULL                      }
-};
-
-static void passport_get_token_ready( struct http_request *req )
-{
-	struct msn_auth_data *mad = req->data;
-	struct xt_parser *parser;
-	
-	g_free( mad->url );
-	g_free( mad->error );
-	mad->url = mad->error = NULL;
-	
-	if( req->status_code == 200 )
-	{
-		parser = xt_new( passport_xt_handlers, mad );
-		xt_feed( parser, req->reply_body, req->body_size );
-		xt_handle( parser, NULL, -1 );
-		xt_free( parser );
-	}
-	else
-	{
-		mad->error = g_strdup_printf( "HTTP error %d (%s)", req->status_code,
-		                              req->status_string ? req->status_string : "unknown" );
-	}
-	
-	if( mad->error == NULL && mad->token == NULL )
-		mad->error = g_strdup( "Could not parse Passport server response" );
-	
-	if( mad->url && mad->token == NULL )
-	{
-		passport_get_token_real( mad );
-	}
-	else
-	{
-		mad->callback( mad );
-		
-		g_free( mad->url );
-		g_free( mad->username );
-		g_free( mad->password );
-		g_free( mad->cookie );
-		g_free( mad->token );
-		g_free( mad->error );
-		g_free( mad );
-	}
-}
-
-static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data )
-{
-	struct msn_auth_data *mad = data;
-	char *s;
-	
-	if( ( s = xt_find_attr( node, "Id" ) ) &&
-	    ( strncmp( s, "Compact", 7 ) == 0 ||
-	      strncmp( s, "PPToken", 7 ) == 0 ) )
-		mad->token = g_memdup( node->text, node->text_len + 1 );
-	
-	return XT_HANDLED;
-}
-
-static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data )
-{
-	struct msn_auth_data *mad = data;
-	struct xt_node *code = xt_find_node( node->children, "faultcode" );
-	struct xt_node *string = xt_find_node( node->children, "faultstring" );
-	struct xt_node *redirect = xt_find_node( node->children, "psf:redirectUrl" );
-	
-	if( redirect && redirect->text_len && mad->ttl-- > 0 )
-		mad->url = g_memdup( redirect->text, redirect->text_len + 1 );
-	
-	if( code == NULL || code->text_len == 0 )
-		mad->error = g_strdup( "Unknown error" );
-	else
-		mad->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ?
-		                              string->text : "no description available" );
-	
-	return XT_HANDLED;
-}
Index: otocols/msn/passport.h
===================================================================
--- protocols/msn/passport.h	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ 	(revision )
@@ -1,113 +1,0 @@
-/* passport.h
- *
- * Functions to login to Microsoft Passport service for Messenger
- * Copyright (C) 2004-2008 Wilmer van der Gaast <wilmer@gaast.net>
- *
- * This program is free software; you can redistribute it and/or modify             
- * it under the terms of the GNU General Public License version 2                   
- * as published by the Free Software Foundation                                     
- *                                                                                   
- * This program is distributed in the hope that is will be useful,                  
- * bit WITHOU ANY WARRANTY; without even the implied warranty of                   
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    
- * GNU General Public License for more details.                                     
- *                                                                                   
- * You should have received a copy of the GNU General Public License                
- * along with this program; if not, write to the Free Software                      
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA          
- */
-
-/* Thanks to http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
-   for the specs! */
-
-#ifndef __PASSPORT_H__
-#define __PASSPORT_H__
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#ifndef _WIN32
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#endif
-#include "nogaim.h"
-
-#define MAX_PASSPORT_PWLEN 16
-
-struct msn_auth_data
-{
-	char *url;
-	int ttl;
-	
-	char *username;
-	char *password;
-	char *cookie;
-	
-	/* The end result, the only thing we'll really be interested in
-	   once finished. */
-	char *token;
-	char *error; /* Yeah, or that... */
-	
-	void (*callback)( struct msn_auth_data *mad );
-	gpointer data;
-};
-
-#define SOAP_AUTHENTICATION_URL "https://loginnet.passport.com/RST.srf"
-
-#define SOAP_AUTHENTICATION_REQUEST \
-"POST %s HTTP/1.0\r\n" \
-"Accept: text/*\r\n" \
-"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \
-"Host: %s\r\n" \
-"Content-Length: %d\r\n" \
-"Cache-Control: no-cache\r\n" \
-"\r\n" \
-"%s"
-
-#define SOAP_AUTHENTICATION_PAYLOAD \
-"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \
-"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \
-  "<Header>" \
-    "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">" \
-      "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \
-      "<ps:BinaryVersion>4</ps:BinaryVersion>" \
-      "<ps:UIVersion>1</ps:UIVersion>" \
-      "<ps:Cookies></ps:Cookies>" \
-      "<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>" \
-    "</ps:AuthInfo>" \
-    "<wsse:Security>" \
-       "<wsse:UsernameToken Id=\"user\">" \
-         "<wsse:Username>%s</wsse:Username>" \
-         "<wsse:Password>%s</wsse:Password>" \
-       "</wsse:UsernameToken>" \
-    "</wsse:Security>" \
-  "</Header>" \
-  "<Body>" \
-    "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">" \
-      "<wst:RequestSecurityToken Id=\"RST0\">" \
-        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
-        "<wsp:AppliesTo>" \
-          "<wsa:EndpointReference>" \
-            "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \
-          "</wsa:EndpointReference>" \
-        "</wsp:AppliesTo>" \
-      "</wst:RequestSecurityToken>" \
-      "<wst:RequestSecurityToken Id=\"RST1\">" \
-       "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
-        "<wsp:AppliesTo>" \
-          "<wsa:EndpointReference>" \
-            "<wsa:Address>messenger.msn.com</wsa:Address>" \
-          "</wsa:EndpointReference>" \
-        "</wsp:AppliesTo>" \
-        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>" \
-      "</wst:RequestSecurityToken>" \
-    "</ps:RequestMultipleSecurityTokens>" \
-  "</Body>" \
-"</Envelope>"
-
-int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie );
-
-#endif /* __PASSPORT_H__ */
Index: protocols/msn/sb.c
===================================================================
--- protocols/msn/sb.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/msn/sb.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2005 Wilmer van der Gaast and others                *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -27,6 +27,6 @@
 #include "nogaim.h"
 #include "msn.h"
-#include "passport.h"
 #include "md5.h"
+#include "soap.h"
 #include "invitation.h"
 
@@ -38,4 +38,10 @@
 {
 	int st;
+	
+	if( getenv( "BITLBEE_DEBUG" ) )
+	{
+		write( 2, "->SB:", 5 );
+		write( 2, s, len );
+	}
 	
 	st = write( sb->fd, s, len );
@@ -407,5 +413,5 @@
 	else if( strcmp( cmd[0], "USR" ) == 0 )
 	{
-		if( num_parts != 5 )
+		if( num_parts < 5 )
 		{
 			msn_sb_destroy( sb );
@@ -433,5 +439,5 @@
 		int num, tot;
 		
-		if( num_parts != 6 )
+		if( num_parts < 6 )
 		{
 			msn_sb_destroy( sb );
@@ -470,5 +476,5 @@
 	else if( strcmp( cmd[0], "ANS" ) == 0 )
 	{
-		if( num_parts != 3 )
+		if( num_parts < 3 )
 		{
 			msn_sb_destroy( sb );
@@ -489,5 +495,5 @@
 	else if( strcmp( cmd[0], "CAL" ) == 0 )
 	{
-		if( num_parts != 4 || !isdigit( cmd[3][0] ) )
+		if( num_parts < 4 || !isdigit( cmd[3][0] ) )
 		{
 			msn_sb_destroy( sb );
@@ -499,5 +505,5 @@
 	else if( strcmp( cmd[0], "JOI" ) == 0 )
 	{
-		if( num_parts != 3 )
+		if( num_parts < 3 )
 		{
 			msn_sb_destroy( sb );
@@ -560,5 +566,5 @@
 	else if( strcmp( cmd[0], "MSG" ) == 0 )
 	{
-		if( num_parts != 4 )
+		if( num_parts < 4 )
 		{
 			msn_sb_destroy( sb );
@@ -625,5 +631,10 @@
 		const struct msn_status_code *err = msn_status_by_number( num );
 		
-		imcb_error( ic, "Error reported by switchboard server: %s", err->text );
+		/* If the person is offline, send an offline message instead,
+		   and don't report an error. */
+		if( num == 217 )
+			msn_soap_oim_send_queue( ic, &sb->msgq );
+		else
+			imcb_error( ic, "Error reported by switchboard server: %s", err->text );
 		
 		if( err->flags & STATUS_SB_FATAL )
Index: protocols/msn/soap.c
===================================================================
--- protocols/msn/soap.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
+++ protocols/msn/soap.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -0,0 +1,644 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* MSN module - All the SOAPy XML stuff.
+   Some manager at Microsoft apparently thought MSNP wasn't XMLy enough so
+   someone stepped up and changed that. This is the result. Kilobytes and
+   more kilobytes of XML vomit to transfer tiny bits of informaiton. */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "http_client.h"
+#include "soap.h"
+#include "msn.h"
+#include "bitlbee.h"
+#include "url.h"
+#include "misc.h"
+#include "sha1.h"
+#include "base64.h"
+#include "xmltree.h"
+#include <ctype.h>
+#include <errno.h>
+
+/* This file tries to make SOAP stuff pretty simple to do by letting you just
+   provide a function to build a request, a few functions to parse various
+   parts of the response, and a function to run when the full response was
+   received and parsed. See the various examples below. */
+
+typedef enum
+{
+	MSN_SOAP_OK,
+	MSN_SOAP_RETRY,
+	MSN_SOAP_ABORT,
+} msn_soap_result_t;
+
+struct msn_soap_req_data;
+typedef int (*msn_soap_func) ( struct msn_soap_req_data * );
+
+struct msn_soap_req_data
+{
+	void *data;
+	struct im_connection *ic;
+	int ttl;
+	
+	char *url, *action, *payload;
+	struct http_request *http_req;
+	
+	const struct xt_handler_entry *xml_parser;
+	msn_soap_func build_request, handle_response, free_data;
+};
+
+static int msn_soap_send_request( struct msn_soap_req_data *req );
+
+static int msn_soap_start( struct im_connection *ic,
+                    void *data,
+                    msn_soap_func build_request,
+                    const struct xt_handler_entry *xml_parser,
+                    msn_soap_func handle_response,
+                    msn_soap_func free_data )
+{
+	struct msn_soap_req_data *req = g_new0( struct msn_soap_req_data, 1 );
+	
+	req->ic = ic;
+	req->data = data;
+	req->xml_parser = xml_parser;
+	req->build_request = build_request;
+	req->handle_response = handle_response;
+	req->free_data = free_data;
+	req->ttl = 3;
+	
+	return msn_soap_send_request( req );
+}
+
+static void msn_soap_handle_response( struct http_request *http_req );
+
+static int msn_soap_send_request( struct msn_soap_req_data *soap_req )
+{
+	char *http_req;
+	char *soap_action = NULL;
+	url_t url;
+	
+	soap_req->build_request( soap_req );
+	
+	if( soap_req->action )
+		soap_action = g_strdup_printf( "SOAPAction: \"%s\"\r\n", soap_req->action );
+	
+	url_set( &url, soap_req->url );
+	http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host,
+		soap_action ? soap_action : "",
+		strlen( soap_req->payload ), soap_req->payload );
+	
+	soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS,
+		http_req, msn_soap_handle_response, soap_req );
+	
+	g_free( http_req );
+	g_free( soap_action );
+	
+	return soap_req->http_req != NULL;
+}
+
+static void msn_soap_handle_response( struct http_request *http_req )
+{
+	struct msn_soap_req_data *soap_req = http_req->data;
+	int st;
+	
+	if( http_req->body_size > 0 )
+	{
+		struct xt_parser *parser;
+		
+		parser = xt_new( soap_req->xml_parser, soap_req );
+		xt_feed( parser, http_req->reply_body, http_req->body_size );
+		xt_handle( parser, NULL, -1 );
+		xt_free( parser );
+	}
+	
+	st = soap_req->handle_response( soap_req );
+	
+	g_free( soap_req->url );
+	g_free( soap_req->action );
+	g_free( soap_req->payload );
+	soap_req->url = soap_req->action = soap_req->payload = NULL;
+	
+	if( st == MSN_SOAP_RETRY && --soap_req->ttl )
+		msn_soap_send_request( soap_req );
+	else
+	{
+		soap_req->free_data( soap_req );
+		g_free( soap_req );
+	}
+}
+
+
+/* passport_sso: Authentication MSNP15+ */
+
+struct msn_soap_passport_sso_data
+{
+	char *policy;
+	char *nonce;
+	char *secret;
+};
+
+static int msn_soap_passport_sso_build_request( struct msn_soap_req_data *soap_req )
+{
+	struct msn_soap_passport_sso_data *sd = soap_req->data;
+	struct im_connection *ic = soap_req->ic;
+	
+	if( g_str_has_suffix( ic->acc->user, "@msn.com" ) )
+		soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL_MSN );
+	else
+		soap_req->url = g_strdup( SOAP_PASSPORT_SSO_URL );
+	
+	soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD,
+		ic->acc->user, ic->acc->pass, sd->policy );
+	
+	return MSN_SOAP_OK;
+}
+
+static xt_status msn_soap_passport_sso_token( struct xt_node *node, gpointer data )
+{
+	struct msn_soap_req_data *soap_req = data;
+	struct msn_soap_passport_sso_data *sd = soap_req->data;
+	struct msn_data *md = soap_req->ic->proto_data;
+	struct xt_node *p;
+	char *id;
+	
+	if( ( id = xt_find_attr( node, "Id" ) ) == NULL )
+		return XT_HANDLED;
+	id += strlen( id ) - 1;
+	if( *id == '1' &&
+	    ( p = node->parent ) && ( p = p->parent ) &&
+	    ( p = xt_find_node( p->children, "wst:RequestedProofToken" ) ) &&
+	    ( p = xt_find_node( p->children, "wst:BinarySecret" ) ) &&
+	    p->text )
+	    	sd->secret = g_strdup( p->text );
+	
+	*id -= '1';
+	if( *id >= 0 && *id <= 2 )
+	{
+		g_free( md->tokens[(int)*id] );
+		md->tokens[(int)*id] = g_strdup( node->text );
+	}
+	
+	return XT_HANDLED;
+}
+
+static const struct xt_handler_entry msn_soap_passport_sso_parser[] = {
+	{ "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token },
+	{ NULL, NULL, NULL }
+};
+
+static char *msn_key_fuckery( char *key, int key_len, char *type )
+{
+	unsigned char hash1[20+strlen(type)+1];
+	unsigned char hash2[20];
+	char *ret;
+	
+	sha1_hmac( key, key_len, type, 0, hash1 );
+	strcpy( (char*) hash1 + 20, type );
+	sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash2 );
+	
+	/* This is okay as hash1 is read completely before it's overwritten. */
+	sha1_hmac( key, key_len, (char*) hash1, 20, hash1 );
+	sha1_hmac( key, key_len, (char*) hash1, sizeof( hash1 ) - 1, hash1 );
+	
+	ret = g_malloc( 24 );
+	memcpy( ret, hash2, 20 );
+	memcpy( ret + 20, hash1, 4 );
+	return ret;
+}
+
+static int msn_soap_passport_sso_handle_response( struct msn_soap_req_data *soap_req )
+{
+	struct msn_soap_passport_sso_data *sd = soap_req->data;
+	struct im_connection *ic = soap_req->ic;
+	char *key1, *key2, *key3, *blurb64;
+	int key1_len;
+	unsigned char *padnonce, *des3res;
+	struct
+	{
+		unsigned int uStructHeaderSize; // 28. Does not count data
+		unsigned int uCryptMode; // CRYPT_MODE_CBC (1)
+		unsigned int uCipherType; // TripleDES (0x6603)
+		unsigned int uHashType; // SHA1 (0x8004)
+		unsigned int uIVLen;    // 8
+		unsigned int uHashLen;  // 20
+		unsigned int uCipherLen; // 72
+		unsigned char iv[8];
+		unsigned char hash[20];
+		unsigned char cipherbytes[72];
+	} blurb = {
+		GUINT32_TO_LE( 28 ),
+		GUINT32_TO_LE( 1 ),
+		GUINT32_TO_LE( 0x6603 ),
+		GUINT32_TO_LE( 0x8004 ),
+		GUINT32_TO_LE( 8 ),
+		GUINT32_TO_LE( 20 ),
+		GUINT32_TO_LE( 72 ),
+	};
+
+	key1_len = base64_decode( sd->secret, (unsigned char**) &key1 );
+	
+	key2 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY HASH" );
+	key3 = msn_key_fuckery( key1, key1_len, "WS-SecureConversationSESSION KEY ENCRYPTION" );
+	
+	sha1_hmac( key2, 24, sd->nonce, 0, blurb.hash );
+	padnonce = g_malloc( strlen( sd->nonce ) + 8 );
+	strcpy( (char*) padnonce, sd->nonce );
+	memset( padnonce + strlen( sd->nonce ), 8, 8 );
+	
+	random_bytes( blurb.iv, 8 );
+	
+	ssl_des3_encrypt( (unsigned char*) key3, 24, padnonce, strlen( sd->nonce ) + 8, blurb.iv, &des3res );
+	memcpy( blurb.cipherbytes, des3res, 72 );
+	
+	blurb64 = base64_encode( (unsigned char*) &blurb, sizeof( blurb ) );
+	msn_auth_got_passport_token( ic, blurb64 );
+	
+	g_free( padnonce );
+	g_free( blurb64 );
+	g_free( des3res );
+	g_free( key1 );
+	g_free( key2 );
+	g_free( key3 );
+	
+	return MSN_SOAP_OK;
+}
+
+static int msn_soap_passport_sso_free_data( struct msn_soap_req_data *soap_req )
+{
+	struct msn_soap_passport_sso_data *sd = soap_req->data;
+	
+	g_free( sd->policy );
+	g_free( sd->nonce );
+	g_free( sd->secret );
+	
+	return MSN_SOAP_OK;
+}
+
+int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce )
+{
+	struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 );
+	
+	sd->policy = g_strdup( policy );
+	sd->nonce = g_strdup( nonce );
+	
+	return msn_soap_start( ic, sd, msn_soap_passport_sso_build_request,
+	                               msn_soap_passport_sso_parser,
+	                               msn_soap_passport_sso_handle_response,
+	                               msn_soap_passport_sso_free_data );
+}
+
+
+/* oim_send: Sending offline messages */
+
+struct msn_soap_oim_send_data
+{
+	char *to;
+	char *msg;
+	int number;
+	int need_retry;
+};
+
+static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req )
+{
+	struct msn_soap_oim_send_data *oim = soap_req->data;
+	struct im_connection *ic = soap_req->ic;
+	struct msn_data *md = ic->proto_data;
+	char *display_name_b64;
+	
+	display_name_b64 = tobase64( set_getstr( &ic->acc->set, "display_name" ) );
+	
+	soap_req->url = g_strdup( SOAP_OIM_SEND_URL );
+	soap_req->action = g_strdup( SOAP_OIM_SEND_ACTION );
+	soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD,
+		ic->acc->user, display_name_b64, MSNP_VER, MSNP_BUILD,
+		oim->to, md->tokens[2],
+		MSNP11_PROD_ID, md->lock_key ? md->lock_key : "",
+		oim->number, oim->number, oim->msg );
+	
+	g_free( display_name_b64 );
+	
+	return MSN_SOAP_OK;
+}
+
+static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data )
+{
+	struct msn_soap_req_data *soap_req = data;
+	struct msn_soap_oim_send_data *oim = soap_req->data;
+	struct im_connection *ic = soap_req->ic;
+	struct msn_data *md = ic->proto_data;
+	
+	g_free( md->lock_key );
+	md->lock_key = msn_p11_challenge( node->text );
+	
+	oim->need_retry = 1;
+	
+	return XT_HANDLED;
+}
+
+static const struct xt_handler_entry msn_soap_oim_send_parser[] = {
+	{ "LockKeyChallenge", "detail", msn_soap_oim_send_challenge },
+	{ NULL,               NULL,     NULL                        }
+};
+
+static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req )
+{
+	struct msn_soap_oim_send_data *oim = soap_req->data;
+	
+	if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 )
+	{
+		oim->need_retry = 0;
+		return MSN_SOAP_RETRY;
+	}
+	else if( soap_req->http_req->status_code == 200 )
+	{
+		imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to );
+		return MSN_SOAP_OK;
+	}
+	else
+	{
+		imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg );
+		return MSN_SOAP_ABORT;
+	}
+}
+
+static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req )
+{
+	struct msn_soap_oim_send_data *oim = soap_req->data;
+	
+	g_free( oim->to );
+	g_free( oim->msg );
+	g_free( oim );
+	
+	return MSN_SOAP_OK;
+}
+
+int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg )
+{
+	struct msn_soap_oim_send_data *data;
+	
+	data = g_new0( struct msn_soap_oim_send_data, 1 );
+	data->to = g_strdup( to );
+	data->msg = tobase64( msg );
+	data->number = 1;
+	
+	return msn_soap_start( ic, data, msn_soap_oim_build_request,
+	                                 msn_soap_oim_send_parser,
+	                                 msn_soap_oim_handle_response,
+	                                 msn_soap_oim_free_data );
+}
+
+int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq )
+{
+	GSList *l;
+	char *n = NULL;
+	
+	for( l = *msgq; l; l = l->next )
+	{
+		struct msn_message *m = l->data;
+		
+		if( n == NULL )
+			n = m->who;
+		if( strcmp( n, m->who ) == 0 )
+			msn_soap_oim_send( ic, m->who, m->text );
+	}
+	
+	while( *msgq != NULL )
+	{
+		struct msn_message *m = (*msgq)->data;
+		
+		g_free( m->who );
+		g_free( m->text );
+		g_free( m );
+		
+		*msgq = g_slist_remove( *msgq, m );
+	}
+	
+	return 1;
+}
+
+
+/* memlist: Fetching the membership list (NOT address book) */
+
+static int msn_soap_memlist_build_request( struct msn_soap_req_data *soap_req )
+{
+	struct msn_data *md = soap_req->ic->proto_data;
+	
+	soap_req->url = g_strdup( SOAP_MEMLIST_URL );
+	soap_req->action = g_strdup( SOAP_MEMLIST_ACTION );
+	soap_req->payload = g_markup_printf_escaped( SOAP_MEMLIST_PAYLOAD, md->tokens[1] );
+	
+	return 1;
+}
+
+static xt_status msn_soap_memlist_member( struct xt_node *node, gpointer data )
+{
+	bee_user_t *bu;
+	struct msn_buddy_data *bd;
+	struct xt_node *p;
+	char *role = NULL, *handle = NULL;
+	struct msn_soap_req_data *soap_req = data;
+	struct im_connection *ic = soap_req->ic;
+	
+	if( ( p = node->parent ) && ( p = p->parent ) &&
+	    ( p = xt_find_node( p->children, "MemberRole" ) ) )
+		role = p->text;
+	
+	if( ( p = xt_find_node( node->children, "PassportName" ) ) )
+		handle = p->text;
+	
+	if( !role || !handle || 
+	    !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ||
+	       ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) )
+		return XT_HANDLED;
+	
+	bd = bu->data;
+	if( strcmp( role, "Allow" ) == 0 )
+		bd->flags |= MSN_BUDDY_AL;
+	else if( strcmp( role, "Block" ) == 0 )
+		bd->flags |= MSN_BUDDY_BL;
+	else if( strcmp( role, "Reverse" ) == 0 )
+		bd->flags |= MSN_BUDDY_RL;
+	else if( strcmp( role, "Pending" ) == 0 )
+		bd->flags |= MSN_BUDDY_PL;
+	
+	return XT_HANDLED;
+}
+
+static const struct xt_handler_entry msn_soap_memlist_parser[] = {
+	{ "Member", "Members", msn_soap_memlist_member },
+	{ NULL,               NULL,     NULL                        }
+};
+
+static int msn_soap_memlist_handle_response( struct msn_soap_req_data *soap_req )
+{
+	msn_soap_addressbook_request( soap_req->ic );
+	
+	return MSN_SOAP_OK;
+}
+
+static int msn_soap_memlist_free_data( struct msn_soap_req_data *soap_req )
+{
+	return 0;
+}
+
+int msn_soap_memlist_request( struct im_connection *ic )
+{
+	return msn_soap_start( ic, NULL, msn_soap_memlist_build_request,
+	                                 msn_soap_memlist_parser,
+	                                 msn_soap_memlist_handle_response,
+	                                 msn_soap_memlist_free_data );
+}
+
+
+/* addressbook: Fetching the membership list (NOT address book) */
+
+static int msn_soap_addressbook_build_request( struct msn_soap_req_data *soap_req )
+{
+	struct msn_data *md = soap_req->ic->proto_data;
+	
+	soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
+	soap_req->action = g_strdup( SOAP_ADDRESSBOOK_ACTION );
+	soap_req->payload = g_markup_printf_escaped( SOAP_ADDRESSBOOK_PAYLOAD, md->tokens[1] );
+	
+	return 1;
+}
+
+static xt_status msn_soap_addressbook_group( struct xt_node *node, gpointer data )
+{
+	struct xt_node *p;
+	char *id = NULL, *name = NULL;
+	struct msn_soap_req_data *soap_req = data;
+	
+	if( ( p = node->parent ) &&
+	    ( p = xt_find_node( p->children, "groupId" ) ) )
+		id = p->text;
+	
+	if( ( p = xt_find_node( node->children, "name" ) ) )
+		name = p->text;
+	
+	printf( "%s %s\n", id, name );
+	
+	return XT_HANDLED;
+}
+
+static xt_status msn_soap_addressbook_contact( struct xt_node *node, gpointer data )
+{
+	bee_user_t *bu;
+	struct msn_buddy_data *bd;
+	struct xt_node *p;
+	char *id = NULL, *type = NULL, *handle = NULL, *display_name = NULL;
+	struct msn_soap_req_data *soap_req = data;
+	struct im_connection *ic = soap_req->ic;
+	
+	if( ( p = node->parent ) &&
+	    ( p = xt_find_node( p->children, "contactId" ) ) )
+		id = p->text;
+	if( ( p = xt_find_node( node->children, "contactType" ) ) )
+		type = p->text;
+	if( ( p = xt_find_node( node->children, "passportName" ) ) )
+		handle = p->text;
+	if( ( p = xt_find_node( node->children, "displayName" ) ) )
+		display_name = p->text;
+	
+	if( type && g_strcasecmp( type, "me" ) == 0 )
+	{
+		set_t *set = set_find( &ic->acc->set, "display_name" );
+		g_free( set->value );
+		set->value = g_strdup( display_name );
+		
+		return XT_HANDLED;
+	}
+	
+	if( !( bu = bee_user_by_handle( ic->bee, ic, handle ) ) &&
+	    !( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) )
+		return XT_HANDLED;
+	
+	bd = bu->data;
+	bd->flags |= MSN_BUDDY_FL;
+	g_free( bd->cid );
+	bd->cid = g_strdup( id );
+	
+	imcb_rename_buddy( ic, handle, display_name );
+	
+	printf( "%s %s %s %s\n", id, type, handle, display_name );
+	
+	return XT_HANDLED;
+}
+
+static const struct xt_handler_entry msn_soap_addressbook_parser[] = {
+	{ "contactInfo", "Contact", msn_soap_addressbook_contact },
+	{ "groupInfo", "Group", msn_soap_addressbook_group },
+	{ NULL,               NULL,     NULL                        }
+};
+
+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;
+}
+
+static int msn_soap_addressbook_free_data( struct msn_soap_req_data *soap_req )
+{
+	return 0;
+}
+
+int msn_soap_addressbook_request( struct im_connection *ic )
+{
+	return msn_soap_start( ic, NULL, msn_soap_addressbook_build_request,
+	                                 msn_soap_addressbook_parser,
+	                                 msn_soap_addressbook_handle_response,
+	                                 msn_soap_addressbook_free_data );
+}
+
+/* Variant: Change our display name. */
+static int msn_soap_ab_namechange_build_request( struct msn_soap_req_data *soap_req )
+{
+	struct msn_data *md = soap_req->ic->proto_data;
+	
+	soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL );
+	soap_req->action = g_strdup( SOAP_AB_NAMECHANGE_ACTION );
+	soap_req->payload = g_markup_printf_escaped( SOAP_AB_NAMECHANGE_PAYLOAD,
+		md->tokens[1], (char *) soap_req->data );
+	
+	return 1;
+}
+
+static int msn_soap_ab_namechange_handle_response( struct msn_soap_req_data *soap_req )
+{
+	/* TODO: Ack the change? Not sure what the NAKs look like.. */
+	return MSN_SOAP_OK;
+}
+
+static int msn_soap_ab_namechange_free_data( struct msn_soap_req_data *soap_req )
+{
+	g_free( soap_req->data );
+	return 0;
+}
+
+int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new )
+{
+	return msn_soap_start( ic, g_strdup( new ),
+	                       msn_soap_ab_namechange_build_request,
+	                       NULL,
+	                       msn_soap_ab_namechange_handle_response,
+	                       msn_soap_ab_namechange_free_data );
+}
Index: protocols/msn/soap.h
===================================================================
--- protocols/msn/soap.h	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
+++ protocols/msn/soap.h	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -0,0 +1,258 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* MSN module - All the SOAPy XML stuff.
+   Some manager at Microsoft apparently thought MSNP wasn't XMLy enough so
+   someone stepped up and changed that. This is the result. Kilobytes and
+   more kilobytes of XML vomit to transfer tiny bits of informaiton. */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/* Thanks to http://msnpiki.msnfanatic.com/ for lots of info on this! */
+
+#ifndef __SOAP_H__
+#define __SOAP_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#endif
+#include "nogaim.h"
+
+
+#define SOAP_HTTP_REQUEST \
+"POST %s HTTP/1.0\r\n" \
+"Host: %s\r\n" \
+"Accept: */*\r\n" \
+"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \
+"Content-Type: text/xml; charset=utf-8\r\n" \
+"%s" \
+"Content-Length: %zd\r\n" \
+"Cache-Control: no-cache\r\n" \
+"\r\n" \
+"%s"
+
+
+#define SOAP_PASSPORT_SSO_URL "https://login.live.com/RST.srf"
+#define SOAP_PASSPORT_SSO_URL_MSN "https://msnia.login.live.com/pp550/RST.srf"
+
+#define SOAP_PASSPORT_SSO_PAYLOAD \
+"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
+   "xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" " \
+   "xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" " \
+   "xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" " \
+   "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" " \
+   "xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" " \
+   "xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" " \
+   "xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \
+   "<Header>" \
+       "<ps:AuthInfo " \
+           "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" " \
+           "Id=\"PPAuthInfo\">" \
+           "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \
+           "<ps:BinaryVersion>4</ps:BinaryVersion>" \
+           "<ps:UIVersion>1</ps:UIVersion>" \
+           "<ps:Cookies></ps:Cookies>" \
+           "<ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>" \
+       "</ps:AuthInfo>" \
+       "<wsse:Security>" \
+           "<wsse:UsernameToken Id=\"user\">" \
+               "<wsse:Username>%s</wsse:Username>" \
+               "<wsse:Password>%s</wsse:Password>" \
+           "</wsse:UsernameToken>" \
+       "</wsse:Security>" \
+   "</Header>" \
+   "<Body>" \
+       "<ps:RequestMultipleSecurityTokens " \
+           "xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" " \
+           "Id=\"RSTS\">" \
+           "<wst:RequestSecurityToken Id=\"RST0\">" \
+               "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+               "<wsp:AppliesTo>" \
+                   "<wsa:EndpointReference>" \
+                       "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \
+                   "</wsa:EndpointReference>" \
+               "</wsp:AppliesTo>" \
+           "</wst:RequestSecurityToken>" \
+           "<wst:RequestSecurityToken Id=\"RST1\">" \
+               "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+               "<wsp:AppliesTo>" \
+                   "<wsa:EndpointReference>" \
+                       "<wsa:Address>messengerclear.live.com</wsa:Address>" \
+                   "</wsa:EndpointReference>" \
+               "</wsp:AppliesTo>" \
+               "<wsse:PolicyReference URI=\"%s\"></wsse:PolicyReference>" \
+           "</wst:RequestSecurityToken>" \
+           "<wst:RequestSecurityToken Id=\"RST2\">" \
+               "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+               "<wsp:AppliesTo>" \
+                   "<wsa:EndpointReference>" \
+                       "<wsa:Address>contacts.msn.com</wsa:Address>" \
+                   "</wsa:EndpointReference>" \
+               "</wsp:AppliesTo>" \
+               "<wsse:PolicyReference xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"MBI\"></wsse:PolicyReference>" \
+           "</wst:RequestSecurityToken>" \
+           "<wst:RequestSecurityToken Id=\"RST3\">" \
+               "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+               "<wsp:AppliesTo>" \
+                   "<wsa:EndpointReference>" \
+                       "<wsa:Address>messengersecure.live.com</wsa:Address>" \
+                   "</wsa:EndpointReference>" \
+               "</wsp:AppliesTo>" \
+               "<wsse:PolicyReference xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"MBI_SSL\"></wsse:PolicyReference>" \
+           "</wst:RequestSecurityToken>" \
+       "</ps:RequestMultipleSecurityTokens>" \
+   "</Body>" \
+"</Envelope>"
+
+int msn_soap_passport_sso_request( struct im_connection *ic, const char *policy, const char *nonce );
+
+
+#define SOAP_OIM_SEND_URL "https://ows.messenger.msn.com/OimWS/oim.asmx"
+#define SOAP_OIM_SEND_ACTION "http://messenger.live.com/ws/2006/09/oim/Store2"
+
+#define SOAP_OIM_SEND_PAYLOAD \
+"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
+"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \
+"<soap:Header>" \
+  "<From memberName=\"%s\" friendlyName=\"=?utf-8?B?%s?=\" xml:lang=\"nl-nl\" proxy=\"MSNMSGR\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\" msnpVer=\"%s\" buildVer=\"%s\"/>" \
+  "<To memberName=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>" \
+  "<Ticket passport=\"%s\" appid=\"%s\" lockkey=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>" \
+  "<Sequence xmlns=\"http://schemas.xmlsoap.org/ws/2003/03/rm\">" \
+    "<Identifier xmlns=\"http://schemas.xmlsoap.org/ws/2002/07/utility\">http://messenger.msn.com</Identifier>" \
+    "<MessageNumber>%d</MessageNumber>" \
+  "</Sequence>" \
+"</soap:Header>" \
+"<soap:Body>" \
+  "<MessageType xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">text</MessageType>" \
+  "<Content xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">" \
+    "MIME-Version: 1.0\r\n" \
+    "Content-Type: text/plain; charset=UTF-8\r\n" \
+    "Content-Transfer-Encoding: base64\r\n" \
+    "X-OIM-Message-Type: OfflineMessage\r\n" \
+    "X-OIM-Run-Id: {F9A6C9DD-0D94-4E85-9CC6-F9D118CC1CAF}\r\n" \
+    "X-OIM-Sequence-Num: %d\r\n" \
+    "\r\n" \
+    "%s" \
+  "</Content>" \
+"</soap:Body>" \
+"</soap:Envelope>"
+
+int msn_soap_oim_send( struct im_connection *ic, const char *to, const char *msg );
+int msn_soap_oim_send_queue( struct im_connection *ic, GSList **msgq );
+
+
+#define SOAP_MEMLIST_URL "http://contacts.msn.com/abservice/SharingService.asmx"
+#define SOAP_MEMLIST_ACTION "http://www.msn.com/webservices/AddressBook/FindMembership"
+
+#define SOAP_MEMLIST_PAYLOAD \
+"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \
+  "<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \
+    "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+      "<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId>" \
+      "<IsMigration xmlns=\"http://www.msn.com/webservices/AddressBook\">false</IsMigration>" \
+      "<PartnerScenario xmlns=\"http://www.msn.com/webservices/AddressBook\">Initial</PartnerScenario>" \
+    "</ABApplicationHeader>" \
+    "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+      "<ManagedGroupRequest xmlns=\"http://www.msn.com/webservices/AddressBook\">false</ManagedGroupRequest>" \
+      "<TicketToken>%s</TicketToken>" \
+    "</ABAuthHeader>" \
+  "</soap:Header>" \
+  "<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \
+    "<FindMembership xmlns=\"http://www.msn.com/webservices/AddressBook\"><serviceFilter xmlns=\"http://www.msn.com/webservices/AddressBook\"><Types xmlns=\"http://www.msn.com/webservices/AddressBook\"><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Messenger</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Invitation</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">SocialNetwork</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Space</ServiceType><ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Profile</ServiceType></Types></serviceFilter>" \
+    "</FindMembership>" \
+  "</soap:Body>" \
+"</soap:Envelope>"
+
+int msn_soap_memlist_request( struct im_connection *ic );
+
+
+#define SOAP_ADDRESSBOOK_URL "http://contacts.msn.com/abservice/abservice.asmx"
+#define SOAP_ADDRESSBOOK_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll"
+
+#define SOAP_ADDRESSBOOK_PAYLOAD \
+"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">" \
+  "<soap:Header>" \
+    "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+      "<ApplicationId>CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId>" \
+      "<IsMigration>false</IsMigration>" \
+      "<PartnerScenario>Initial</PartnerScenario>" \
+    "</ABApplicationHeader>" \
+    "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+      "<ManagedGroupRequest>false</ManagedGroupRequest>" \
+      "<TicketToken>%s</TicketToken>" \
+    "</ABAuthHeader>" \
+  "</soap:Header>" \
+  "<soap:Body>" \
+    "<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+      "<abId>00000000-0000-0000-0000-000000000000</abId>" \
+      "<abView>Full</abView>" \
+      "<deltasOnly>false</deltasOnly>" \
+      "<lastChange>0001-01-01T00:00:00.0000000-08:00</lastChange>" \
+    "</ABFindAll>" \
+  "</soap:Body>" \
+"</soap:Envelope>"
+
+#define SOAP_AB_NAMECHANGE_ACTION "http://www.msn.com/webservices/AddressBook/ABContactUpdate"
+
+#define SOAP_AB_NAMECHANGE_PAYLOAD \
+"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">" \
+  "<soap:Header>" \
+    "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+      "<ApplicationId>CFE80F9D-180F-4399-82AB-413F33A1FA11</ApplicationId>" \
+      "<IsMigration>false</IsMigration>" \
+      "<PartnerScenario>Initial</PartnerScenario>" \
+    "</ABApplicationHeader>" \
+    "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+      "<ManagedGroupRequest>false</ManagedGroupRequest>" \
+      "<TicketToken>%s</TicketToken>" \
+    "</ABAuthHeader>" \
+  "</soap:Header>" \
+    "<soap:Body>" \
+        "<ABContactUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+            "<abId>00000000-0000-0000-0000-000000000000</abId>" \
+            "<contacts>" \
+                "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">" \
+                    "<contactInfo>" \
+                        "<contactType>Me</contactType>" \
+                        "<displayName>%s</displayName>" \
+                    "</contactInfo>" \
+                    "<propertiesChanged>DisplayName</propertiesChanged>" \
+                "</Contact>" \
+            "</contacts>" \
+        "</ABContactUpdate>" \
+    "</soap:Body>" \
+"</soap:Envelope>"
+
+int msn_soap_addressbook_request( struct im_connection *ic );
+int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new );
+
+
+#endif /* __SOAP_H__ */
Index: protocols/msn/tables.c
===================================================================
--- protocols/msn/tables.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/msn/tables.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -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                *
   \********************************************************************/
 
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/nogaim.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -326,12 +326,4 @@
 	imcb_log( ic, "Signing off.." );
 	
-	b_event_remove( ic->keepalive );
-	ic->keepalive = 0;
-	ic->acc->prpl->logout( ic );
-	b_event_remove( ic->inpa );
-	
-	g_free( ic->away );
-	ic->away = NULL;
-	
 	for( l = bee->users; l; )
 	{
@@ -344,4 +336,12 @@
 		l = next;
 	}
+	
+	b_event_remove( ic->keepalive );
+	ic->keepalive = 0;
+	ic->acc->prpl->logout( ic );
+	b_event_remove( ic->inpa );
+	
+	g_free( ic->away );
+	ic->away = NULL;
 	
 	query_del_by_conn( (irc_t*) ic->bee->ui_data, ic );
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ protocols/nogaim.h	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -194,4 +194,6 @@
 	   this info via imcb_log(). Implementing these are optional. */
 	void (* get_info)	(struct im_connection *, char *who);
+	/* set_my_name is *DEPRECATED*, not used by the UI anymore. Use the
+	   display_name setting instead. */
 	void (* set_my_name)	(struct im_connection *, char *name);
 	void (* set_name)	(struct im_connection *, char *who, char *name);
Index: storage_xml.c
===================================================================
--- storage_xml.c	(revision 136c2bb632715ab83710c93c7b339c5cca7d2679)
+++ storage_xml.c	(revision 07874bef9e9c4e6ace44e4d0605ce1aec89cad74)
@@ -320,4 +320,14 @@
 	else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 && xd->current_setting )
 	{
+		if( xd->current_account )
+		{
+			set_t *s = set_find( xd->current_set_head, xd->current_setting );
+			if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) )
+			{
+				g_free( xd->current_setting );
+				xd->current_setting = NULL;
+				return;
+			}
+		}
 		set_setstr( xd->current_set_head, xd->current_setting, (char*) text );
 		g_free( xd->current_setting );
