Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision daf544ac6a08ce2fc0f9bd2b07c70ea349a41e30)
+++ protocols/msn/msn.c	(revision c6fc24a81fa14cea4b3f497bbbb0e6e65a3cc6d4)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  * Copyright 2002-2012 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -98,4 +98,5 @@
 		g_free( md->lock_key );
 		g_free( md->pp_policy );
+		g_free( md->uuid );
 		
 		while( md->groups )
@@ -190,9 +191,27 @@
 		md->away_state = msn_away_state_list + 1;
 	
-	if( !msn_ns_write( ic, -1, "CHG %d %s\r\n", ++md->trId, md->away_state->code ) )
+	if( !msn_ns_write( ic, -1, "CHG %d %s %d:%02d\r\n", ++md->trId, md->away_state->code, MSN_CAP1, MSN_CAP2 ) )
 		return;
 	
-	uux = g_markup_printf_escaped( "<Data><PSM>%s</PSM><CurrentMedia></CurrentMedia>"
-	                               "</Data>", message ? message : "" );
+	uux = g_markup_printf_escaped( "<EndpointData><Capabilities>%d:%02d"
+	                               "</Capabilities></EndpointData>",
+	                               MSN_CAP1, MSN_CAP2 );
+	msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux );
+	g_free( uux );
+	
+	uux = g_markup_printf_escaped( "<PrivateEndpointData><EpName>%s</EpName>"
+	                               "<Idle>%s</Idle><ClientType>%d</ClientType>"
+	                               "<State>%s</State></PrivateEndpointData>",
+	                               md->uuid,
+	                               strcmp( md->away_state->code, "IDL" ) ? "false" : "true",
+	                               1, /* ? */
+	                               md->away_state->code );
+	msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux );
+	g_free( uux );
+	
+	uux = g_markup_printf_escaped( "<Data><DDP></DDP><PSM>%s</PSM>"
+	                               "<CurrentMedia></CurrentMedia>"
+	                               "<MachineGuid>%s</MachineGuid></Data>",
+	                               message ? message : "", md->uuid );
 	msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux );
 	g_free( uux );
@@ -232,11 +251,7 @@
 {
 	struct msn_switchboard *sb = msn_sb_by_chat( c );
-	char buf[1024];
 	
 	if( sb )
-	{
-		g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
-		msn_sb_write( sb, buf, strlen( buf ) );
-	}
+		msn_sb_write( sb, "CAL %d %s\r\n", ++sb->trId, who );
 }
 
@@ -246,5 +261,5 @@
 	
 	if( sb )
-		msn_sb_write( sb, "OUT\r\n", 5 );
+		msn_sb_write( sb, "OUT\r\n" );
 }
 
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision daf544ac6a08ce2fc0f9bd2b07c70ea349a41e30)
+++ protocols/msn/msn.h	(revision c6fc24a81fa14cea4b3f497bbbb0e6e65a3cc6d4)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  * Copyright 2002-2012 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -53,10 +53,18 @@
 */
 
+/* <= BitlBee 3.0.5
 #define MSNP11_PROD_KEY "ILTXC!4IXB5FB*PX"
 #define MSNP11_PROD_ID  "PROD0119GSJUC$18"
-#define MSNP_VER        "MSNP15"
-#define MSNP_BUILD      "8.5.1288"
+*/
+
+#define MSNP11_PROD_KEY "C1BX{V4W}Q3*10SM"
+#define MSNP11_PROD_ID  "PROD0120PW!CCV9@"
+#define MSNP_VER        "MSNP18"
+#define MSNP_BUILD      "14.0.8117.416"
 
 #define MSN_SB_NEW         -24062002
+
+#define MSN_CAP1        0xC000
+#define MSN_CAP2        0x0000
 
 #define MSN_MESSAGE_HEADERS "MIME-Version: 1.0\r\n" \
@@ -118,4 +126,5 @@
 	char *tokens[4];
 	char *lock_key, *pp_policy;
+	char *uuid;
 	
 	GSList *msgq, *grpq, *soapq;
@@ -222,5 +231,5 @@
 
 /* ns.c */
