diff options
| author | Wilmer van der Gaast <wilmer@gaast.net> | 2012-10-01 23:51:39 +0100 | 
|---|---|---|
| committer | Wilmer van der Gaast <wilmer@gaast.net> | 2012-10-01 23:51:39 +0100 | 
| commit | 06aed9a22b0218740f8130486b797f12f36ba605 (patch) | |
| tree | f35780d108a8f5bd51eb3c6a34e98259434d4f18 | |
| parent | a992d7aa04ce3224429e3648c49abced2edf1b07 (diff) | |
| parent | 4c9d3777d99191786a449912989a66a44a038383 (diff) | |
Merge msnp18 branch. It's stable enough and really not that intrusive.
| -rw-r--r-- | protocols/msn/msn.c | 64 | ||||
| -rw-r--r-- | protocols/msn/msn.h | 25 | ||||
| -rw-r--r-- | protocols/msn/msn_util.c | 10 | ||||
| -rw-r--r-- | protocols/msn/ns.c | 246 | ||||
| -rw-r--r-- | protocols/msn/sb.c | 54 | 
5 files changed, 315 insertions, 84 deletions
| diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 03f28422..71570ce0 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -1,7 +1,7 @@    /********************************************************************\    * 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                *    \********************************************************************/  /* MSN module - Main file; functions to be called from BitlBee          */ @@ -97,6 +97,7 @@ static void msn_logout( struct im_connection *ic )  			g_free( md->tokens[i] );  		g_free( md->lock_key );  		g_free( md->pp_policy ); +		g_free( md->uuid );  		while( md->groups )  		{ @@ -138,6 +139,8 @@ static void msn_logout( struct im_connection *ic )  static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, int away )  { +	struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who ); +	struct msn_buddy_data *bd = bu ? bu->data : NULL;  	struct msn_switchboard *sb;  #ifdef DEBUG @@ -147,7 +150,11 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in  	}  	else  #endif -	if( ( sb = msn_sb_by_handle( ic, who ) ) ) +	if( bd && bd->flags & MSN_BUDDY_FED ) +	{ +		msn_ns_sendmessage( ic, bu, message ); +	} +	else if( ( sb = msn_sb_by_handle( ic, who ) ) )  	{  		return( msn_sb_sendmessage( sb, message ) );  	} @@ -189,11 +196,29 @@ static void msn_set_away( struct im_connection *ic, char *state, char *message )  	else if( ( md->away_state = msn_away_state_by_name( state ) ) == NULL )  		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 );  } @@ -231,13 +256,9 @@ static void msn_chat_msg( struct groupchat *c, char *message, int flags )  static void msn_chat_invite( struct groupchat *c, char *who, char *message )  {  	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 );  }  static void msn_chat_leave( struct groupchat *c ) @@ -245,7 +266,7 @@ static void msn_chat_leave( struct groupchat *c )  	struct msn_switchboard *sb = msn_sb_by_chat( c );  	if( sb ) -		msn_sb_write( sb, "OUT\r\n", 5 ); +		msn_sb_write( sb, "OUT\r\n" );  }  static struct groupchat *msn_chat_with( struct im_connection *ic, char *who ) @@ -339,8 +360,27 @@ static char *set_eval_display_name( set_t *set, char *value )  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 ); +	struct msn_buddy_data *bd; +	char *handle; +	 +	bd = bu->data = g_new0( struct msn_buddy_data, 1 );  	g_tree_insert( md->domaintree, bu->handle, bu ); +	 +	for( handle = bu->handle; isdigit( *handle ); handle ++ ); +	if( *handle == ':' ) +	{ +		/* Pass a nick hint so hopefully the stupid numeric prefix +		   won't show up to the user.  */ +		char *s = strchr( ++handle, '@' ); +		if( s ) +		{ +			handle = g_strndup( handle, s - handle ); +			imcb_buddy_nick_hint( bu->ic, bu->handle, handle ); +			g_free( handle ); +		} +		 +		bd->flags |= MSN_BUDDY_FED; +	}  }  static void msn_buddy_data_free( bee_user_t *bu ) diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index bf7cdfa8..1dcb0071 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -1,7 +1,7 @@    /********************************************************************\    * 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                *    \********************************************************************/  /* MSN module                                                           */ @@ -52,13 +52,21 @@  #define MSNP11_PROD_ID  "PROD01065C%ZFN6F"  */ +/* <= 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" \                              "Content-Type: text/plain; charset=UTF-8\r\n" \                              "User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \ @@ -117,6 +125,7 @@ struct msn_data  	int trId;  	char *tokens[4];  	char *lock_key, *pp_policy; +	char *uuid;  	GSList *msgq, *grpq, *soapq;  	GSList *switchboards; @@ -188,6 +197,7 @@ typedef enum  	MSN_BUDDY_RL = 8,  	MSN_BUDDY_PL = 16,  	MSN_BUDDY_ADL_SYNCED = 256, +	MSN_BUDDY_FED = 512,  } msn_buddy_flags_t;  struct msn_buddy_data @@ -221,12 +231,14 @@ extern GSList *msn_connections;  extern GSList *msn_switchboards;  /* 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 );  void msn_auth_got_passport_token( struct im_connection *ic, const char *token, const char *error );  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 */  int msn_logged_in( struct im_connection *ic ); @@ -241,6 +253,7 @@ gint msn_domaintree_cmp( gconstpointer a_, gconstpointer b_ );  struct msn_group *msn_group_by_name( struct im_connection *ic, const char *name );  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 */  const struct msn_away_state *msn_away_state_by_number( int number ); @@ -249,9 +262,9 @@ const struct msn_away_state *msn_away_state_by_name( char *name );  const struct msn_status_code *msn_status_by_number( int number );  /* 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 );  int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ); diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index d5a74a47..f2f67eb3 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -1,7 +1,7 @@    /********************************************************************\    * 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                *    \********************************************************************/  /* MSN module - Miscellaneous utilities                                 */ @@ -536,3 +536,11 @@ int msn_ns_set_display_name( struct im_connection *ic, const char *value )  	   and won't give proper feedback yet if it doesn't. */  	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; +} diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index a2f1b4bd..d9a558f9 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -1,7 +1,7 @@    /********************************************************************\    * 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                *    \********************************************************************/  /* MSN module - Notification server callbacks                           */ @@ -24,9 +24,11 @@  */  #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,6 +112,23 @@ static gboolean msn_ns_connected( gpointer data, gint source, b_input_condition  	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 ) )  	{  		handler->inpa = b_input_add( handler->fd, B_EV_IO_READ, msn_ns_callback, handler ); @@ -352,9 +371,11 @@ static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num  		g_free( resp );  		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 )  		{ @@ -362,59 +383,45 @@ static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num  			imc_logout( ic, TRUE );  			return( 0 );  		} +		/* 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[5] ); -		imcb_rename_buddy( ic, cmd[3], cmd[5] ); +		http_decode( cmd[3] ); +		imcb_rename_buddy( ic, handle, cmd[3] ); -		st = msn_away_state_by_code( cmd[2] ); +		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[3], OPT_LOGGED_IN |  -		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ), +		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, handle ) );  	}  	else if( strcmp( cmd[0], "FLN" ) == 0 )  	{ +		const char *handle; +		  		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 |  -		                   ( 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] ) ); +		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 )  	{ @@ -461,7 +468,7 @@ static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num  		}  		else  		{ -			sb->who = g_strdup( cmd[5] ); +			sb->who = g_strdup( msn_normalize_handle( cmd[5] ) );  		}  	}  	else if( strcmp( cmd[0], "OUT" ) == 0 ) @@ -554,8 +561,8 @@ static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num  	else if( strcmp( cmd[0], "UBX" ) == 0 )  	{  		/* 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 )  	{ @@ -564,6 +571,11 @@ static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num  		if( num_parts >= 2 )  			handler->msglen = atoi( cmd[1] );  	} +	else if( strcmp( cmd[0], "UBM" ) == 0 ) +	{ +		if( num_parts >= 7 ) +			handler->msglen = atoi( cmd[6] ); +	}  	else if( isdigit( cmd[0][0] ) )  	{  		int num = atoi( cmd[0] ); @@ -667,7 +679,57 @@ static int msn_ns_message( struct msn_handler_data *handler, char *msg, int msgl  			}  			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, 0 ); +				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  			{ @@ -687,7 +749,7 @@ static int msn_ns_message( struct msn_handler_data *handler, char *msg, int msgl  		    ( psm = xt_find_node( ubx->children, "PSM" ) ) )  			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 );  	}  	else if( strcmp( cmd[0], "ADL" ) == 0 ) @@ -715,6 +777,8 @@ static int msn_ns_message( struct msn_handler_data *handler, char *msg, int msgl  				    ( cn = xt_find_attr( c, "n" ) ) == NULL )  					continue; +				/* FIXME: Use "t" here, guess I should just add it +				   as a prefix like elsewhere in the protocol. */  				handle = g_strdup_printf( "%s@%s", cn, dn );  				if( !( ( bu = bee_user_by_handle( ic->bee, ic, handle ) ) ||  				       ( bu = bee_user_new( ic->bee, ic, handle, 0 ) ) ) ) @@ -740,8 +804,37 @@ static int msn_ns_message( struct msn_handler_data *handler, char *msg, int msgl  			}  		}  	} +	else if( strcmp( cmd[0], "UBM" ) == 0 ) +	{ +		/* This one will give us msgs from federated networks. Technically +		   it should also get us offline messages, but I don't know how +		   I can signal MSN servers to use it. */ +		char *ct, *handle; +		 +		if( strcmp( cmd[1], ic->acc->user ) == 0 ) +		{ +			/* With MPOP, you'll get copies of your own msgs from other +			   sessions. Discard those at least for now. */ +			return 1; +		} +		 +		ct = get_rfc822_header( msg, "Content-Type", msglen ); +		if( strncmp( ct, "text/plain", 10 ) != 0 ) +		{ +			/* Typing notification or something? */ +			g_free( ct ); +			return 1; +		} +		if( strcmp( cmd[2], "1" ) != 0 ) +			handle = g_strdup_printf( "%s:%s", cmd[2], cmd[1] ); +		else +			handle = g_strdup( cmd[1] ); +		 +		imcb_buddy_msg( ic, handle, body, 0, 0 ); +		g_free( handle ); +	} -	return( 1 ); +	return 1;  }  void msn_auth_got_passport_token( struct im_connection *ic, const char *token, const char *error ) @@ -756,7 +849,7 @@ void msn_auth_got_passport_token( struct im_connection *ic, const char *token, c  	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  	{ @@ -808,7 +901,7 @@ static gboolean msn_ns_send_adl_1( gpointer key, gpointer value, gpointer data )  	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_add_attr( c, "t", "1" ); /* FIXME: Network type, i.e. 32 for Y!MSG */  	xt_insert_child( d, c );  	/* Do this in batches of 100. */ @@ -885,3 +978,60 @@ int msn_ns_finish_login( struct im_connection *ic )  	return 1;  } + +int msn_ns_sendmessage( struct im_connection *ic, bee_user_t *bu, const char *text ) +{ +	struct msn_data *md = ic->proto_data; +	int type = 0; +	char *buf, *handle; +	 +	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; +	 +	/* This might be a federated contact. Get its network number, +	   prefixed to bu->handle with a colon. Default is 1. */ +	for( handle = bu->handle; isdigit( *handle ); handle ++ ) +		type = type * 10 + *handle - '0'; +	if( *handle == ':' ) +		handle ++; +	else +		type = 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, handle, type, +	                          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 ); +	} +} diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 45e74cb0..1cd1c743 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -1,7 +1,7 @@    /********************************************************************\    * 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                *    \********************************************************************/  /* MSN module - Switchboard server callbacks and utilities              */ @@ -121,7 +121,7 @@ struct msn_switchboard *msn_sb_create( struct im_connection *ic, char *host, int  	return( sb );  } -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;  	struct msn_switchboard *sb; @@ -307,6 +307,7 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond )  {  	struct msn_switchboard *sb = data;  	struct im_connection *ic; +	struct msn_data *md;  	char buf[1024];  	/* Are we still alive? */ @@ -314,6 +315,7 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond )  		return FALSE;  	ic = sb->ic; +	md = ic->proto_data;  	if( source != sb->fd )  	{ @@ -331,9 +333,9 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond )  	sb->handler->exec_message = msn_sb_message;  	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 ) )  		sb->inp = b_input_add( sb->fd, B_EV_IO_READ, msn_sb_callback, sb ); @@ -452,18 +454,31 @@ static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num  		{  			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; +				if( sb->chat ) +					imcb_chat_add_buddy( sb->chat, cmd[4] );  			} -			imcb_chat_add_buddy( sb->chat, cmd[4] ); -			 -			if( num == tot ) +			/* 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,6 +521,10 @@ static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num  			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 )  		{  			/* The user we wanted to talk to is finally there, let's send the queued messages then. */ @@ -540,6 +559,10 @@ static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num  			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 )  		{  			debug( "Converting chat with %s to a groupchat because %s joined the session.", sb->who, cmd[1] ); @@ -612,7 +635,7 @@ static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num  			   as a spare for a next conversation sounds more sane to me.  			   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], "" );  		} @@ -628,12 +651,9 @@ static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num  		/* 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 );  		if( err->flags & STATUS_SB_FATAL ) | 
