diff options
| -rw-r--r-- | lib/misc.c | 7 | ||||
| -rw-r--r-- | protocols/msn/Makefile | 2 | ||||
| -rw-r--r-- | protocols/msn/msn.c | 2 | ||||
| -rw-r--r-- | protocols/msn/msn.h | 12 | ||||
| -rw-r--r-- | protocols/msn/msn_util.c | 95 | ||||
| -rw-r--r-- | protocols/msn/ns.c | 2 | ||||
| -rw-r--r-- | protocols/msn/sb.c | 9 | ||||
| -rw-r--r-- | protocols/msn/soap.c | 256 | ||||
| -rw-r--r-- | protocols/msn/soap.h | 87 | 
9 files changed, 462 insertions, 10 deletions
| @@ -297,11 +297,10 @@ void http_decode( char *s )  /* This fuction is safe, but make sure you call it safely as well! */  void http_encode( char *s )  { -	char *t; +	char t[strlen(s)+1];  	int i, j; -	t = g_strdup( s ); -	 +	strcpy( t, s );  	for( i = j = 0; t[i]; i ++, j ++ )  	{  		/* Warning: isalnum() is locale-aware, so don't use it here! */ @@ -319,8 +318,6 @@ void http_encode( char *s )  		}  	}  	s[j] = 0; -	 -	g_free( t );  }  /* Strip newlines from a string. Modifies the string passed to it. */  diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 781482f5..b9c7ed28 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 passport.o sb.o soap.o tables.o  LFLAGS += -r diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 60d58532..6222e1b6 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -102,6 +102,8 @@ static void msn_logout( struct im_connection *ic )  		while( md->groupcount > 0 )  			g_free( md->grouplist[--md->groupcount] );  		g_free( md->grouplist ); +		g_free( md->passport_token ); +		g_free( md->lock_key );  		while( md->grpq )  		{ diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 31683cb5..f060000a 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                                                           */ @@ -41,6 +41,13 @@  #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 MSNP11_PROD_KEY "O4BG@C7BWLYQX?5G" +#define MSNP11_PROD_ID  "PROD01065C%ZFN6F" +  #define MSN_SB_NEW         -24062002  #define MSN_MESSAGE_HEADERS "MIME-Version: 1.0\r\n" \ @@ -68,6 +75,8 @@ struct msn_data  	struct msn_handler_data *handler;  	int trId; +	char *passport_token; +	char *lock_key;  	GSList *msgq, *grpq;  	GSList *switchboards; @@ -174,6 +183,7 @@ 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 );  /* tables.c */  const struct msn_away_state *msn_away_state_by_number( int number ); diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 23447403..954ee716 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,6 +25,7 @@  #include "nogaim.h"  #include "msn.h" +#include "md5.h"  #include <ctype.h>  int msn_write( struct im_connection *ic, char *s, int len ) @@ -443,3 +444,95 @@ gboolean msn_set_display_name( struct im_connection *ic, const char *rawname )  	return msn_write( ic, buf, strlen( buf ) ) != 0;  } + +unsigned int little_endian( unsigned int dw ) +{ +#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN +	return dw; +#else +	/* We're still not sure if this machine is big endian since the +	   constants above are not that portable. Don't swap bytes, just +	   force-compose a 32-bit little endian integer. */ +	unsigned int ret = 0, i; +	char *dst = (char*) (&ret + 1); +	 +	for (i = 0; i < 4; i ++) +	{ +		*(--dst) = dw >> 24; +		dw <<= 8; +	} +	 +	return ret; +#endif +} + +/* 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] = little_endian(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]   = little_endian(chlStringParts[i]); +		chlStringParts[i+1] = little_endian(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] = little_endian(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; +} diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 0be9e727..40c4cdec 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -782,6 +782,8 @@ static void msn_auth_got_passport_token( struct msn_auth_data *mad )  	{  		char buf[1024]; +		md->passport_token = g_strdup( mad->token ); +		  		g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token );  		msn_write( ic, buf, strlen( buf ) );  	} diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index cb5789b8..10425708 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -28,7 +28,7 @@  #include "msn.h"  #include "passport.h"  #include "md5.h" -#include "invitation.h" +#include "soap.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 ); @@ -624,7 +624,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 )  		{ diff --git a/protocols/msn/soap.c b/protocols/msn/soap.c new file mode 100644 index 00000000..82ecfea2 --- /dev/null +++ b/protocols/msn/soap.c @@ -0,0 +1,256 @@ +/** soap.c + * + * SOAP-related functions. Some manager at Microsoft apparently thought + * MSNP wasn't XMLy enough so someone stepped up and changed that. This + * is the result. + * + * Copyright (C) 2010 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 "soap.h" +#include "msn.h" +#include "bitlbee.h" +#include "url.h" +#include "misc.h" +#include "base64.h" +#include "xmltree.h" +#include <ctype.h> +#include <errno.h> + +typedef enum +{ +	MSN_SOAP_OK, +	MSN_SOAP_RETRY, +	MSN_SOAP_ABORT, +} msn_soap_result_t; + +struct msn_soap_req_data; + +typedef int (*msn_soap_func) ( struct msn_soap_req_data * ); + +struct msn_soap_req_data +{ +	void *data; +	struct im_connection *ic; +	int ttl; +	 +	char *url, *action, *payload; +	struct http_request *http_req; +	 +	const struct xt_handler_entry *xml_parser; +	msn_soap_func build_request, handle_response, free_data; +}; + +static int msn_soap_send_request( struct msn_soap_req_data *req ); + +static int msn_soap_start( struct im_connection *ic, +                    void *data, +                    msn_soap_func build_request, +                    const struct xt_handler_entry *xml_parser, +                    msn_soap_func handle_response, +                    msn_soap_func free_data ) +{ +	struct msn_soap_req_data *req = g_new0( struct msn_soap_req_data, 1 ); +	 +	req->ic = ic; +	req->data = data; +	req->xml_parser = xml_parser; +	req->build_request = build_request; +	req->handle_response = handle_response; +	req->free_data = free_data; +	req->ttl = 3; +	 +	return msn_soap_send_request( req ); +} + +static void msn_soap_handle_response( struct http_request *http_req ); + +static int msn_soap_send_request( struct msn_soap_req_data *soap_req ) +{ +	char *http_req; +	url_t url; +	 +	soap_req->build_request( soap_req ); +	 +	url_set( &url, soap_req->url ); +	http_req = g_strdup_printf( SOAP_HTTP_REQUEST, url.file, url.host, +		soap_req->action, strlen( soap_req->payload ), soap_req->payload ); +	 +	soap_req->http_req = http_dorequest( url.host, url.port, url.proto == PROTO_HTTPS, +		http_req, msn_soap_handle_response, soap_req ); +	 +	g_free( http_req ); +	 +	return soap_req->http_req != NULL; +} + +static void msn_soap_handle_response( struct http_request *http_req ) +{ +	struct msn_soap_req_data *soap_req = http_req->data; +	int st; +	 +	if( http_req->body_size > 0 ) +	{ +		struct xt_parser *parser; +		 +		parser = xt_new( soap_req->xml_parser, soap_req ); +		xt_feed( parser, http_req->reply_body, http_req->body_size ); +		xt_handle( parser, NULL, -1 ); +		xt_free( parser ); +	} +	 +	st = soap_req->handle_response( soap_req ); +	 +	g_free( soap_req->url ); +	g_free( soap_req->action ); +	g_free( soap_req->payload ); +	soap_req->url = soap_req->action = soap_req->payload = NULL; +	 +	if( st == MSN_SOAP_RETRY && --soap_req->ttl ) +		msn_soap_send_request( soap_req ); +	else +	{ +		soap_req->free_data( soap_req ); +		g_free( soap_req ); +	} +} + + +/* oim_send: Sending offline messages */ + +struct msn_soap_oim_send_data +{ +	char *to; +	char *msg; +	int number; +	int need_retry; +}; + +static int msn_soap_oim_build_request( struct msn_soap_req_data *soap_req ) +{ +	struct msn_soap_oim_send_data *oim = soap_req->data; +	struct im_connection *ic = soap_req->ic; +	struct msn_data *md = ic->proto_data; +	char *display_name_b64; +	 +	display_name_b64 = tobase64( ic->displayname ); +	 +	soap_req->url = g_strdup( SOAP_OIM_SEND_URL ); +	soap_req->action = g_strdup( SOAP_OIM_ACTION_URL ); +	soap_req->payload = g_markup_printf_escaped( SOAP_OIM_SEND_PAYLOAD, +		ic->acc->user, display_name_b64, oim->to, md->passport_token, +		MSNP11_PROD_ID, md->lock_key ? : "", oim->number, oim->number, oim->msg ); +	 +	g_free( display_name_b64 ); +	 +	return 1; +} + +static xt_status msn_soap_oim_send_challenge( struct xt_node *node, gpointer data ) +{ +	struct msn_soap_req_data *soap_req = data; +	struct msn_soap_oim_send_data *oim = soap_req->data; +	struct im_connection *ic = soap_req->ic; +	struct msn_data *md = ic->proto_data; +	 +	g_free( md->lock_key ); +	md->lock_key = msn_p11_challenge( node->text ); +	 +	oim->need_retry = 1; +	 +	return XT_HANDLED; +} + +static const struct xt_handler_entry msn_soap_oim_send_parser[] = { +	{ "LockKeyChallenge", "detail", msn_soap_oim_send_challenge }, +	{ NULL,               NULL,     NULL                        } +}; + +static int msn_soap_oim_handle_response( struct msn_soap_req_data *soap_req ) +{ +	struct msn_soap_oim_send_data *oim = soap_req->data; +	 +	if( soap_req->http_req->status_code == 500 && oim->need_retry && soap_req->ttl > 0 ) +	{ +		oim->need_retry = 0; +		return MSN_SOAP_RETRY; +	} +	else if( soap_req->http_req->status_code == 200 ) +	{ +		imcb_log( soap_req->ic, "Offline message successfully delivered to %s", oim->to ); +		return MSN_SOAP_OK; +	} +	else +	{ +		imcb_log( soap_req->ic, "Failed to deliver offline message to %s:\n%s", oim->to, oim->msg ); +		return MSN_SOAP_ABORT; +	} +} + +static int msn_soap_oim_free_data( struct msn_soap_req_data *soap_req ) +{ +	struct msn_soap_oim_send_data *oim = soap_req->data; +	 +	g_free( oim->to ); +	g_free( oim->msg ); +	g_free( oim ); +	 +	return 0; +} + +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 ); +	} +} diff --git a/protocols/msn/soap.h b/protocols/msn/soap.h new file mode 100644 index 00000000..3db2d59d --- /dev/null +++ b/protocols/msn/soap.h @@ -0,0 +1,87 @@ +/* soap.h + * + * SOAP-related functions. Some manager at Microsoft apparently thought + * MSNP wasn't XMLy enough so someone stepped up and changed that. This + * is the result. + * + * Copyright (C) 2010 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/ for lots of info on this! */ + +#ifndef __SOAP_H__ +#define __SOAP_H__ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#endif +#include "nogaim.h" + + +#define SOAP_HTTP_REQUEST \ +"POST %s HTTP/1.0\r\n" \ +"Host: %s\r\n" \ +"Accept: */*\r\n" \ +"SOAPAction: \"%s\"\r\n" \ +"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \ +"Content-Type: text/xml; charset=utf-8\r\n" \ +"Content-Length: %d\r\n" \ +"Cache-Control: no-cache\r\n" \ +"\r\n" \ +"%s" + + +#define SOAP_OIM_SEND_URL "https://ows.messenger.msn.com/OimWS/oim.asmx" +#define SOAP_OIM_ACTION_URL "http://messenger.msn.com/ws/2004/09/oim/Store" + +#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=\"MSNP13\" buildVer=\"8.0.0328\"/>" \ +  "<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: {89527393-8723-4F4F-8005-287532973298}\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 ); + +#endif /* __SOAP_H__ */ | 