-int msn_ns_write( struct im_connection *ic, int fd, const char *fmt, ... );
+int msn_ns_write( struct im_connection *ic, int fd, const char *fmt, ... ) G_GNUC_PRINTF( 3, 4 );
 gboolean msn_ns_connect( struct im_connection *ic, struct msn_handler_data *handler, const char *host, int port );
 void msn_ns_close( struct msn_handler_data *handler );
@@ -228,4 +237,6 @@
 void msn_auth_got_contact_list( struct im_connection *ic );
 int msn_ns_finish_login( struct im_connection *ic );
+int msn_ns_sendmessage( struct im_connection *ic, struct bee_user *bu, const char *text );
+void msn_ns_oim_send_queue( struct im_connection *ic, GSList **msgq );
 
 /* msn_util.c */
@@ -242,4 +253,5 @@
 struct msn_group *msn_group_by_id( struct im_connection *ic, const char *id );
 int msn_ns_set_display_name( struct im_connection *ic, const char *value );
+const char *msn_normalize_handle( const char *handle );
 
 /* tables.c */
@@ -250,7 +262,7 @@
 
 /* sb.c */
-int msn_sb_write( struct msn_switchboard *sb, const char *fmt, ... );
+int msn_sb_write( struct msn_switchboard *sb, const char *fmt, ... ) G_GNUC_PRINTF( 2, 3 );;
 struct msn_switchboard *msn_sb_create( struct im_connection *ic, char *host, int port, char *key, int session );
-struct msn_switchboard *msn_sb_by_handle( struct im_connection *ic, char *handle );
+struct msn_switchboard *msn_sb_by_handle( struct im_connection *ic, const char *handle );
 struct msn_switchboard *msn_sb_by_chat( struct groupchat *c );
 struct msn_switchboard *msn_sb_spare( struct im_connection *ic );
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision daf544ac6a08ce2fc0f9bd2b07c70ea349a41e30)
+++ protocols/msn/msn_util.c	(revision c6fc24a81fa14cea4b3f497bbbb0e6e65a3cc6d4)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  * Copyright 2002-2012 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -537,2 +537,10 @@
 	return msn_ns_write( ic, -1, "PRP %d MFN %s\r\n", ++md->trId, fn );
 }
+
+const char *msn_normalize_handle( const char *handle )
+{
+	if( strncmp( handle, "1:", 2 ) == 0 )
+		return handle + 2;
+	else
+		return handle;
+}
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision daf544ac6a08ce2fc0f9bd2b07c70ea349a41e30)
+++ protocols/msn/ns.c	(revision c6fc24a81fa14cea4b3f497bbbb0e6e65a3cc6d4)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  * Copyright 2002-2012 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -25,7 +25,9 @@
 
 #include <ctype.h>
+#include <sys/utsname.h>
 #include "nogaim.h"
 #include "msn.h"
 #include "md5.h"
+#include "sha1.h"
 #include "soap.h"
 #include "xmltree.h"
@@ -110,4 +112,21 @@
 	handler->rxlen = 0;
 	handler->rxq = g_new0( char, 1 );
+	
+	if( md->uuid == NULL )
+	{
+		struct utsname name;
+		sha1_state_t sha[1];
+		
+		/* UUID == SHA1("BitlBee" + my hostname + MSN username) */
+		sha1_init( sha );
+		sha1_append( sha, (void*) "BitlBee", 7 );
+		if( uname( &name ) == 0 )
+		{
+			sha1_append( sha, (void*) name.nodename, strlen( name.nodename ) );
+		}
+		sha1_append( sha, (void*) ic->acc->user, strlen( ic->acc->user ) );
+		md->uuid = sha1_random_uuid( sha );
+		memcpy( md->uuid, "b171be3e", 8 ); /* :-P */
+	}
 	
 	if( msn_ns_write( ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER ) )
@@ -353,7 +372,9 @@
 		return st;
 	}
-	else if( strcmp( cmd[0], "ILN" ) == 0 )
+	else if( strcmp( cmd[0], "ILN" ) == 0 || strcmp( cmd[0], "NLN" ) == 0 )
 	{
 		const struct msn_away_state *st;
+		const char *handle;
+		int cap = 0;
 		
 		if( num_parts < 6 )
@@ -363,9 +384,20 @@
 			return( 0 );
 		}
