Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision 0baed0da940c0d82280a5674d7fa8ad06d449384)
+++ protocols/msn/msn.h	(revision 21029d0fe2b7b11512d2cc67495d590e9df53f85)
@@ -2,5 +2,5 @@
   * 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                *
   \********************************************************************/
 
@@ -40,4 +40,8 @@
 #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_SB_NEW         -24062002
@@ -161,4 +165,5 @@
 char *msn_http_encode( const char *input );
 void msn_msgq_purge( struct im_connection *ic, GSList **list );
+char *msn_p11_challenge( char *challenge );
 
 /* tables.c */
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision 0baed0da940c0d82280a5674d7fa8ad06d449384)
+++ protocols/msn/msn_util.c	(revision 21029d0fe2b7b11512d2cc67495d590e9df53f85)
@@ -2,5 +2,5 @@
   * 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                *
   \********************************************************************/
 
@@ -26,4 +26,5 @@
 #include "nogaim.h"
 #include "msn.h"
+#include "md5.h"
 #include <ctype.h>
 
@@ -377,2 +378,95 @@
 	g_string_free( ret, TRUE );
 }
+
+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;
+}
