aboutsummaryrefslogtreecommitdiffstats
path: root/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'protocols')
-rw-r--r--protocols/msn/Makefile2
-rw-r--r--protocols/msn/msn.c2
-rw-r--r--protocols/msn/msn.h12
-rw-r--r--protocols/msn/msn_util.c95
-rw-r--r--protocols/msn/ns.c2
-rw-r--r--protocols/msn/sb.c9
-rw-r--r--protocols/msn/soap.c256
-rw-r--r--protocols/msn/soap.h87
8 files changed, 460 insertions, 5 deletions
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__ */