-		
-		http_decode( cmd[5] );
-		imcb_rename_buddy( ic, cmd[3], cmd[5] );
-		
-		st = msn_away_state_by_code( cmd[2] );
+		/* ILN and NLN are more or less the same, except ILN has a trId
+		   at the start, and NLN has a capability field at the end. 
+		   Does ILN still exist BTW? */
+		if( cmd[0][1] == 'I' )
+			cmd ++;
+		else
+			cap = atoi( cmd[4] );
+
+		handle = msn_normalize_handle( cmd[2] );
+		if( strcmp( handle, ic->acc->user ) == 0 )
+			return 1; /* That's me! */
+		
+		http_decode( cmd[3] );
+		imcb_rename_buddy( ic, handle, cmd[3] );
+		
+		st = msn_away_state_by_code( cmd[1] );
 		if( !st )
 		{
@@ -374,46 +406,21 @@
 		}
 		
-		imcb_buddy_status( ic, cmd[3], OPT_LOGGED_IN | 
-		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ),
-		                   st->name, NULL );
-	}
-	else if( strcmp( cmd[0], "FLN" ) == 0 )
-	{
-		if( cmd[1] == NULL )
-			return 1;
-		
-		imcb_buddy_status( ic, cmd[1], 0, NULL, NULL );
-		
-		msn_sb_start_keepalives( msn_sb_by_handle( ic, cmd[1] ), TRUE );
-	}
-	else if( strcmp( cmd[0], "NLN" ) == 0 )
-	{
-		const struct msn_away_state *st;
-		int cap;
-		
-		if( num_parts < 6 )
-		{
-			imcb_error( ic, "Syntax error" );
-			imc_logout( ic, TRUE );
-			return( 0 );
-		}
-		
-		http_decode( cmd[4] );
-		cap = atoi( cmd[5] );
-		imcb_rename_buddy( ic, cmd[2], cmd[4] );
-		
-		st = msn_away_state_by_code( cmd[1] );
-		if( !st )
-		{
-			/* FIXME: Warn/Bomb about unknown away state? */
-			st = msn_away_state_list + 1;
-		}
-		
-		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN | 
+		imcb_buddy_status( ic, handle, OPT_LOGGED_IN | 
 		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ) |
 		                   ( cap & 1 ? OPT_MOBILE : 0 ),
 		                   st->name, NULL );
 		
-		msn_sb_stop_keepalives( msn_sb_by_handle( ic, cmd[2] ) );
+		msn_sb_stop_keepalives( msn_sb_by_handle( ic, handle ) );
+	}
+	else if( strcmp( cmd[0], "FLN" ) == 0 )
+	{
+		const char *handle;
+		
+		if( cmd[1] == NULL )
+			return 1;
+		
+		handle = msn_normalize_handle( cmd[1] );
+		imcb_buddy_status( ic, handle, 0, NULL, NULL );
+		msn_sb_start_keepalives( msn_sb_by_handle( ic, handle ), TRUE );
 	}
 	else if( strcmp( cmd[0], "RNG" ) == 0 )
@@ -462,5 +469,5 @@
 		else
 		{
-			sb->who = g_strdup( cmd[5] );
+			sb->who = g_strdup( msn_normalize_handle( cmd[5] ) );
 		}
 	}
@@ -555,6 +562,6 @@
 	{
 		/* Status message. */
-		if( num_parts >= 4 )
-			handler->msglen = atoi( cmd[3] );
+		if( num_parts >= 3 )
+			handler->msglen = atoi( cmd[2] );
 	}
 	else if( strcmp( cmd[0], "NOT" ) == 0 )
