diff options
Diffstat (limited to 'protocols')
| -rw-r--r-- | protocols/bee.h | 1 | ||||
| -rw-r--r-- | protocols/bee_chat.c | 2 | ||||
| -rw-r--r-- | protocols/bee_user.c | 29 | ||||
| -rw-r--r-- | protocols/jabber/conference.c | 18 | ||||
| -rw-r--r-- | protocols/msn/Makefile | 2 | ||||
| -rw-r--r-- | protocols/msn/msn.c | 129 | ||||
| -rw-r--r-- | protocols/msn/msn.h | 122 | ||||
| -rw-r--r-- | protocols/msn/msn_util.c | 291 | ||||
| -rw-r--r-- | protocols/msn/ns.c | 640 | ||||
| -rw-r--r-- | protocols/msn/passport.c | 172 | ||||
| -rw-r--r-- | protocols/msn/passport.h | 113 | ||||
| -rw-r--r-- | protocols/msn/sb.c | 94 | ||||
| -rw-r--r-- | protocols/msn/soap.c | 1078 | ||||
| -rw-r--r-- | protocols/msn/soap.h | 350 | ||||
| -rw-r--r-- | protocols/msn/tables.c | 4 | ||||
| -rw-r--r-- | protocols/nogaim.c | 18 | ||||
| -rw-r--r-- | protocols/nogaim.h | 2 | ||||
| -rw-r--r-- | protocols/oscar/ssi.c | 2 | ||||
| -rw-r--r-- | protocols/purple/purple.c | 44 | ||||
| -rw-r--r-- | protocols/twitter/twitter.c | 15 | 
20 files changed, 2302 insertions, 824 deletions
| diff --git a/protocols/bee.h b/protocols/bee.h index b99c8de7..077c3661 100644 --- a/protocols/bee.h +++ b/protocols/bee.h @@ -150,6 +150,7 @@ void bee_group_free( bee_t *bee );   *   OPT_LOGGED_IN and OPT_AWAY.   * - '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. */  G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, guint32 flags, time_t sent_at ); diff --git a/protocols/bee_chat.c b/protocols/bee_chat.c index 0314cae5..349e0547 100644 --- a/protocols/bee_chat.c +++ b/protocols/bee_chat.c @@ -195,7 +195,7 @@ void imcb_chat_remove_buddy( struct groupchat *c, const char *handle, const char  		bu = bee_user_by_handle( bee, ic, handle );  	} -	if( bee->ui->chat_remove_user ) +	if( bee->ui->chat_remove_user && bu )  		bee->ui->chat_remove_user( bee, c, bu );  } diff --git a/protocols/bee_user.c b/protocols/bee_user.c index 33853a1e..4ea538a9 100644 --- a/protocols/bee_user.c +++ b/protocols/bee_user.c @@ -186,8 +186,13 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags,  	/* 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( bu->status == NULL && ( flags & OPT_MOBILE ) &&  	    set_getbool( &bee->set, "mobile_is_away" ) ) @@ -204,6 +209,28 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags,  	g_free( old );  } +/* 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 = message && *message ? g_strdup( message ) : NULL; +	 +	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 )  {  	bee_t *bee = ic->bee; diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index e04b9792..0c2db0b3 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -311,6 +311,15 @@ void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud  	struct jabber_chat *jc = chat ? chat->data : NULL;  	char *s; +	if( subject && chat ) +	{ +		s = bud ? strchr( bud->ext_jid, '/' ) : NULL; +		if( s ) *s = 0; +		imcb_chat_topic( chat, bud ? bud->ext_jid : NULL, subject->text_len > 0 ? +		                 subject->text : NULL, jabber_get_timestamp( node ) ); +		if( s ) *s = '/'; +	} +	  	if( bud == NULL || ( jc && ~jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me ) )  	{  		char *nick; @@ -365,15 +374,6 @@ void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud  		   now or just wait for the OS to do it. :-) */  		return;  	} -	 -	if( subject ) -	{ -		s = strchr( bud->ext_jid, '/' ); -		if( s ) *s = 0; -		imcb_chat_topic( chat, bud->ext_jid, subject->text_len > 0 ? -		                 subject->text : NULL, jabber_get_timestamp( node ) ); -		if( s ) *s = '/'; -	}  	if( body && body->text_len > 0 )  	{  		s = strchr( bud->ext_jid, '/' ); diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 068d7e98..8845d41b 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -12,7 +12,7 @@ SRCDIR := $(SRCDIR)protocols/msn/  endif  # [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 diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 29b7cecc..5b2c53f7 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-2004 Wilmer van der Gaast and others                * +  * Copyright 2002-2010 Wilmer van der Gaast and others                *    \********************************************************************/  /* MSN module - Main file; functions to be called from BitlBee          */ @@ -24,6 +24,7 @@  */  #include "nogaim.h" +#include "soap.h"  #include "msn.h"  int msn_chat_id; @@ -34,10 +35,15 @@ static char *set_eval_display_name( set_t *set, char *value );  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;  }  static void msn_login( account_t *acc ) @@ -46,7 +52,6 @@ static void msn_login( account_t *acc )  	struct msn_data *md = g_new0( struct msn_data, 1 );  	ic->proto_data = md; -	md->fd = -1;  	if( strchr( acc->user, '@' ) == NULL )  	{ @@ -55,26 +60,22 @@ static void msn_login( account_t *acc )  		return;  	} -	imcb_log( ic, "Connecting" ); -	 -	md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, ic ); -	if( md->fd < 0 ) -	{ -		imcb_error( ic, "Could not connect to server" ); -		imc_logout( ic, TRUE ); -		return; -	} -	  	md->ic = ic;  	md->away_state = msn_away_state_list; +	md->domaintree = g_tree_new( msn_domaintree_cmp ); +	md->ns->fd = -1; -	msn_connections = g_slist_append( msn_connections, ic ); +	msn_connections = g_slist_prepend( msn_connections, ic ); +	 +	imcb_log( ic, "Connecting" ); +	msn_ns_connect( ic, md->ns, MSN_NS_HOST, MSN_NS_PORT );  }  static void msn_logout( struct im_connection *ic )  {  	struct msn_data *md = ic->proto_data;  	GSList *l; +	int i;  	if( md )  	{ @@ -84,24 +85,30 @@ static void msn_logout( struct im_connection *ic )  		}  		*/ -		if( md->fd >= 0 ) -			closesocket( md->fd ); -		 -		if( md->handler ) -		{ -			if( md->handler->rxq ) g_free( md->handler->rxq ); -			if( md->handler->cmd_text ) g_free( md->handler->cmd_text ); -			g_free( md->handler ); -		} +		msn_ns_close( md->ns );  		while( md->switchboards )  			msn_sb_destroy( md->switchboards->data );  		msn_msgq_purge( ic, &md->msgq ); +		msn_soapq_flush( ic, FALSE ); +		 +		for( i = 0; i < sizeof( md->tokens ) / sizeof( md->tokens[0] ); i ++ ) +			g_free( md->tokens[i] ); +		g_free( md->lock_key ); +		g_free( md->pp_policy ); -		while( md->groupcount > 0 ) -			g_free( md->grouplist[--md->groupcount] ); -		g_free( md->grouplist ); +		while( md->groups ) +		{ +			struct msn_group *mg = md->groups->data; +			g_free( mg->id ); +			g_free( mg->name ); +			g_free( mg ); +			md->groups = g_slist_remove( md->groups, mg ); +		} +		 +		g_tree_destroy( md->domaintree ); +		md->domaintree = NULL;  		while( md->grpq )  		{ @@ -133,8 +140,7 @@ static int msn_buddy_msg( struct im_connection *ic, char *who, char *message, in  #ifdef DEBUG  	if( strcmp( who, "raw" ) == 0 )  	{ -		msn_write( ic, message, strlen( message ) ); -		msn_write( ic, "\r\n", 2 ); +		msn_ns_write( ic, -1, "%s\r\n", message );  	}  	else  #endif @@ -172,7 +178,7 @@ static GList *msn_away_states( struct im_connection *ic )  static void msn_set_away( struct im_connection *ic, char *state, char *message )  { -	char buf[1024]; +	char *uux;  	struct msn_data *md = ic->proto_data;  	if( state == NULL ) @@ -180,13 +186,13 @@ 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; -	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_ns_write( ic, -1, "CHG %d %s\r\n", ++md->trId, md->away_state->code ) ) +		return; +	 +	uux = g_markup_printf_escaped( "<Data><PSM>%s</PSM><CurrentMedia></CurrentMedia>" +	                               "</Data>", message ? message : "" ); +	msn_ns_write( ic, -1, "UUX %d %zd\r\n%s", ++md->trId, strlen( uux ), uux ); +	g_free( uux );  }  static void msn_get_info(struct im_connection *ic, char *who)  @@ -199,14 +205,14 @@ static void msn_add_buddy( struct im_connection *ic, char *who, char *group )  {  	struct bee_user *bu = bee_user_by_handle( ic->bee, ic, who ); -	msn_buddy_list_add( ic, "FL", who, who, group ); +	msn_buddy_list_add( ic, MSN_BUDDY_FL, who, who, group );  	if( bu && bu->group ) -		msn_buddy_list_remove( ic, "FL", who, bu->group->name ); +		msn_buddy_list_remove( ic, MSN_BUDDY_FL, who, bu->group->name );  }  static void msn_remove_buddy( struct im_connection *ic, char *who, char *group )  { -	msn_buddy_list_remove( ic, "FL", who, NULL ); +	msn_buddy_list_remove( ic, MSN_BUDDY_FL, who, NULL );  }  static void msn_chat_msg( struct groupchat *c, char *message, int flags ) @@ -266,24 +272,24 @@ static struct groupchat *msn_chat_with( struct im_connection *ic, char *who )  static void msn_keepalive( struct im_connection *ic )  { -	msn_write( ic, "PNG\r\n", strlen( "PNG\r\n" ) ); +	msn_ns_write( ic, -1, "PNG\r\n" );  }  static void msn_add_permit( struct im_connection *ic, char *who )  { -	msn_buddy_list_add( ic, "AL", who, who, NULL ); +	msn_buddy_list_add( ic, MSN_BUDDY_AL, who, who, NULL );  }  static void msn_rem_permit( struct im_connection *ic, char *who )  { -	msn_buddy_list_remove( ic, "AL", who, NULL ); +	msn_buddy_list_remove( ic, MSN_BUDDY_AL, who, NULL );  }  static void msn_add_deny( struct im_connection *ic, char *who )  {  	struct msn_switchboard *sb; -	msn_buddy_list_add( ic, "BL", who, who, NULL ); +	msn_buddy_list_add( ic, MSN_BUDDY_BL, who, who, NULL );  	/* If there's still a conversation with this person, close it. */  	if( ( sb = msn_sb_by_handle( ic, who ) ) ) @@ -294,7 +300,7 @@ static void msn_add_deny( struct im_connection *ic, char *who )  static void msn_rem_deny( struct im_connection *ic, char *who )  { -	msn_buddy_list_remove( ic, "BL", who, NULL ); +	msn_buddy_list_remove( ic, MSN_BUDDY_BL, who, NULL );  }  static int msn_send_typing( struct im_connection *ic, char *who, int typing ) @@ -313,10 +319,7 @@ static char *set_eval_display_name( set_t *set, char *value )  {  	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;  	if( strlen( value ) > 129 )  	{ @@ -324,10 +327,26 @@ static char *set_eval_display_name( set_t *set, char *value )  		return NULL;  	} -	/* 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; +	if( md->flags & MSN_GOT_PROFILE_DN ) +		imcb_log( ic, "Warning: Persistent name changes for this account have to be done " +		              "in the profile. BitlBee doesn't currently support this." ); +	 +	msn_soap_addressbook_set_display_name( ic, value ); +	return msn_ns_set_display_name( ic, value ) ? 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 );  }  void msn_initmodule() @@ -343,7 +362,6 @@ void msn_initmodule()  	ret->away_states = msn_away_states;  	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;  	ret->chat_msg = msn_chat_msg; @@ -357,6 +375,9 @@ void msn_initmodule()  	ret->rem_deny = msn_rem_deny;  	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;  	register_protocol(ret); diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 31683cb5..829bbd4c 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-2004 Wilmer van der Gaast and others                * +  * Copyright 2002-2010 Wilmer van der Gaast and others                *    \********************************************************************/  /* MSN module                                                           */ @@ -38,8 +38,23 @@  #define debug( text... )  #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" +*/ + +#define MSN_NS_HOST "messenger.hotmail.com" +#define MSN_NS_PORT 1863 + +/* Some other version. +#define MSNP11_PROD_KEY "O4BG@C7BWLYQX?5G" +#define MSNP11_PROD_ID  "PROD01065C%ZFN6F" +*/ + +#define MSNP11_PROD_KEY "ILTXC!4IXB5FB*PX" +#define MSNP11_PROD_ID  "PROD0119GSJUC$18" +#define MSNP_VER        "MSNP15" +#define MSNP_BUILD      "8.5.1288"  #define MSN_SB_NEW         -24062002 @@ -60,31 +75,61 @@  #define PROFILE_URL "http://members.msn.com/" +typedef enum +{ +	MSN_GOT_PROFILE = 1, +	MSN_GOT_PROFILE_DN = 2, +	MSN_DONE_ADL = 4, +	MSN_REAUTHING = 8, +} msn_flags_t; + +struct msn_handler_data +{ +	int fd, inpa; +	int rxlen; +	char *rxq; +	 +	int msglen; +	char *cmd_text; +	 +	/* Either ic or sb */ +	gpointer data; +	 +	int (*exec_command) ( struct msn_handler_data *handler, char **cmd, int count ); +	int (*exec_message) ( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int count ); +}; +  struct msn_data  {  	struct im_connection *ic; -	int fd; -	struct msn_handler_data *handler; +	struct msn_handler_data ns[1]; +	msn_flags_t flags;  	int trId; +	char *tokens[4]; +	char *lock_key, *pp_policy; -	GSList *msgq, *grpq; +	GSList *msgq, *grpq, *soapq;  	GSList *switchboards;  	int sb_failures;  	time_t first_sb_failure; -	GSList *filetransfers;  	const struct msn_away_state *away_state; -	int buddycount; -	int groupcount; -	char **grouplist; +	GSList *groups; +	 +	/* Mostly used for sending the ADL command; since MSNP13 the client +	   is responsible for downloading the contact list and then sending +	   it to the MSNP server. */ +	GTree *domaintree; +	int adl_todo;  };  struct msn_switchboard  {  	struct im_connection *ic; +	/* The following two are also in the handler. TODO: Clean up. */  	int fd;  	gint inp;  	struct msn_handler_data *handler; @@ -126,19 +171,26 @@ struct msn_groupadd  	char *group;  }; -struct msn_handler_data +typedef enum  { -	int fd; -	int rxlen; -	char *rxq; -	 -	int msglen; -	char *cmd_text; -	 -	gpointer data; -	 -	int (*exec_command) ( gpointer data, char **cmd, int count ); -	int (*exec_message) ( gpointer data, char *msg, int msglen, char **cmd, int count ); +	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_ADL_SYNCED = 256, +} msn_buddy_flags_t; + +struct msn_buddy_data +{ +	char *cid; +	msn_buddy_flags_t flags; +}; + +struct msn_group +{ +	char *name; +	char *id;  };  /* Bitfield values for msn_status_code.flags */ @@ -160,20 +212,27 @@ extern GSList *msn_connections;  extern GSList *msn_switchboards;  /* ns.c */ -gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ); +int msn_ns_write( struct im_connection *ic, int fd, const char *fmt, ... ); +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 );  /* msn_util.c */ -int msn_write( struct im_connection *ic, char *s, int len );  int msn_logged_in( struct im_connection *ic ); -int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ); -int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who, const char *group ); -void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname ); +int msn_buddy_list_add( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *realname_, const char *group ); +int msn_buddy_list_remove( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *group ); +void msn_buddy_ask( bee_user_t *bu );  char *msn_findheader( char *text, char *header, int len );  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_ ); +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 );  /* tables.c */  const struct msn_away_state *msn_away_state_by_number( int number ); @@ -182,7 +241,7 @@ 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, char *s, int len ); +int msn_sb_write( struct msn_switchboard *sb, const char *fmt, ... );  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_chat( struct groupchat *c ); @@ -195,7 +254,4 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m );  void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial );  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 diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 23447403..7fa68915 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-2004 Wilmer van der Gaast and others                * +  * Copyright 2002-2010 Wilmer van der Gaast and others                *    \********************************************************************/  /* MSN module - Miscellaneous utilities                                 */ @@ -25,37 +25,41 @@  #include "nogaim.h"  #include "msn.h" +#include "md5.h" +#include "soap.h"  #include <ctype.h> -int msn_write( struct im_connection *ic, char *s, int len ) +int msn_logged_in( struct im_connection *ic )  { -	struct msn_data *md = ic->proto_data; -	int st; -	 -	st = write( md->fd, s, len ); -	if( st != len ) -	{ -		imcb_error( ic, "Short write() to main server" ); -		imc_logout( ic, TRUE ); -		return 0; -	} +	imcb_connected( ic ); -	return 1; +	return( 0 );  } -int msn_logged_in( struct im_connection *ic ) +static char *adlrml_entry( const char *handle_, msn_buddy_flags_t list )  { -	imcb_connected( ic ); +	char *domain, handle[strlen(handle_)+1]; -	return( 0 ); +	strcpy( handle, handle_ ); +	if( ( domain = strchr( handle, '@' ) ) ) +		*(domain++) = '\0'; +	else +		return NULL; +	 +	return g_markup_printf_escaped( "<ml><d n=\"%s\"><c n=\"%s\" l=\"%d\" t=\"1\"/></d></ml>", +		domain, handle, list );  } -int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *who, const char *realname_, const char *group ) +int msn_buddy_list_add( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *realname, const char *group )  {  	struct msn_data *md = ic->proto_data; -	char buf[1024], *realname, groupid[8]; +	char groupid[8]; +	bee_user_t *bu; +	struct msn_buddy_data *bd; +	char *adl;  	*groupid = '\0'; +#if 0  	if( group )  	{  		int i; @@ -86,9 +90,10 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *  			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 ) );  			}  			else @@ -100,20 +105,42 @@ int msn_buddy_list_add( struct im_connection *ic, const char *list, const char *  			}  		}  	} +#endif +	 +	if( !( ( bu = bee_user_by_handle( ic->bee, ic, who ) ) || +	       ( bu = bee_user_new( ic->bee, ic, who, 0 ) ) ) || +	    !( bd = bu->data ) || bd->flags & list ) +		return 1; +	 +	bd->flags |= list; -	realname = msn_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 ); +	if( list == MSN_BUDDY_FL ) +		msn_soap_ab_contact_add( ic, bu ); +	else +		msn_soap_memlist_edit( ic, who, TRUE, list ); -	return msn_write( ic, buf, strlen( buf ) ); +	if( ( adl = adlrml_entry( who, list ) ) ) +	{ +		int st = msn_ns_write( ic, -1, "ADL %d %zd\r\n%s", +		                       ++md->trId, strlen( adl ), adl ); +		g_free( adl ); +		 +		return st; +	} +	 +	return 1;  } -int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who, const char *group ) +int msn_buddy_list_remove( struct im_connection *ic, msn_buddy_flags_t list, const char *who, const char *group )  {  	struct msn_data *md = ic->proto_data; -	char buf[1024], groupid[8]; +	char groupid[8]; +	bee_user_t *bu; +	struct msn_buddy_data *bd; +	char *adl;  	*groupid = '\0'; +#if 0  	if( group )  	{  		int i; @@ -124,12 +151,29 @@ int msn_buddy_list_remove( struct im_connection *ic, char *list, const char *who  				break;  			}  	} +#endif -	g_snprintf( buf, sizeof( buf ), "REM %d %s %s%s\r\n", ++md->trId, list, who, groupid ); -	if( msn_write( ic, buf, strlen( buf ) ) ) -		return( 1 ); +	if( !( bu = bee_user_by_handle( ic->bee, ic, who ) ) || +	    !( bd = bu->data ) || !( bd->flags & list ) ) +		return 1; -	return( 0 ); +	bd->flags &= ~list; +	 +	if( list == MSN_BUDDY_FL ) +		msn_soap_ab_contact_del( ic, bu ); +	else +		msn_soap_memlist_edit( ic, who, FALSE, list ); +	 +	if( ( adl = adlrml_entry( who, list ) ) ) +	{ +		int st = msn_ns_write( ic, -1, "RML %d %zd\r\n%s", +		                       ++md->trId, strlen( adl ), adl ); +		g_free( adl ); +		 +		return st; +	} +	 +	return 1;  }  struct msn_buddy_ask_data @@ -143,7 +187,7 @@ static void msn_buddy_ask_yes( void *data )  {  	struct msn_buddy_ask_data *bla = data; -	msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname, NULL ); +	msn_buddy_list_add( bla->ic, MSN_BUDDY_AL, bla->handle, bla->realname, NULL );  	imcb_ask_add( bla->ic, bla->handle, NULL ); @@ -156,26 +200,31 @@ static void msn_buddy_ask_no( void *data )  {  	struct msn_buddy_ask_data *bla = data; -	msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname, NULL ); +	msn_buddy_list_add( bla->ic, MSN_BUDDY_BL, bla->handle, bla->realname, NULL );  	g_free( bla->handle );  	g_free( bla->realname );  	g_free( bla );  } -void msn_buddy_ask( struct im_connection *ic, char *handle, char *realname ) +void msn_buddy_ask( bee_user_t *bu )  { -	struct msn_buddy_ask_data *bla = g_new0( struct msn_buddy_ask_data, 1 ); +	struct msn_buddy_ask_data *bla; +	struct msn_buddy_data *bd = bu->data;  	char buf[1024]; -	bla->ic = ic; -	bla->handle = g_strdup( handle ); -	bla->realname = g_strdup( realname ); +	if( ( bd->flags & 30 ) != 8 && ( bd->flags & 30 ) != 16 ) +		return; +	 +	bla = g_new0( struct msn_buddy_ask_data, 1 ); +	bla->ic = bu->ic; +	bla->handle = g_strdup( bu->handle ); +	bla->realname = g_strdup( bu->fullname );  	g_snprintf( buf, sizeof( buf ),  	            "The user %s (%s) wants to add you to his/her buddy list.", -	            handle, realname ); -	imcb_ask( ic, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no ); +	            bu->handle, bu->fullname ); +	imcb_ask( bu->ic, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no );  }  char *msn_findheader( char *text, char *header, int len ) @@ -279,6 +328,12 @@ int msn_handler( struct msn_handler_data *h )  	if( st <= 0 )  		return( -1 ); +	if( getenv( "BITLBEE_DEBUG" ) ) +	{ +		write( 2, "->C:", 4 ); +		write( 2, h->rxq + h->rxlen - st, st ); +	} +	  	while( st )  	{  		int i; @@ -295,7 +350,7 @@ int msn_handler( struct msn_handler_data *h )  					cmd_text = g_strndup( h->rxq, i );  					cmd = msn_linesplit( cmd_text );  					for( count = 0; cmd[count]; count ++ ); -					st = h->exec_command( h->data, cmd, count ); +					st = h->exec_command( h, cmd, count );  					g_free( cmd_text );  					/* If the connection broke, don't continue. We don't even exist anymore. */ @@ -330,7 +385,7 @@ int msn_handler( struct msn_handler_data *h )  			cmd = msn_linesplit( h->cmd_text );  			for( count = 0; cmd[count]; count ++ ); -			st = h->exec_message( h->data, msg, h->msglen, cmd, count ); +			st = h->exec_message( h, msg, h->msglen, cmd, count );  			g_free( msg );  			g_free( h->cmd_text );  			h->cmd_text = NULL; @@ -366,32 +421,6 @@ int msn_handler( struct msn_handler_data *h )  	return( 1 );  } -/* 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 )  {  	struct msn_message *m; @@ -432,14 +461,130 @@ void msn_msgq_purge( struct im_connection *ic, GSList **list )  	g_string_free( ret, TRUE );  } -gboolean msn_set_display_name( struct im_connection *ic, const char *rawname ) +/* 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; +} + +struct msn_group *msn_group_by_name( struct im_connection *ic, const char *name )  { -	char *fn = msn_http_encode( rawname );  	struct msn_data *md = ic->proto_data; -	char buf[1024]; +	GSList *l; +	 +	for( l = md->groups; l; l = l->next ) +	{ +		struct msn_group *mg = l->data; +		 +		if( g_strcasecmp( mg->name, name ) == 0 ) +			return mg; +	} +	 +	return NULL; +} + +struct msn_group *msn_group_by_id( struct im_connection *ic, const char *id ) +{ +	struct msn_data *md = ic->proto_data; +	GSList *l; +	 +	for( l = md->groups; l; l = l->next ) +	{ +		struct msn_group *mg = l->data; +		 +		if( g_strcasecmp( mg->id, id ) == 0 ) +			return mg; +	} +	 +	return NULL; +} + +int msn_ns_set_display_name( struct im_connection *ic, const char *value ) +{ +	struct msn_data *md = ic->proto_data; +	char fn[strlen(value)*3+1]; -	g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn ); -	g_free( fn ); +	strcpy( fn, value ); +	http_encode( fn ); -	return msn_write( ic, buf, strlen( buf ) ) != 0; +	/* 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_ns_write( ic, -1, "PRP %d MFN %s\r\n", ++md->trId, fn );  } diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 0be9e727..d64a71ac 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-2004 Wilmer van der Gaast and others                * +  * Copyright 2002-2010 Wilmer van der Gaast and others                *    \********************************************************************/  /* MSN module - Notification server callbacks                           */ @@ -26,68 +26,122 @@  #include <ctype.h>  #include "nogaim.h"  #include "msn.h" -#include "passport.h"  #include "md5.h" +#include "soap.h" +#include "xmltree.h" +static gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond );  static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond ); -static int msn_ns_command( gpointer data, char **cmd, int num_parts ); -static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ); +static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num_parts ); +static int msn_ns_message( struct msn_handler_data *handler, 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_start( struct im_connection *ic ); +static void msn_ns_send_adl( struct im_connection *ic ); -gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ) +int msn_ns_write( struct im_connection *ic, int fd, const char *fmt, ... )  { -	struct im_connection *ic = data; -	struct msn_data *md; -	char s[1024]; +	struct msn_data *md = ic->proto_data; +	va_list params; +	char *out; +	size_t len; +	int st; -	if( !g_slist_find( msn_connections, ic ) ) -		return FALSE; +	va_start( params, fmt ); +	out = g_strdup_vprintf( fmt, params ); +	va_end( params ); -	if( source == -1 ) +	if( fd < 0 ) +		fd = md->ns->fd; +	 +	if( getenv( "BITLBEE_DEBUG" ) ) +		fprintf( stderr, "->NS%d:%s", fd, out ); +	 +	len = strlen( out ); +	st = write( fd, out, len ); +	g_free( out ); +	if( st != len ) +	{ +		imcb_error( ic, "Short write() to main server" ); +		imc_logout( ic, TRUE ); +		return 0; +	} +	 +	return 1; +} + +gboolean msn_ns_connect( struct im_connection *ic, struct msn_handler_data *handler, const char *host, int port ) +{ +	if( handler->fd >= 0 ) +		closesocket( handler->fd ); +	 +	handler->exec_command = msn_ns_command; +	handler->exec_message = msn_ns_message; +	handler->data = ic; +	handler->fd = proxy_connect( host, port, msn_ns_connected, handler ); +	if( handler->fd < 0 )  	{  		imcb_error( ic, "Could not connect to server" );  		imc_logout( ic, TRUE );  		return FALSE;  	} +	return TRUE; +} + +static gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ) +{ +	struct msn_handler_data *handler = data; +	struct im_connection *ic = handler->data; +	struct msn_data *md; +	 +	if( !g_slist_find( msn_connections, ic ) ) +		return FALSE; +	  	md = ic->proto_data; -	if( !md->handler ) -	{ -		md->handler = g_new0( struct msn_handler_data, 1 ); -		md->handler->data = ic; -		md->handler->exec_command = msn_ns_command; -		md->handler->exec_message = msn_ns_message; -	} -	else +	if( source == -1 )  	{ -		if( md->handler->rxq ) -			g_free( md->handler->rxq ); -		 -		md->handler->rxlen = 0; +		imcb_error( ic, "Could not connect to server" ); +		imc_logout( ic, TRUE ); +		return FALSE;  	} -	md->handler->fd = md->fd; -	md->handler->rxq = g_new0( char, 1 ); +	g_free( handler->rxq ); +	handler->rxlen = 0; +	handler->rxq = g_new0( char, 1 ); -	g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId ); -	if( msn_write( ic, s, strlen( s ) ) ) +	if( msn_ns_write( ic, source, "VER %d %s CVR0\r\n", ++md->trId, MSNP_VER ) )  	{ -		ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic ); +		handler->inpa = b_input_add( handler->fd, B_EV_IO_READ, msn_ns_callback, handler );  		imcb_log( ic, "Connected to server, waiting for reply" );  	}  	return FALSE;  } +void msn_ns_close( struct msn_handler_data *handler ) +{ +	if( handler->fd >= 0 ) +	{ +		closesocket( handler->fd ); +		b_event_remove( handler->inpa ); +	} +	 +	handler->fd = handler->inpa = -1; +	g_free( handler->rxq ); +	g_free( handler->cmd_text ); +	 +	handler->rxlen = 0; +	handler->rxq = NULL; +	handler->cmd_text = NULL; +} +  static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition cond )  { -	struct im_connection *ic = data; -	struct msn_data *md = ic->proto_data; +	struct msn_handler_data *handler = data; +	struct im_connection *ic = handler->data; -	if( msn_handler( md->handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */ +	if( msn_handler( handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */  	{  		imcb_error( ic, "Error while reading from server" );  		imc_logout( ic, TRUE ); @@ -98,11 +152,10 @@ static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition c  		return TRUE;  } -static int msn_ns_command( gpointer data, char **cmd, int num_parts ) +static int msn_ns_command( struct msn_handler_data *handler, char **cmd, int num_parts )  { -	struct im_connection *ic = data; +	struct im_connection *ic = handler->data;  	struct msn_data *md = ic->proto_data; -	char buf[1024];  	if( num_parts == 0 )  	{ @@ -112,33 +165,30 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	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" );  			imc_logout( ic, FALSE );  			return( 0 );  		} -		g_snprintf( buf, sizeof( buf ), "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n", -		                                ++md->trId, ic->acc->user ); -		return( msn_write( ic, buf, strlen( buf ) ) ); +		return( msn_ns_write( ic, handler->fd, "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n", +		                      ++md->trId, ic->acc->user ) );  	}  	else if( strcmp( cmd[0], "CVR" ) == 0 )  	{  		/* 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 ); -		return( msn_write( ic, buf, strlen( buf ) ) ); +		return msn_ns_write( ic, handler->fd, "USR %d SSO I %s\r\n", ++md->trId, ic->acc->user );  	}  	else if( strcmp( cmd[0], "XFR" ) == 0 )  	{  		char *server;  		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 ); -			ic->inpa = 0; -			closesocket( md->fd ); +			b_event_remove( handler->inpa ); +			handler->inpa = -1;  			server = strchr( cmd[3], ':' );  			if( !server ) @@ -152,10 +202,9 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  			server = cmd[3];  			imcb_log( ic, "Transferring to other server" ); -			 -			md->fd = proxy_connect( server, port, msn_ns_connected, ic ); +			return msn_ns_connect( ic, handler, server, port );  		} -		else if( num_parts == 6 && strcmp( cmd[2], "SB" ) == 0 ) +		else if( num_parts >= 6 && strcmp( cmd[2], "SB" ) == 0 )  		{  			struct msn_switchboard *sb; @@ -219,27 +268,17 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	}  	else if( strcmp( cmd[0], "USR" ) == 0 )  	{ -		if( num_parts == 5 && strcmp( cmd[2], "TWN" ) == 0 && strcmp( cmd[3], "S" ) == 0 ) +		if( num_parts >= 6 && strcmp( cmd[2], "SSO" ) == 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 ); -			} +			g_free( md->pp_policy ); +			md->pp_policy = g_strdup( cmd[4] ); +			msn_soap_passport_sso_request( ic, cmd[5] );  		} -		else if( num_parts >= 7 && strcmp( cmd[2], "OK" ) == 0 ) +		else if( 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" ); -			  			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  		{ @@ -250,163 +289,76 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	}  	else if( strcmp( cmd[0], "MSG" ) == 0 )  	{ -		if( num_parts != 4 ) +		if( num_parts < 4 )  		{  			imcb_error( ic, "Syntax error" );  			imc_logout( ic, TRUE );  			return( 0 );  		} -		md->handler->msglen = atoi( cmd[3] ); +		handler->msglen = atoi( cmd[3] ); -		if( md->handler->msglen <= 0 ) +		if( handler->msglen <= 0 )  		{  			imcb_error( ic, "Syntax error" );  			imc_logout( ic, TRUE );  			return( 0 );  		}  	} -	else if( strcmp( cmd[0], "SYN" ) == 0 ) +	else if( strcmp( cmd[0], "BLP" ) == 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 ); -		} +		msn_ns_send_adl_start( ic ); +		return msn_ns_finish_login( ic );  	} -	else if( strcmp( cmd[0], "LST" ) == 0 ) +	else if( strcmp( cmd[0], "ADL" ) == 0 )  	{ -		int list; -		 -		if( num_parts != 4 && num_parts != 5 ) +		if( num_parts >= 3 && strcmp( cmd[2], "OK" ) == 0 )  		{ -			imcb_error( ic, "Syntax error" ); -			imc_logout( ic, TRUE ); -			return( 0 ); +			msn_ns_send_adl( ic ); +			return msn_ns_finish_login( ic );  		} -		 -		http_decode( cmd[2] ); -		list = atoi( cmd[3] ); -		 -		if( list & 1 ) /* FL */ +		else if( num_parts >= 3 )  		{ -			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 ); -			} +			handler->msglen = atoi( cmd[2] );  		}  	} -	else if( strcmp( cmd[0], "LSG" ) == 0 ) +	else if( strcmp( cmd[0], "PRP" ) == 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] ); +		imcb_connected( ic );  	}  	else if( strcmp( cmd[0], "CHL" ) == 0 )  	{ -		md5_state_t state; -		md5_byte_t digest[16]; -		int i; +		char *resp; +		int st; -		if( num_parts != 3 ) +		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 ); +		resp = msn_p11_challenge( cmd[2] ); -		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] ); -		 -		return( msn_write( ic, buf, strlen( buf ) ) ); +		st =  msn_ns_write( ic, -1, "QRY %d %s %zd\r\n%s", +		                    ++md->trId, MSNP11_PROD_ID, +		                    strlen( resp ), resp ); +		g_free( resp ); +		return st;  	}  	else if( strcmp( cmd[0], "ILN" ) == 0 )  	{  		const struct msn_away_state *st; -		if( num_parts != 6 ) +		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] ); +		http_decode( cmd[5] ); +		imcb_rename_buddy( ic, cmd[3], cmd[5] );  		st = msn_away_state_by_code( cmd[2] );  		if( !st ) @@ -431,16 +383,18 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	else if( strcmp( cmd[0], "NLN" ) == 0 )  	{  		const struct msn_away_state *st; +		int cap; -		if( num_parts != 5 ) +		if( num_parts < 6 )  		{  			imcb_error( ic, "Syntax error" );  			imc_logout( ic, TRUE );  			return( 0 );  		} -		http_decode( cmd[3] ); -		imcb_rename_buddy( ic, cmd[2], cmd[3] ); +		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 ) @@ -450,7 +404,8 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		}  		imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN |  -		                   ( st != msn_away_state_list ? OPT_AWAY : 0 ), +		                   ( 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] ) ); @@ -461,7 +416,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		char *server;  		int session, port; -		if( num_parts != 7 ) +		if( num_parts < 7 )  		{  			imcb_error( ic, "Syntax error" );  			imc_logout( ic, TRUE ); @@ -503,46 +458,6 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  			sb->who = g_strdup( cmd[5] );  		}  	} -	else if( strcmp( cmd[0], "ADD" ) == 0 ) -	{ -		if( num_parts == 6 && strcmp( cmd[2], "RL" ) == 0 ) -		{ -			GSList *l; -			 -			http_decode( cmd[5] ); -			 -			if( strchr( cmd[4], '@' ) == NULL ) -			{ -				imcb_error( ic, "Syntax error" ); -				imc_logout( ic, TRUE ); -				return 0; -			} -			 -			/* We got added by someone. If we don't have this -			   person in permit/deny yet, inform the user. */ -			for( l = ic->permit; l; l = l->next ) -				if( g_strcasecmp( l->data, cmd[4] ) == 0 ) -					return 1; -			 -			for( l = ic->deny; l; l = l->next ) -				if( g_strcasecmp( l->data, cmd[4] ) == 0 ) -					return 1; -			 -			msn_buddy_ask( ic, cmd[4], cmd[5] ); -		} -		else if( num_parts >= 6 && strcmp( cmd[2], "FL" ) == 0 ) -		{ -			const char *group = NULL; -			int num; -			 -			if( cmd[6] != NULL && sscanf( cmd[6], "%d", &num ) == 1 && num < md->groupcount ) -				group = md->grouplist[num]; -			 -			http_decode( cmd[5] ); -			imcb_add_buddy( ic, cmd[4], group ); -			imcb_rename_buddy( ic, cmd[4], cmd[5] ); -		} -	}  	else if( strcmp( cmd[0], "OUT" ) == 0 )  	{  		int allow_reconnect = TRUE; @@ -564,53 +479,20 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  		imc_logout( ic, allow_reconnect );  		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 )  	{  		imcb_error( ic, "Received IPG command, we don't handle them yet." ); -		md->handler->msglen = atoi( cmd[1] ); +		handler->msglen = atoi( cmd[1] ); -		if( md->handler->msglen <= 0 ) +		if( handler->msglen <= 0 )  		{  			imcb_error( ic, "Syntax error" );  			imc_logout( ic, TRUE );  			return( 0 );  		}  	} +#if 0  	else if( strcmp( cmd[0], "ADG" ) == 0 )  	{  		char *group = g_strdup( cmd[3] ); @@ -655,6 +537,26 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  			}  		}  	} +#endif +	else if( strcmp( cmd[0], "GCF" ) == 0 ) +	{ +		/* Coming up is cmd[2] bytes of stuff we're supposed to +		   censore. Meh. */ +		handler->msglen = atoi( cmd[2] ); +	} +	else if( strcmp( cmd[0], "UBX" ) == 0 ) +	{ +		/* Status message. */ +		if( num_parts >= 4 ) +			handler->msglen = atoi( cmd[3] ); +	} +	else if( strcmp( cmd[0], "NOT" ) == 0 ) +	{ +		/* Some kind of notification, poorly documented but +		   apparently used to announce address book changes. */ +		if( num_parts >= 2 ) +			handler->msglen = atoi( cmd[1] ); +	}  	else if( isdigit( cmd[0][0] ) )  	{  		int num = atoi( cmd[0] ); @@ -667,6 +569,10 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  			imc_logout( ic, TRUE );  			return( 0 );  		} +		 +		/* Oh yes, errors can have payloads too now. Discard them for now. */ +		if( num_parts >= 3 ) +			handler->msglen = atoi( cmd[2] );  	}  	else  	{ @@ -676,9 +582,9 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	return( 1 );  } -static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ) +static int msn_ns_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts )  { -	struct im_connection *ic = data; +	struct im_connection *ic = handler->data;  	char *body;  	int blen = 0; @@ -764,13 +670,75 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int  			g_free( ct );  		}  	} +	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 ); +	} +	else if( strcmp( cmd[0], "ADL" ) == 0 ) +	{ +		struct xt_node *adl, *d, *c; +		 +		if( !( adl = xt_from_string( msg ) ) ) +			return 1; +		 +		for( d = adl->children; d; d = d->next ) +		{ +			char *dn; +			if( strcmp( d->name, "d" ) != 0 || +			    ( dn = xt_find_attr( d, "n" ) ) == NULL ) +				continue; +			for( c = d->children; c; c = c->next ) +			{ +				bee_user_t *bu; +				struct msn_buddy_data *bd; +				char *cn, *handle, *f, *l; +				int flags; +				 +				if( strcmp( c->name, "c" ) != 0 || +				    ( l = xt_find_attr( c, "l" ) ) == NULL || +				    ( cn = xt_find_attr( c, "n" ) ) == NULL ) +					continue; +				 +				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 ) ) ) ) +				{ +					g_free( handle ); +					continue; +				} +				g_free( handle ); +				bd = bu->data; +				 +				if( ( f = xt_find_attr( c, "f" ) ) ) +				{ +					http_decode( f ); +					imcb_rename_buddy( ic, bu->handle, f ); +				} +				 +				flags = atoi( l ) & 15; +				if( bd->flags != flags ) +				{ +					bd->flags = flags; +					msn_buddy_ask( bu ); +				} +			} +		} +	}  	return( 1 );  } -static void msn_auth_got_passport_token( struct msn_auth_data *mad ) +void msn_auth_got_passport_token( struct im_connection *ic, const char *token, const char *error )  { -	struct im_connection *ic = mad->data;  	struct msn_data *md;  	/* Dead connection? */ @@ -778,61 +746,129 @@ static void msn_auth_got_passport_token( struct msn_auth_data *mad )  		return;  	md = ic->proto_data; -	if( mad->token ) +	 +	if( token )  	{ -		char buf[1024]; -		 -		g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token ); -		msn_write( ic, buf, strlen( buf ) ); +		msn_ns_write( ic, -1, "USR %d SSO S %s %s\r\n", ++md->trId, md->tokens[0], token );  	}  	else  	{ -		imcb_error( ic, "Error during Passport authentication: %s", mad->error ); +		imcb_error( ic, "Error during Passport authentication: %s", 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; +	struct msn_data *md; -	if( ( s = set_find( &ic->acc->set, "display_name" ) ) == NULL ) -		return FALSE; /* Shouldn't happen.. */ +	/* Dead connection? */ +	if( g_slist_find( msn_connections, ic ) == NULL ) +		return; -	http_decode( name ); +	md = ic->proto_data; +	msn_ns_write( ic, -1, "BLP %d %s\r\n", ++md->trId, "BL" ); +} + +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; +	struct msn_data *md = bu->ic->proto_data; +	char handle[strlen(bu->handle)]; +	char *domain; +	char l[4]; -	if( s->value && strcmp( s->value, name ) == 0 ) +	if( ( bd->flags & 7 ) == 0 || ( bd->flags & MSN_BUDDY_ADL_SYNCED ) ) +		return FALSE; +	 +	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 )  	{ -		return TRUE; -		/* The names match, nothing to worry about. */ +		d = xt_new_node( "d", NULL, NULL ); +		xt_add_attr( d, "n", domain ); +		xt_insert_child( adl, d );  	} -	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 ); +	 +	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 ); +	 +	/* Do this in batches of 100. */ +	bd->flags |= MSN_BUDDY_ADL_SYNCED; +	return (--md->adl_todo % 140) == 0; +} + +static void msn_ns_send_adl( struct im_connection *ic ) +{ +	struct xt_node *adl; +	struct msn_data *md = ic->proto_data; +	char *adls; +	 +	adl = xt_new_node( "ml", NULL, NULL ); +	xt_add_attr( adl, "l", "1" ); +	g_tree_foreach( md->domaintree, msn_ns_send_adl_1, adl ); +	if( adl->children == NULL ) +	{ +		/* This tells the caller that we're done now. */ +		md->adl_todo = -1; +		xt_free_node( adl ); +		return;  	} -	else +	 +	adls = xt_to_string( adl ); +	msn_ns_write( ic, -1, "ADL %d %zd\r\n%s", ++md->trId, strlen( adls ), adls ); +	g_free( adls ); +} + +static void msn_ns_send_adl_start( struct im_connection *ic ) +{ +	struct msn_data *md; +	GSList *l; +	 +	/* Dead connection? */ +	if( g_slist_find( msn_connections, ic ) == NULL ) +		return; +	 +	md = ic->proto_data; +	md->adl_todo = 0; +	for( l = ic->bee->users; l; l = l->next )  	{ -		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 ); +		bee_user_t *bu = l->data; +		struct msn_buddy_data *bd = bu->data; -		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" ); -		} +		if( bu->ic != ic || ( bd->flags & 7 ) == 0 ) +			continue; -		return TRUE; +		bd->flags &= ~MSN_BUDDY_ADL_SYNCED; +		md->adl_todo++;  	} +	 +	msn_ns_send_adl( ic ); +} + +int msn_ns_finish_login( struct im_connection *ic ) +{ +	struct msn_data *md = ic->proto_data; +	 +	if( ic->flags & OPT_LOGGED_IN ) +		return 1; +	 +	if( md->adl_todo < 0 ) +		md->flags |= MSN_DONE_ADL; +	 +	if( ( md->flags & MSN_DONE_ADL ) && ( md->flags & MSN_GOT_PROFILE ) ) +		return msn_ns_set_display_name( ic, set_getstr( &ic->acc->set, "display_name" ) ); +	else +		return 1;  } diff --git a/protocols/msn/passport.c b/protocols/msn/passport.c deleted file mode 100644 index 7c896db1..00000000 --- a/protocols/msn/passport.c +++ /dev/null @@ -1,172 +0,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; -} diff --git a/protocols/msn/passport.h b/protocols/msn/passport.h deleted file mode 100644 index 517d2e91..00000000 --- a/protocols/msn/passport.h +++ /dev/null @@ -1,113 +0,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__ */ diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index cb5789b8..898fb34f 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-2005 Wilmer van der Gaast and others                * +  * Copyright 2002-2010 Wilmer van der Gaast and others                *    \********************************************************************/  /* MSN module - Switchboard server callbacks and utilities              */ @@ -26,33 +26,44 @@  #include <ctype.h>  #include "nogaim.h"  #include "msn.h" -#include "passport.h"  #include "md5.h" +#include "soap.h"  #include "invitation.h"  static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition cond ); -static int msn_sb_command( gpointer data, char **cmd, int num_parts ); -static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ); +static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num_parts ); +static int msn_sb_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts ); -int msn_sb_write( struct msn_switchboard *sb, char *s, int len ) +int msn_sb_write( struct msn_switchboard *sb, const char *fmt, ... )  { +	va_list params; +	char *out; +	size_t len;  	int st; -	st = write( sb->fd, s, len ); +	va_start( params, fmt ); +	out = g_strdup_vprintf( fmt, params ); +	va_end( params ); +	 +	if( getenv( "BITLBEE_DEBUG" ) ) +		fprintf( stderr, "->SB%d:%s", sb->fd, out ); +	 +	len = strlen( out ); +	st = write( sb->fd, out, len ); +	g_free( out );  	if( st != len )  	{  		msn_sb_destroy( sb ); -		return( 0 ); +		return 0;  	} -	return( 1 ); +	return 1;  }  int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m )  {  	struct msn_data *md = ic->proto_data;  	struct msn_switchboard *sb; -	char buf[1024];  	/* FIXME: *CHECK* the reliability of using spare sb's! */  	if( ( sb = msn_sb_spare( ic ) ) ) @@ -60,8 +71,7 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m )  		debug( "Trying to use a spare switchboard to message %s", m->who );  		sb->who = g_strdup( m->who ); -		g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, m->who ); -		if( msn_sb_write( sb, buf, strlen( buf ) ) ) +		if( msn_sb_write( sb, "CAL %d %s\r\n", ++sb->trId, m->who ) )  		{  			/* He/She should join the switchboard soon, let's queue the message. */  			sb->msgq = g_slist_append( sb->msgq, m ); @@ -72,8 +82,7 @@ int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m )  	debug( "Creating a new switchboard to message %s", m->who );  	/* If we reach this line, there was no spare switchboard, so let's make one. */ -	g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); -	if( !msn_write( ic, buf, strlen( buf ) ) ) +	if( !msn_ns_write( ic, -1, "XFR %d SB\r\n", ++md->trId ) )  	{  		g_free( m->who );  		g_free( m->text ); @@ -164,7 +173,7 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )  {  	if( sb->ready )  	{ -		char *packet, *buf; +		char *buf;  		int i, j;  		/* Build the message. Convert LF to CR-LF for normal messages. */ @@ -200,17 +209,15 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )  		}  		/* Build the final packet (MSG command + the message). */ -		packet = g_strdup_printf( "MSG %d N %d\r\n%s", ++sb->trId, i, buf ); -		g_free( buf ); -		if( msn_sb_write( sb, packet, strlen( packet ) ) ) +		if( msn_sb_write( sb, "MSG %d N %d\r\n%s", ++sb->trId, i, buf ) )  		{ -			g_free( packet ); -			return( 1 ); +			g_free( buf ); +			return 1;  		}  		else  		{ -			g_free( packet ); -			return( 0 ); +			g_free( buf ); +			return 0;  		}  	}  	else if( sb->who ) @@ -325,7 +332,7 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond )  	else  		g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, ic->acc->user, sb->key, sb->session ); -	if( msn_sb_write( sb, buf, strlen( buf ) ) ) +	if( msn_sb_write( sb, "%s", buf ) )  		sb->inp = b_input_add( sb->fd, B_EV_IO_READ, msn_sb_callback, sb );  	else  		debug( "Error %d while connecting to switchboard server", 2 ); @@ -345,7 +352,6 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c  	if( sb->msgq != NULL )  	{  		time_t now = time( NULL ); -		char buf[1024];  		if( now - md->first_sb_failure > 600 )  		{ @@ -377,8 +383,7 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c  		debug( "Moved queued messages back to the main queue, "  		       "creating a new switchboard to retry." ); -		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); -		if( !msn_write( ic, buf, strlen( buf ) ) ) +		if( !msn_ns_write( ic, -1, "XFR %d SB\r\n", ++md->trId ) )  			return FALSE;  	} @@ -386,11 +391,10 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c  	return FALSE;  } -static int msn_sb_command( gpointer data, char **cmd, int num_parts ) +static int msn_sb_command( struct msn_handler_data *handler, char **cmd, int num_parts )  { -	struct msn_switchboard *sb = data; +	struct msn_switchboard *sb = handler->data;  	struct im_connection *ic = sb->ic; -	char buf[1024];  	if( !num_parts )  	{ @@ -406,7 +410,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  	}  	else if( strcmp( cmd[0], "USR" ) == 0 )  	{ -		if( num_parts != 5 ) +		if( num_parts < 5 )  		{  			msn_sb_destroy( sb );  			return( 0 ); @@ -419,20 +423,15 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  		}  		if( sb->who ) -		{ -			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, sb->who ); -			return( msn_sb_write( sb, buf, strlen( buf ) ) ); -		} +			return msn_sb_write( sb, "CAL %d %s\r\n", ++sb->trId, sb->who );  		else -		{  			debug( "Just created a switchboard, but I don't know what to do with it." ); -		}  	}  	else if( strcmp( cmd[0], "IRO" ) == 0 )  	{  		int num, tot; -		if( num_parts != 6 ) +		if( num_parts < 6 )  		{  			msn_sb_destroy( sb );  			return( 0 ); @@ -469,7 +468,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  	}  	else if( strcmp( cmd[0], "ANS" ) == 0 )  	{ -		if( num_parts != 3 ) +		if( num_parts < 3 )  		{  			msn_sb_destroy( sb );  			return( 0 ); @@ -488,7 +487,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  	}  	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 );  			return( 0 ); @@ -498,7 +497,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  	}  	else if( strcmp( cmd[0], "JOI" ) == 0 )  	{ -		if( num_parts != 3 ) +		if( num_parts < 3 )  		{  			msn_sb_destroy( sb );  			return( 0 ); @@ -559,7 +558,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  	}  	else if( strcmp( cmd[0], "MSG" ) == 0 )  	{ -		if( num_parts != 4 ) +		if( num_parts < 4 )  		{  			msn_sb_destroy( sb );  			return( 0 ); @@ -624,7 +623,12 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  		int num = atoi( cmd[0] );  		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 )  		{ @@ -660,9 +664,9 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  	return( 1 );  } -static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ) +static int msn_sb_message( struct msn_handler_data *handler, char *msg, int msglen, char **cmd, int num_parts )  { -	struct msn_switchboard *sb = data; +	struct msn_switchboard *sb = handler->data;  	struct im_connection *ic = sb->ic;  	char *body;  	int blen = 0; @@ -740,8 +744,8 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int  #endif  		else if( g_strncasecmp( ct, "application/x-msnmsgrp2p", 24 ) == 0 )   		{ -			imcb_error( sb->ic, "Cannot receive file from %s: BitlBee does not " -					"support msnmsgrp2p yet.", sb->who ); +			/* Not currently implemented. Don't warn about it since +			   this seems to be used for avatars now. */  			g_free( ct );  		}  		else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 ) diff --git a/protocols/msn/soap.c b/protocols/msn/soap.c new file mode 100644 index 00000000..909f6ad9 --- /dev/null +++ b/protocols/msn/soap.c @@ -0,0 +1,1078 @@ +  /********************************************************************\ +  * 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_REAUTH, +	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 void msn_soap_free( struct msn_soap_req_data *soap_req ); +static void msn_soap_debug_print( const char *headers, const char *payload ); + +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 ); +	 +	msn_soap_debug_print( http_req, 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( g_slist_find( msn_connections, soap_req->ic ) == NULL ) +	{ +		msn_soap_free( soap_req ); +		return; +	} +	 +	msn_soap_debug_print( http_req->reply_headers, http_req->reply_body ); +	 +	if( http_req->body_size > 0 ) +	{ +		struct xt_parser *parser; +		struct xt_node *err; +		 +		parser = xt_new( soap_req->xml_parser, soap_req ); +		xt_feed( parser, http_req->reply_body, http_req->body_size ); +		if( http_req->status_code == 500 && +		    ( err = xt_find_path( parser->root, "soap:Body/soap:Fault/detail/errorcode" ) ) && +		    err->text_len > 0 ) +		{ +			if( strcmp( err->text, "PassportAuthFail" ) == 0 ) +			{ +				xt_free( parser ); +				st = MSN_SOAP_REAUTH; +				goto fail; +			} +			/* TODO: Handle/report other errors. */ +		} +		 +		xt_handle( parser, NULL, -1 ); +		xt_free( parser ); +	} +	 +	st = soap_req->handle_response( soap_req ); + +fail:	 +	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 if( st == MSN_SOAP_REAUTH ) +	{ +		struct msn_data *md = soap_req->ic->proto_data; +		 +		if( !( md->flags & MSN_REAUTHING ) ) +		{ +			/* Nonce shouldn't actually be touched for re-auths. */ +			msn_soap_passport_sso_request( soap_req->ic, "blaataap" ); +			md->flags |= MSN_REAUTHING;  +		} +		md->soapq = g_slist_append( md->soapq, soap_req ); +	} +	else +	{ +		soap_req->free_data( soap_req ); +		g_free( soap_req ); +	} +} + +static char *msn_soap_abservice_build( const char *body_fmt, const char *scenario, const char *ticket, ... ) +{ +	va_list params; +	char *ret, *format, *body; +	 +	format = g_markup_printf_escaped( SOAP_ABSERVICE_PAYLOAD, scenario, ticket ); +	 +	va_start( params, ticket ); +	body = g_strdup_vprintf( body_fmt, params ); +	va_end( params ); +	 +	ret = g_strdup_printf( format, body ); +	g_free( body ); +	g_free( format ); +	 +	return ret; +} + +static void msn_soap_debug_print( const char *headers, const char *payload ) +{ +	char *s; +	 +	if( !getenv( "BITLBEE_DEBUG" ) ) +		return; +	 +	if( ( s = strstr( headers, "\r\n\r\n" ) ) ) +		write( 1, s, s - headers + 4 ); +	else +		write( 1, headers, strlen( headers ) ); +	 +#ifdef DEBUG +	{ +		struct xt_node *xt = xt_from_string( payload ); +		if( xt ) +			xt_print( xt ); +		xt_free_node( xt ); +	} +#endif +} + +int msn_soapq_flush( struct im_connection *ic, gboolean resend ) +{ +	struct msn_data *md = ic->proto_data; +	 +	while( md->soapq ) +	{ +		if( resend ) +			msn_soap_send_request( (struct msn_soap_req_data*) md->soapq->data ); +		else +			msn_soap_free( (struct msn_soap_req_data*) md->soapq->data ); +		md->soapq = g_slist_remove( md->soapq, md->soapq->data ); +	} +	 +	return MSN_SOAP_OK; +} + +static void msn_soap_free( struct msn_soap_req_data *soap_req ) +{ +	soap_req->free_data( soap_req ); +	g_free( soap_req->url ); +	g_free( soap_req->action ); +	g_free( soap_req->payload ); +	g_free( soap_req ); +} + + +/* passport_sso: Authentication MSNP15+ */ + +struct msn_soap_passport_sso_data +{ +	char *nonce; +	char *secret; +	char *error; +	char *redirect; +}; + +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; +	struct msn_data *md = ic->proto_data; +	char pass[MAX_PASSPORT_PWLEN+1]; +	 +	if( sd->redirect ) +	{ +		soap_req->url = sd->redirect; +		sd->redirect = NULL; +	} +	else 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 ); +	 +	strncpy( pass, ic->acc->pass, MAX_PASSPORT_PWLEN ); +	soap_req->payload = g_markup_printf_escaped( SOAP_PASSPORT_SSO_PAYLOAD, +		ic->acc->user, pass, md->pp_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 = xt_find_path( node, "../../wst:RequestedProofToken/wst:BinarySecret" ) ) && +	    p->text ) +	    	sd->secret = g_strdup( p->text ); +	 +	*id -= '1'; +	if( *id >= 0 && *id < sizeof( md->tokens ) / sizeof( md->tokens[0] ) ) +	{ +		g_free( md->tokens[(int)*id] ); +		md->tokens[(int)*id] = g_strdup( node->text ); +	} +	 +	return XT_HANDLED; +} + +static xt_status msn_soap_passport_failure( 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 xt_node *code = xt_find_node( node->children, "faultcode" ); +	struct xt_node *string = xt_find_node( node->children, "faultstring" ); +	struct xt_node *url; +	 +	if( code == NULL || code->text_len == 0 ) +		sd->error = g_strdup( "Unknown error" ); +	else if( strcmp( code->text, "psf:Redirect" ) == 0 && +	         ( url = xt_find_node( node->children, "psf:redirectUrl" ) ) && +	         url->text_len > 0 ) +		sd->redirect = g_strdup( url->text ); +	else +		sd->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ? +		                             string->text : "no description available" ); +	 +	return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_passport_sso_parser[] = { +	{ "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", msn_soap_passport_sso_token }, +	{ "S:Fault", "S:Envelope", msn_soap_passport_failure }, +	{ 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; +	struct msn_data *md = ic->proto_data; +	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 ), +	}; +	 +	if( sd->redirect ) +		return MSN_SOAP_RETRY; +	 +	if( md->soapq ) +		return msn_soapq_flush( ic, TRUE ); +	 +	if( sd->secret == NULL ) +	{ +		msn_auth_got_passport_token( ic, NULL, sd->error ); +		return MSN_SOAP_OK; +	} + +	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, NULL ); +	 +	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->nonce ); +	g_free( sd->secret ); +	g_free( sd->error ); +	g_free( sd->redirect ); +	 +	return MSN_SOAP_OK; +} + +int msn_soap_passport_sso_request( struct im_connection *ic, const char *nonce ) +{ +	struct msn_soap_passport_sso_data *sd = g_new0( struct msn_soap_passport_sso_data, 1 ); +	 +	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; +	msn_soap_result_t 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 ); +	oim->need_retry = MSN_SOAP_OK; +	 +	return MSN_SOAP_OK; +} + +static xt_status msn_soap_oim_reauth( 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; +	struct xt_node *c; +	 +	if( ( c = xt_find_node( node->children, "LockKeyChallenge" ) ) && c->text_len > 0 ) +	{ +		g_free( md->lock_key ); +		md->lock_key = msn_p11_challenge( c->text ); +		oim->need_retry = MSN_SOAP_RETRY; +	} +	if( xt_find_node( node->children, "RequiredAuthPolicy" ) ) +	{ +		oim->need_retry = MSN_SOAP_REAUTH; +	} +	 +	return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_oim_send_parser[] = { +	{ "detail", "soap:Fault", msn_soap_oim_reauth }, +	{ 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 ) +	{ +		return oim->need_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 = msn_soap_abservice_build( SOAP_MEMLIST_PAYLOAD, "Initial", 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 = xt_find_path( node, "../../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; +		ic->permit = g_slist_prepend( ic->permit, g_strdup( handle ) ); +	} +	else if( strcmp( role, "Block" ) == 0 ) +	{ +		bd->flags |= MSN_BUDDY_BL; +		ic->deny = g_slist_prepend( ic->deny, g_strdup( handle ) ); +	} +	else if( strcmp( role, "Reverse" ) == 0 ) +		bd->flags |= MSN_BUDDY_RL; +	else if( strcmp( role, "Pending" ) == 0 ) +		bd->flags |= MSN_BUDDY_PL; +	 +	if( getenv( "BITLBEE_DEBUG" ) ) +		printf( "%p %s %d\n", bu, handle, bd->flags ); +	 +	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 ); +} + +/* Variant: Adding/Removing people */ +struct msn_soap_memlist_edit_data +{ +	char *handle; +	gboolean add; +	msn_buddy_flags_t list; +}; + +static int msn_soap_memlist_edit_build_request( struct msn_soap_req_data *soap_req ) +{ +	struct msn_data *md = soap_req->ic->proto_data; +	struct msn_soap_memlist_edit_data *med = soap_req->data; +	char *add, *scenario, *list; +	 +	soap_req->url = g_strdup( SOAP_MEMLIST_URL ); +	if( med->add ) +	{ +		soap_req->action = g_strdup( SOAP_MEMLIST_ADD_ACTION ); +		add = "Add"; +	} +	else +	{ +		soap_req->action = g_strdup( SOAP_MEMLIST_DEL_ACTION ); +		add = "Delete"; +	} +	switch( med->list ) +	{ +	case MSN_BUDDY_AL: +		scenario = "BlockUnblock"; +		list = "Allow"; +		break; +	case MSN_BUDDY_BL: +		scenario = "BlockUnblock"; +		list = "Block"; +		break; +	case MSN_BUDDY_RL: +		scenario = "Timer"; +		list = "Reverse"; +		break; +	case MSN_BUDDY_PL: +	default: +		scenario = "Timer"; +		list = "Pending"; +		break; +	} +	soap_req->payload = msn_soap_abservice_build( SOAP_MEMLIST_EDIT_PAYLOAD, +		scenario, md->tokens[1], add, list, med->handle, add ); +	 +	return 1; +} + +static int msn_soap_memlist_edit_handle_response( struct msn_soap_req_data *soap_req ) +{ +	return MSN_SOAP_OK; +} + +static int msn_soap_memlist_edit_free_data( struct msn_soap_req_data *soap_req ) +{ +	struct msn_soap_memlist_edit_data *med = soap_req->data; +	 +	g_free( med->handle ); +	g_free( med ); +	 +	return 0; +} + +int msn_soap_memlist_edit( struct im_connection *ic, const char *handle, gboolean add, int list ) +{ +	struct msn_soap_memlist_edit_data *med; +	 +	med = g_new0( struct msn_soap_memlist_edit_data, 1 ); +	med->handle = g_strdup( handle ); +	med->add = add; +	med->list = list; +	 +	return msn_soap_start( ic, med, msn_soap_memlist_edit_build_request, +	                                NULL, +	                                msn_soap_memlist_edit_handle_response, +	                                msn_soap_memlist_edit_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 = msn_soap_abservice_build( SOAP_ADDRESSBOOK_PAYLOAD, "Initial", 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; +	struct msn_data *md = soap_req->ic->proto_data; +	 +	if( ( p = xt_find_path( node, "../groupId" ) ) ) +		id = p->text; +	 +	if( ( p = xt_find_node( node->children, "name" ) ) ) +		name = p->text; +	 +	if( id && name ) +	{ +		struct msn_group *mg = g_new0( struct msn_group, 1 ); +		mg->id = g_strdup( id ); +		mg->name = g_strdup( name ); +		md->groups = g_slist_prepend( md->groups, mg ); +	} +	 +	if( getenv( "BITLBEE_DEBUG" ) ) +		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, *is_msgr = "false", +	     *display_name = NULL, *group_id = NULL; +	struct msn_soap_req_data *soap_req = data; +	struct im_connection *ic = soap_req->ic; +	struct msn_group *group; +	 +	if( ( p = xt_find_path( node, "../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( ( p = xt_find_node( node->children, "isMessengerUser" ) ) ) +		is_msgr = p->text; +	if( ( p = xt_find_path( node, "groupIds/guid" ) ) ) +		group_id = 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 ); +		 +		/* Try to fetch the profile; if the user has one, that's where +		   we can find the persistent display_name. */ +		if( ( p = xt_find_node( node->children, "CID" ) ) && p->text ) +			msn_soap_profile_get( ic, p->text ); +		 +		return XT_HANDLED; +	} +	 +	if( !bool2int( is_msgr ) || handle == NULL ) +		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 ); +	 +	if( group_id && ( group = msn_group_by_id( ic, group_id ) ) ) +		imcb_add_buddy( ic, handle, group->name ); +	 +	if( getenv( "BITLBEE_DEBUG" ) ) +		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 ) +{ +	GSList *l; +	 +	for( l = soap_req->ic->bee->users; l; l = l->next ) +	{ +		struct bee_user *bu = l->data; +		 +		if( bu->ic == soap_req->ic ) +			msn_buddy_ask( bu ); +	} +	 +	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 = msn_soap_abservice_build( SOAP_AB_NAMECHANGE_PAYLOAD, +		"Timer", 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 ); +} + +/* Add a contact. */ +static int msn_soap_ab_contact_add_build_request( struct msn_soap_req_data *soap_req ) +{ +	struct msn_data *md = soap_req->ic->proto_data; +	bee_user_t *bu = soap_req->data; +	 +	soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL ); +	soap_req->action = g_strdup( SOAP_AB_CONTACT_ADD_ACTION ); +	soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_ADD_PAYLOAD, +		"ContactSave", md->tokens[1], bu->handle, bu->fullname ? bu->fullname : bu->handle ); +	 +	return 1; +} + +static xt_status msn_soap_ab_contact_add_cid( struct xt_node *node, gpointer data ) +{ +	struct msn_soap_req_data *soap_req = data; +	bee_user_t *bu = soap_req->data; +	struct msn_buddy_data *bd = bu->data; +	 +	g_free( bd->cid ); +	bd->cid = g_strdup( node->text ); +	 +	return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_ab_contact_add_parser[] = { +	{ "guid", "ABContactAddResult", msn_soap_ab_contact_add_cid }, +	{ NULL,               NULL,     NULL                        } +}; + +static int msn_soap_ab_contact_add_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_contact_add_free_data( struct msn_soap_req_data *soap_req ) +{ +	return 0; +} + +int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu ) +{ +	return msn_soap_start( ic, bu, +	                       msn_soap_ab_contact_add_build_request, +	                       msn_soap_ab_contact_add_parser, +	                       msn_soap_ab_contact_add_handle_response, +	                       msn_soap_ab_contact_add_free_data ); +} + +/* Remove a contact. */ +static int msn_soap_ab_contact_del_build_request( struct msn_soap_req_data *soap_req ) +{ +	struct msn_data *md = soap_req->ic->proto_data; +	const char *cid = soap_req->data; +	 +	soap_req->url = g_strdup( SOAP_ADDRESSBOOK_URL ); +	soap_req->action = g_strdup( SOAP_AB_CONTACT_DEL_ACTION ); +	soap_req->payload = msn_soap_abservice_build( SOAP_AB_CONTACT_DEL_PAYLOAD, +		"Timer", md->tokens[1], cid ); +	 +	return 1; +} + +static int msn_soap_ab_contact_del_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_contact_del_free_data( struct msn_soap_req_data *soap_req ) +{ +	g_free( soap_req->data ); +	return 0; +} + +int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu ) +{ +	struct msn_buddy_data *bd = bu->data; +	 +	return msn_soap_start( ic, g_strdup( bd->cid ), +	                       msn_soap_ab_contact_del_build_request, +	                       NULL, +	                       msn_soap_ab_contact_del_handle_response, +	                       msn_soap_ab_contact_del_free_data ); +} + + + +/* Storage stuff: Fetch profile. */ +static int msn_soap_profile_get_build_request( struct msn_soap_req_data *soap_req ) +{ +	struct msn_data *md = soap_req->ic->proto_data; +	 +	soap_req->url = g_strdup( SOAP_STORAGE_URL ); +	soap_req->action = g_strdup( SOAP_PROFILE_GET_ACTION ); +	soap_req->payload = g_markup_printf_escaped( SOAP_PROFILE_GET_PAYLOAD, +		md->tokens[3], (char*) soap_req->data ); +	 +	return 1; +} + +static xt_status msn_soap_profile_get_result( struct xt_node *node, gpointer data ) +{ +	struct msn_soap_req_data *soap_req = data; +	struct im_connection *ic = soap_req->ic; +	struct msn_data *md = soap_req->ic->proto_data; +	struct xt_node *dn; +	 +	if( ( dn = xt_find_node( node->children, "DisplayName" ) ) && dn->text ) +	{ +		set_t *set = set_find( &ic->acc->set, "display_name" ); +		g_free( set->value ); +		set->value = g_strdup( dn->text ); +		 +		md->flags |= MSN_GOT_PROFILE_DN; +	} +	 +	return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_profile_get_parser[] = { +	{ "ExpressionProfile", "GetProfileResult", msn_soap_profile_get_result }, +	{ NULL,               NULL,     NULL                        } +}; + +static int msn_soap_profile_get_handle_response( struct msn_soap_req_data *soap_req ) +{ +	struct msn_data *md = soap_req->ic->proto_data; +	 +	md->flags |= MSN_GOT_PROFILE; +	msn_ns_finish_login( soap_req->ic ); +	 +	return MSN_SOAP_OK; +} + +static int msn_soap_profile_get_free_data( struct msn_soap_req_data *soap_req ) +{ +	g_free( soap_req->data ); +	return 0; +} + +int msn_soap_profile_get( struct im_connection *ic, const char *cid ) +{ +	return msn_soap_start( ic, g_strdup( cid ), +	                       msn_soap_profile_get_build_request, +	                       msn_soap_profile_get_parser, +	                       msn_soap_profile_get_handle_response, +	                       msn_soap_profile_get_free_data ); +} diff --git a/protocols/msn/soap.h b/protocols/msn/soap.h new file mode 100644 index 00000000..fccdebc6 --- /dev/null +++ b/protocols/msn/soap.h @@ -0,0 +1,350 @@ +  /********************************************************************\ +  * 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" + + +int msn_soapq_flush( struct im_connection *ic, gboolean resend ); + + +#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/pp800/RST.srf" +#define MAX_PASSPORT_PWLEN 16 + +#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>" \ +           "<wst:RequestSecurityToken Id=\"RST4\">" \ +               "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \ +               "<wsp:AppliesTo>" \ +                   "<wsa:EndpointReference>" \ +                       "<wsa:Address>storage.msn.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 *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_ABSERVICE_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\">%s</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/\">" \ +    "%%s" \ +  "</soap:Body>" \ +"</soap:Envelope>" + +#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 \ +    "<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>" + +#define SOAP_MEMLIST_ADD_ACTION "http://www.msn.com/webservices/AddressBook/AddMember" +#define SOAP_MEMLIST_DEL_ACTION "http://www.msn.com/webservices/AddressBook/DeleteMember" + +#define SOAP_MEMLIST_EDIT_PAYLOAD \ +  "<%sMember xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ +   "<serviceHandle>" \ +    "<Id>0</Id>" \ +    "<Type>Messenger</Type>" \ +    "<ForeignId></ForeignId>" \ +   "</serviceHandle>" \ +   "<memberships>" \ +    "<Membership>" \ +     "<MemberRole>%s</MemberRole>" \ +     "<Members>" \ +      "<Member xsi:type=\"PassportMember\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" \ +       "<Type>Passport</Type>" \ +       "<State>Accepted</State>" \ +       "<PassportName>%s</PassportName>" \ +      "</Member>" \ +     "</Members>" \ +    "</Membership>" \ +   "</memberships>" \ +  "</%sMember>" + +int msn_soap_memlist_request( struct im_connection *ic ); +int msn_soap_memlist_edit( struct im_connection *ic, const char *handle, gboolean add, int list ); + + +#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 \ +    "<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>" + +#define SOAP_AB_NAMECHANGE_ACTION "http://www.msn.com/webservices/AddressBook/ABContactUpdate" + +#define SOAP_AB_NAMECHANGE_PAYLOAD \ +        "<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>" + +#define SOAP_AB_CONTACT_ADD_ACTION "http://www.msn.com/webservices/AddressBook/ABContactAdd" + +#define SOAP_AB_CONTACT_ADD_PAYLOAD \ +        "<ABContactAdd 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>LivePending</contactType>" \ +                        "<passportName>%s</passportName>" \ +                        "<isMessengerUser>true</isMessengerUser>" \ +                        "<MessengerMemberInfo>" \ +                            "<DisplayName>%s</DisplayName>" \ +                        "</MessengerMemberInfo>" \ +                    "</contactInfo>" \ +                "</Contact>" \ +            "</contacts>" \ +            "<options>" \ +                "<EnableAllowListManagement>true</EnableAllowListManagement>" \ +            "</options>" \ +        "</ABContactAdd>" + +#define SOAP_AB_CONTACT_DEL_ACTION "http://www.msn.com/webservices/AddressBook/ABContactDelete" + +#define SOAP_AB_CONTACT_DEL_PAYLOAD \ +        "<ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ +            "<abId>00000000-0000-0000-0000-000000000000</abId>" \ +            "<contacts>" \ +                "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">" \ +                    "<contactId>%s</contactId>" \ +                "</Contact>" \ +            "</contacts>" \ +        "</ABContactDelete>" + +int msn_soap_addressbook_request( struct im_connection *ic ); +int msn_soap_addressbook_set_display_name( struct im_connection *ic, const char *new ); +int msn_soap_ab_contact_add( struct im_connection *ic, bee_user_t *bu ); +int msn_soap_ab_contact_del( struct im_connection *ic, bee_user_t *bu ); + + +#define SOAP_STORAGE_URL "https://storage.msn.com/storageservice/SchematizedStore.asmx" +#define SOAP_PROFILE_GET_ACTION "http://www.msn.com/webservices/storage/w10/GetProfile" + +#define SOAP_PROFILE_GET_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/\">" \ +    "<StorageApplicationHeader xmlns=\"http://www.msn.com/webservices/storage/w10\">" \ +      "<ApplicationID>Messenger Client 9.0</ApplicationID>" \ +      "<Scenario>Initial</Scenario>" \ +    "</StorageApplicationHeader>" \ +    "<StorageUserHeader xmlns=\"http://www.msn.com/webservices/storage/w10\">" \ +      "<Puid>0</Puid>" \ +      "<TicketToken>%s</TicketToken>" \ +    "</StorageUserHeader>" \ +  "</soap:Header>" \ +  "<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">" \ +    "<GetProfile xmlns=\"http://www.msn.com/webservices/storage/w10\">" \ +      "<profileHandle>" \ +        "<Alias>" \ +          "<Name>%s</Name>" \ +          "<NameSpace>MyCidStuff</NameSpace>" \ +        "</Alias>" \ +        "<RelationshipName>MyProfile</RelationshipName>" \ +      "</profileHandle>" \ +      "<profileAttributes>" \ +        "<ResourceID>true</ResourceID>" \ +        "<DateModified>true</DateModified>" \ +        "<ExpressionProfileAttributes>" \ +          "<ResourceID>true</ResourceID>" \ +          "<DateModified>true</DateModified>" \ +          "<DisplayName>true</DisplayName>" \ +          "<DisplayNameLastModified>true</DisplayNameLastModified>" \ +          "<PersonalStatus>true</PersonalStatus>" \ +          "<PersonalStatusLastModified>true</PersonalStatusLastModified>" \ +          "<StaticUserTilePublicURL>true</StaticUserTilePublicURL>" \ +          "<Photo>true</Photo>" \ +          "<Flags>true</Flags>" \ +        "</ExpressionProfileAttributes>" \ +      "</profileAttributes>" \ +    "</GetProfile>" \ +  "</soap:Body>" \ +"</soap:Envelope>" + +int msn_soap_profile_get( struct im_connection *ic, const char *cid ); + + +#endif /* __SOAP_H__ */ diff --git a/protocols/msn/tables.c b/protocols/msn/tables.c index 42b12aa9..273d291e 100644 --- a/protocols/msn/tables.c +++ b/protocols/msn/tables.c @@ -1,7 +1,7 @@    /********************************************************************\    * 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                *    \********************************************************************/  /* MSN module - Some tables with useful data                            */ @@ -82,6 +82,8 @@ const struct msn_status_code msn_status_code_list[] =  	{ 229, "Group name too long",                                   0 },  	{ 230, "Cannot remove that group",                              0 },  	{ 231, "Invalid group",                                         0 }, +	{ 240, "ADL/RML command with corrupted payload",                STATUS_FATAL }, +	{ 241, "ADL/RML command with invalid modification",             0 },  	{ 280, "Switchboard failed",                                    STATUS_SB_FATAL },  	{ 281, "Transfer to switchboard failed",                        0 }, diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 7943e026..aa3ad5bb 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -251,7 +251,7 @@ void imcb_error( struct im_connection *ic, char *format, ... )  	if( ic->flags & OPT_LOGGED_IN )  		serv_got_crap( ic, "Error: %s", text );  	else -		serv_got_crap( ic, "Couldn't log in: %s", text ); +		serv_got_crap( ic, "Login error: %s", text );  	g_free( text );  } @@ -325,14 +325,6 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )  	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; )  	{  		bee_user_t *bu = l->data; @@ -344,6 +336,14 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )  		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 );  	for( a = bee->accounts; a; a = a->next ) diff --git a/protocols/nogaim.h b/protocols/nogaim.h index cf6cdc30..a09a3f67 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -199,6 +199,8 @@ struct prpl {  	/* Request profile info. Free-formatted stuff, the IM module gives back  	   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); diff --git a/protocols/oscar/ssi.c b/protocols/oscar/ssi.c index 76b5b427..f37d98e5 100644 --- a/protocols/oscar/ssi.c +++ b/protocols/oscar/ssi.c @@ -414,7 +414,7 @@ int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn)  		for (parentgroup=sess->ssi.items; ((parentgroup) && (parentgroup->type!=AIM_SSI_TYPE_GROUP) && (parentgroup->gid==0x0000)); parentgroup=parentgroup->next);  		if (!parentgroup) {  			char *newgroup; -			newgroup = (char*)g_malloc(strlen("Unknown")*sizeof(char)); +			newgroup = (char*)g_malloc(strlen("Unknown")+1);  			strcpy(newgroup, "Unknown");  			aim_ssi_addgroups(sess, conn, &newgroup, 1);  		} diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 5522c0fc..d38f99fb 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -79,6 +79,28 @@ static void purple_init( account_t *acc )  	set_t *s;  	char help_title[64];  	GString *help; +	static gboolean dir_fixed = FALSE; +	 +	/* Layer violation coming up: Making an exception for libpurple here. +	   Dig in the IRC state a bit to get a username. Ideally we should +	   check if s/he identified but this info doesn't seem *that* important. +	   It's just that fecking libpurple can't *not* store this shit. +	    +	   Remember that libpurple is not really meant to be used on public +	   servers anyway! */ +	if( !dir_fixed ) +	{ +		irc_t *irc = acc->bee->ui_data; +		char *dir; +		 +		dir = g_strdup_printf( "%s/purple/%s", global.conf->configdir, irc->user->nick ); +		purple_util_set_user_dir( dir ); +		g_free( dir ); +		 +		purple_blist_load(); +		purple_prefs_load(); +		dir_fixed = TRUE; +	}  	help = g_string_new( "" );  	g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:", @@ -253,7 +275,8 @@ static void purple_login( account_t *acc )  	struct im_connection *ic = imcb_new( acc );  	PurpleAccount *pa; -	if( local_bee != NULL && local_bee != acc->bee ) +	if( ( local_bee != NULL && local_bee != acc->bee ) || +	    ( global.conf->runmode == RUNMODE_DAEMON && !getenv( "BITLBEE_DEBUG" ) ) )  	{  		imcb_error( ic,  "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "  		                 "Please use inetd or ForkDaemon mode instead." ); @@ -351,6 +374,9 @@ static char *set_eval_display_name( set_t *set, char *value )  	account_t *acc = set->data;  	struct im_connection *ic = acc->ic; +	if( ic ) +		imcb_log( ic, "Changing display_name not currently supported with libpurple!" ); +	  	return NULL;  } @@ -516,7 +542,7 @@ void purple_chat_leave( struct groupchat *gc )  	purple_conversation_destroy( pc );  } -struct groupchat *purple_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password ) +struct groupchat *purple_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password, set_t **sets )  {  	PurpleAccount *pa = ic->proto_data;  	PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); @@ -1131,6 +1157,7 @@ void purple_initmodule()  	struct prpl funcs;  	GList *prots;  	GString *help; +	char *dir;  	if( B_EV_IO_READ != PURPLE_INPUT_READ ||  	    B_EV_IO_WRITE != PURPLE_INPUT_WRITE ) @@ -1139,7 +1166,10 @@ void purple_initmodule()  		exit( 1 );  	} -	purple_util_set_user_dir( "/tmp" ); +	dir = g_strdup_printf( "%s/purple", global.conf->configdir ); +	purple_util_set_user_dir( dir ); +	g_free( dir ); +	  	purple_debug_set_enabled( FALSE );  	purple_core_set_ui_ops( &bee_core_uiops );  	purple_eventloop_set_ui_ops( &glib_eventloops ); @@ -1150,12 +1180,7 @@ void purple_initmodule()  		abort();  	} -	/* This seems like stateful shit we don't want... */  	purple_set_blist( purple_blist_new() ); -	purple_blist_load(); -	 -	/* Meh? */ -	purple_prefs_load();  	/* No, really. So far there were ui_ops for everything, but now suddenly  	   one needs to use signals for typing notification stuff. :-( */ @@ -1220,7 +1245,8 @@ void purple_initmodule()  	}  	g_string_append( help, "\n\nFor used protocols, more information about available " -	                 "settings can be found using \x02help purple <protocol name>\x02" ); +	                 "settings can be found using \x02help purple <protocol name>\x02 " +	                 "(create an account using that protocol first!)" );  	/* Add a simple dynamically-generated help item listing all  	   the supported protocols. */ diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 1bc596eb..4ece97db 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -28,6 +28,16 @@  #include "twitter_lib.h"  #include "url.h" +#define twitter_msg( ic, fmt... ) \ +	do {                                                        \ +		struct twitter_data *td = ic->proto_data;           \ +		if( td->home_timeline_gc )                          \ +			imcb_chat_log( td->home_timeline_gc, fmt ); \ +		else                                                \ +			imcb_log( ic, fmt );                        \ +	} while( 0 ); +		 +  /**   * Main loop function   */ @@ -435,6 +445,8 @@ static void twitter_handle_command( struct im_connection *ic, char *message )  		/* TODO: User feedback. */  		if( id )  			twitter_status_destroy( ic, id ); +		else +			twitter_msg( ic, "Could not undo last action" );  		g_free( cmds );  		return; @@ -466,6 +478,9 @@ static void twitter_handle_command( struct im_connection *ic, char *message )  		td->last_status_id = 0;  		if( id )  			twitter_status_retweet( ic, id ); +		else +			twitter_msg( ic, "User `%s' does not exist or didn't " +			                 "post any statuses recently", cmd[1] );  		g_free( cmds );  		return; | 