@@ -668,5 +675,55 @@
 			else if( g_strncasecmp( ct, "text/x-msmsgsactivemailnotification", 35 ) == 0 )
 			{
-				/* Sorry, but this one really is *USELESS* */
+			}
+			else if( g_strncasecmp( ct, "text/x-msmsgsinitialmdatanotification", 37 ) == 0 ||
+			         g_strncasecmp( ct, "text/x-msmsgsoimnotification", 28 ) == 0 )
+			{
+				/* We received an offline message. Or at least notification
+				   that there is one waiting for us. Fetching the message(s)
+				   and purging them from the server is a lot of SOAPy work
+				   not worth doing IMHO. Also I thought it was possible to
+				   have the notification server send them directly, I was
+				   pretty sure I saw Pidgin do it..
+				   
+				   At least give a notification for now, seems like a
+				   reasonable thing to do. Only problem is, they'll keep
+				   coming back at login time until you read them using a
+				   different client. :-( */
+				
+				char *xml = get_rfc822_header( body, "Mail-Data:", blen );
+				struct xt_node *md, *m;
+				
+				if( !xml )
+					return 1;
+				md = xt_from_string( xml );
+				if( !md )
+					return 1;
+				
+				for( m = md->children; ( m = xt_find_node( m, "M" ) ); m = m->next )
+				{
+					struct xt_node *e = xt_find_node( m->children, "E" );
+					struct xt_node *rt = xt_find_node( m->children, "RT" );
+					struct tm tp;
+					time_t msgtime = 0;
+					
+					if( !e || !e->text )
+						continue;
+					
+					memset( &tp, 0, sizeof( tp ) );
+					if( rt && rt->text &&
+					    sscanf( rt->text, "%4d-%2d-%2dT%2d:%2d:%2d.",
+					            &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
+					            &tp.tm_hour, &tp.tm_min, &tp.tm_sec ) == 6 )
+					{
+						tp.tm_year -= 1900;
+						tp.tm_mon --;
+						msgtime = mktime_utc( &tp );
+						
+					}
+					imcb_buddy_msg( ic, e->text, "<< \002BitlBee\002 - Received offline message. BitlBee can't show these. >>", 0, msgtime );
+				}
+				
+				g_free( xml );
+				xt_free_node( md );
 			}
 			else
@@ -688,5 +745,5 @@
 			psm_text = psm->text;
 		
-		imcb_buddy_status_msg( ic, cmd[1], psm_text );
+		imcb_buddy_status_msg( ic, msn_normalize_handle( cmd[1] ), psm_text );
 		xt_free_node( ubx );
 	}
@@ -757,5 +814,5 @@
 	if( token )
 	{
-		msn_ns_write( ic, -1, "USR %d SSO S %s %s\r\n", ++md->trId, md->tokens[0], token );
+		msn_ns_write( ic, -1, "USR %d SSO S %s %s {%s}\r\n", ++md->trId, md->tokens[0], token, md->uuid );
 	}
 	else
@@ -886,2 +943,50 @@
 	return 1;
 }
+
+int msn_ns_sendmessage( struct im_connection *ic, bee_user_t *bu, const char *text )
+{
+	struct msn_data *md = ic->proto_data;
+	char *buf;
+	
+	if( strncmp( text, "\r\r\r", 3 ) == 0 )
+		/* Err. Shouldn't happen but I guess it can. Don't send others
+		   any of the "SHAKE THAT THING" messages. :-D */
+		return 1;
+	
+	buf = g_strdup_printf( "%s%s", MSN_MESSAGE_HEADERS, text );
+	
+	if( msn_ns_write( ic, -1, "UUM %d %s %d %d %zd\r\n%s",
+	                          ++md->trId, bu->handle,
+	                          1, /* type == MSN offline message */
+	                          1, /* type == IM (not nudge/typing) */
+	                          strlen( buf ), buf ) )
+		return 1;
+	else
+		return 0;
+}
+
+void msn_ns_oim_send_queue( struct im_connection *ic, GSList **msgq )
+{
+	GSList *l;
+	
+	for( l = *msgq; l; l = l->next )
+	{
+		struct msn_message *m = l->data;
+		bee_user_t *bu = bee_user_by_handle( ic->bee, ic, m->who );
+		
+		if( bu )
+			if( !msn_ns_sendmessage( ic, bu, m->text ) )
+				return;
+	}
+	
+	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 );
+	}
+}
Index: protocols/msn/sb.c
===================================================================
--- protocols/msn/sb.c	(revision daf544ac6a08ce2fc0f9bd2b07c70ea349a41e30)
+++ protocols/msn/sb.c	(revision c6fc24a81fa14cea4b3f497bbbb0e6e65a3cc6d4)
@@ -2,5 +2,5 @@
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  * Copyright 2002-2012 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -122,5 +122,5 @@
 }
 
-struct msn_switchboard *msn_sb_by_handle( struct im_connection *ic, char *handle )
+struct msn_switchboard *msn_sb_by_handle( struct im_connection *ic, const char *handle )
 {
 	struct msn_data *md = ic->proto_data;
@@ -308,4 +308,5 @@
 	struct msn_switchboard *sb = data;
 	struct im_connection *ic;
+	struct msn_data *md;
 	char buf[1024];
 	
@@ -315,4 +316,5 @@
 	
 	ic = sb->ic;
+	md = ic->proto_data;
 	
 	if( source != sb->fd )
@@ -332,7 +334,7 @@
 	
 	if( sb->session == MSN_SB_NEW )
-		g_snprintf( buf, sizeof( buf ), "USR %d %s %s\r\n", ++sb->trId, ic->acc->user, sb->key );
+		g_snprintf( buf, sizeof( buf ), "USR %d %s;{%s} %s\r\n", ++sb->trId, ic->acc->user, md->uuid, sb->key );
 	else
-		g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, ic->acc->user, sb->key, sb->session );
+		g_snprintf( buf, sizeof( buf ), "ANS %d %s;{%s} %s %d\r\n", ++sb->trId, ic->acc->user, md->uuid, sb->key, sb->session );
 	
 	if( msn_sb_write( sb, "%s", buf ) )
@@ -453,16 +455,29 @@
 			char buf[1024];
 			
-			if( num == 1 )
+			/* For as much as I understand this MPOP stuff now, a
+			   switchboard has two (or more) roster entries per
+			   participant. One "bare JID" and one JID;UUID. Ignore
+			   the latter. */
+			if( !strchr( cmd[4], ';' ) )
 			{
-				g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session );
-				sb->chat = imcb_chat_new( ic, buf );
+				/* HACK: Since even 1:1 chats now have >2 participants
+				   (ourselves included) it gets hard to tell them apart
+				   from rooms. Let's hope this is enough: */
+				if( sb->chat == NULL && num != tot )
+				{
+					g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session );
+					sb->chat = imcb_chat_new( ic, buf );
+					
+					g_free( sb->who );
+					sb->who = NULL;
+				}
 				
-				g_free( sb->who );
-				sb->who = NULL;
-			}
-			
-			imcb_chat_add_buddy( sb->chat, cmd[4] );
-			
-			if( num == tot )
+				if( sb->chat )
+					imcb_chat_add_buddy( sb->chat, cmd[4] );
+			}
+			
+			/* We have the full roster, start showing the channel to
+			   the user. */
+			if( num == tot && sb->chat )
 			{
 				imcb_chat_add_buddy( sb->chat, ic->acc->user );
@@ -506,4 +521,8 @@
 			return( 0 );
 		}
+		
+		/* See IRO above. Handle "bare JIDs" only. */
+		if( strchr( cmd[1], ';' ) )
+			return 1;
 		
 		if( sb->who && g_strcasecmp( cmd[1], sb->who ) == 0 )
@@ -541,4 +560,8 @@
 			return( st );
 		}
+		else if( strcmp( cmd[1], ic->acc->user ) == 0 )
+		{
+			/* Well, gee thanks. Thanks for letting me know I've arrived.. */
+		}
 		else if( sb->who )
 		{
@@ -613,5 +636,5 @@
 			   The server will clean it up when it's idle for too long. */
 		}
-		else if( sb->chat )
+		else if( sb->chat && !strchr( cmd[1], ';' ) )
 		{
 			imcb_chat_remove_buddy( sb->chat, cmd[1], "" );
@@ -629,10 +652,7 @@
 		/* If the person is offline, send an offline message instead,
 		   and don't report an error. */
-		/* TODO: Support for OIMs that works. (#874) */
-		/*
 		if( num == 217 )
-			msn_soap_oim_send_queue( ic, &sb->msgq );
+			msn_ns_oim_send_queue( ic, &sb->msgq );
 		else
-		*/
 			imcb_error( ic, "Error reported by switchboard server: %s", err->text );
 		
