changeset 31110:bd7942308234

propagate from branch 'im.pidgin.pidgin' (head d813bbb8c323184987e92c9bd801fd7e64262104) to branch 'im.pidgin.cpw.qulogic.msnp16' (head ae093be5fdd4311e82faf775ce3c3690f0a7df65)
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Sun, 13 Dec 2009 09:44:27 +0000
parents fc7798a661f7 (diff) ec101fb81111 (current diff)
children d32c0a73c689
files
diffstat 10 files changed, 392 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/msn/msn.h	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/msn.h	Sun Dec 13 09:44:27 2009 +0000
@@ -52,9 +52,9 @@
 #define MSN_SERVER "messenger.hotmail.com"
 #define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com"
 #define MSN_PORT 1863
-#define WLM_PROT_VER		15
+#define WLM_PROT_VER		16
 
-#define WLM_MAX_PROTOCOL	15
+#define WLM_MAX_PROTOCOL	16
 #define WLM_MIN_PROTOCOL	15
 
 #define MSN_TYPING_RECV_TIMEOUT 6
@@ -137,15 +137,14 @@
 
 } MsnClientVerId;
 
-#define MSN_CLIENT_ID_VERSION      MSN_CLIENT_VER_7_0
+#define MSN_CLIENT_ID_VERSION      MSN_CLIENT_VER_9_0
 #define MSN_CLIENT_ID_CAPABILITIES (MSN_CLIENT_CAP_PACKET|MSN_CLIENT_CAP_INK_GIF|MSN_CLIENT_CAP_VOICEIM)
+#define MSN_CLIENT_ID_EXT_CAPS     (0)
 
 #define MSN_CLIENT_ID \
 	((MSN_CLIENT_ID_VERSION    << 24) | \
 	 (MSN_CLIENT_ID_CAPABILITIES))
 
-#define MSN_CLIENT_EXT_ID 0
-
 gboolean msn_email_is_valid(const char *passport);
 void msn_act_id(PurpleConnection *gc, const char *entry);
 void msn_send_privacy(PurpleConnection *gc);
--- a/libpurple/protocols/msn/notification.c	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/notification.c	Sun Dec 13 09:44:27 2009 +0000
@@ -161,7 +161,10 @@
 
 	msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END);
 
-	msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s", ticket, response);
+	if (session->protocol_ver >= 16)
+		msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s %s", ticket, response, session->guid);
+	else
+		msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s", ticket, response);
 }
 
 static void
@@ -233,20 +236,22 @@
 	MsnSession *session;
 	PurpleAccount *account;
 	gboolean protocol_supported = FALSE;
-	char proto_str[8];
+	int proto_ver;
 	size_t i;
 
 	session = cmdproc->session;
 	account = session->account;
 
-	g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver);
-
+	session->protocol_ver = 0;
 	for (i = 1; i < cmd->param_count; i++)
 	{
-		if (!strcmp(cmd->params[i], proto_str))
-		{
-			protocol_supported = TRUE;
-			break;
+		if (sscanf(cmd->params[i], "MSNP%d", &proto_ver) == 1) {
+			if (proto_ver >= WLM_MIN_PROTOCOL
+			 && proto_ver <= WLM_MAX_PROTOCOL
+			 && proto_ver > session->protocol_ver) {
+				protocol_supported = TRUE;
+				session->protocol_ver = proto_ver;
+			}
 		}
 	}
 
@@ -257,6 +262,8 @@
 		return;
 	}
 
+	purple_debug_info("msn", "Negotiated protocol version %d with the server.\n", session->protocol_ver);
+
 	/*
 	 * Windows Live Messenger 8.5
 	 * Notice :CVR String discriminate!
@@ -1081,7 +1088,8 @@
 	PurpleConnection *gc;
 	MsnUser *user;
 	MsnObject *msnobj = NULL;
-	unsigned long clientid;
+	unsigned long clientid, extcaps;
+	char *extcap_str;
 	int networkid = 0;
 	const char *state, *passport;
 	char *friendly;
@@ -1102,7 +1110,11 @@
 		/* Yahoo! Buddy, looks like */
 		networkid = atoi(cmd->params[3]);
 		friendly = g_strdup(purple_url_decode(cmd->params[4]));
-		clientid = strtoul(cmd->params[5], NULL, 10);
+		clientid = strtoul(cmd->params[5], &extcap_str, 10);
+		if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+			extcaps = strtoul(extcap_str+1, NULL, 10);
+		else
+			extcaps = 0;
 
 		/* cmd->params[7] seems to be a URL to a Yahoo! icon:
 				https://sec.yimg.com/i/us/nt/b/purpley.1.0.png
@@ -1112,7 +1124,11 @@
 		/* MSNP14+ with Display Picture object */
 		networkid = atoi(cmd->params[3]);
 		friendly = g_strdup(purple_url_decode(cmd->params[4]));
-		clientid = strtoul(cmd->params[5], NULL, 10);
+		clientid = strtoul(cmd->params[5], &extcap_str, 10);
+		if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+			extcaps = strtoul(extcap_str+1, NULL, 10);
+		else
+			extcaps = 0;
 		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6]));
 	} else if (cmd->param_count == 6) {
 		/* Yes, this is 5. The friendly name could start with a number,
@@ -1121,17 +1137,29 @@
 			/* MSNP14 without Display Picture object */
 			networkid = atoi(cmd->params[3]);
 			friendly = g_strdup(purple_url_decode(cmd->params[4]));
-			clientid = strtoul(cmd->params[5], NULL, 10);
+			clientid = strtoul(cmd->params[5], &extcap_str, 10);
+			if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+				extcaps = strtoul(extcap_str+1, NULL, 10);
+			else
+				extcaps = 0;
 		} else {
 			/* MSNP8+ with Display Picture object */
 			friendly = g_strdup(purple_url_decode(cmd->params[3]));
-			clientid = strtoul(cmd->params[4], NULL, 10);
+			clientid = strtoul(cmd->params[4], &extcap_str, 10);
+			if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+				extcaps = strtoul(extcap_str+1, NULL, 10);
+			else
+				extcaps = 0;
 			msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
 		}
 	} else if (cmd->param_count == 5) {
 		/* MSNP8+ without Display Picture object */
 		friendly = g_strdup(purple_url_decode(cmd->params[3]));
-		clientid = strtoul(cmd->params[4], NULL, 10);
+		clientid = strtoul(cmd->params[4], &extcap_str, 10);
+		if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+			extcaps = strtoul(extcap_str+1, NULL, 10);
+		else
+			extcaps = 0;
 	} else {
 		purple_debug_warning("msn", "Received ILN with unknown number of parameters.\n");
 		return;
@@ -1146,6 +1174,7 @@
 
 	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+');
 	msn_user_set_clientid(user, clientid);
+	msn_user_set_extcaps(user, extcaps);
 	msn_user_set_network(user, networkid);
 
 	msn_user_set_state(user, state);
@@ -1284,7 +1313,8 @@
 	PurpleConnection *gc;
 	MsnUser *user;
 	MsnObject *msnobj;
-	unsigned long clientid;
+	unsigned long clientid, extcaps;
+	char *extcap_str;
 	int networkid;
 	const char *state, *passport, *friendly;
 
@@ -1297,7 +1327,10 @@
 	networkid = atoi(cmd->params[2]);
 	friendly = purple_url_decode(cmd->params[3]);
 
-	user = msn_userlist_find_user(session->userlist, passport);
+	if (g_str_equal(passport, session->user->passport))
+		user = session->user;
+	else
+		user = msn_userlist_find_user(session->userlist, passport);
 	if (user == NULL) return;
 
 	if (msn_user_set_friendly_name(user, friendly))
@@ -1315,10 +1348,15 @@
 		msn_user_set_object(user, NULL);
 	}
 
-	clientid = strtoul(cmd->params[4], NULL, 10);
+	clientid = strtoul(cmd->params[4], &extcap_str, 10);
+	if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+		extcaps = strtoul(extcap_str+1, NULL, 10);
+	else
+		extcaps = 0;
 	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+');
 
 	msn_user_set_clientid(user, clientid);
+	msn_user_set_extcaps(user, extcaps);
 	msn_user_set_network(user, networkid);
 
 	msn_user_set_state(user, state);
@@ -1599,6 +1637,42 @@
 	/*get the payload content*/
 }
 
+static void
+parse_user_endpoints(MsnUser *user, xmlnode *payloadNode)
+{
+	xmlnode *epNode, *capsNode;
+	MsnUserEndpoint data;
+	const char *id;
+	char *caps, *tmp;
+
+	purple_debug_info("msn", "Get EndpointData\n");
+
+	for (epNode = xmlnode_get_child(payloadNode, "EndpointData");
+	     epNode;
+	     epNode = xmlnode_get_next_twin(epNode)) {
+		id = xmlnode_get_attrib(epNode, "id");
+		capsNode = xmlnode_get_child(epNode, "Capabilities");
+
+		if (capsNode != NULL) {
+			caps = xmlnode_get_data(capsNode);
+
+			data.clientid = strtoul(caps, &tmp, 10);
+			if (tmp && *tmp)
+				data.extcaps = strtoul(tmp + 1, NULL, 10);
+			else
+				data.extcaps = 0;
+
+			g_free(caps);
+
+		} else {
+			data.clientid = 0;
+			data.extcaps = 0;
+		}
+
+		msn_user_set_endpoint_data(user, id, &data);
+	}
+}
+
 /*
  * Get the UBX's PSM info
  * Post it to the User status
@@ -1612,6 +1686,7 @@
 	PurpleAccount *account;
 	MsnUser *user;
 	const char *passport;
+	xmlnode *payloadNode;
 	char *psm_str, *str;
 	CurrentMedia media = {CURRENT_MEDIA_UNKNOWN, NULL, NULL, NULL};
 
@@ -1619,7 +1694,10 @@
 	account = session->account;
 
 	passport = cmd->params[0];
-	user = msn_userlist_find_user(session->userlist, passport);
+	if (g_str_equal(passport, session->user->passport))
+		user = session->user;
+	else
+		user = msn_userlist_find_user(session->userlist, passport);
 	if (user == NULL) {
 		char *str = g_strndup(payload, len);
 		purple_debug_info("msn", "unknown user %s, payload is %s\n",
@@ -1629,11 +1707,22 @@
 	}
 
 	if (len != 0) {
-		psm_str = msn_get_psm(cmd->payload,len);
+		payloadNode = xmlnode_from_str(payload, len);
+		if (!payloadNode) {
+			purple_debug_error("msn", "UBX XML parse Error!\n");
+
+			msn_user_set_statusline(user, NULL);
+			msn_user_set_currentmedia(user, NULL);
+
+			msn_user_update(user);
+			return;
+		}
+
+		psm_str = msn_get_psm(payloadNode);
 		msn_user_set_statusline(user, psm_str);
 		g_free(psm_str);
 
-		str = msn_get_currentmedia(cmd->payload, len);
+		str = msn_get_currentmedia(payloadNode);
 		if (msn_parse_currentmedia(str, &media))
 			msn_user_set_currentmedia(user, &media);
 		else
@@ -1643,6 +1732,10 @@
 		g_free(media.artist);
 		g_free(str);
 
+		parse_user_endpoints(user, payloadNode);
+
+		xmlnode_free(payloadNode);
+
 	} else {
 		msn_user_set_statusline(user, NULL);
 		msn_user_set_currentmedia(user, NULL);
@@ -1676,6 +1769,86 @@
 	cmd->payload_len = atoi(cmd->params[1]);
 }
 
+void
+msn_notification_send_uux(MsnSession *session, const char *payload)
+{
+	MsnTransaction *trans;
+	MsnCmdProc *cmdproc;
+	size_t len = strlen(payload);
+
+	cmdproc = session->notification->cmdproc;
+	purple_debug_misc("msn", "Sending UUX command with payload: %s\n", payload);
+	trans = msn_transaction_new(cmdproc, "UUX", "%" G_GSIZE_FORMAT, len);
+	msn_transaction_set_payload(trans, payload, len);
+	msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+void msn_notification_send_uux_endpointdata(MsnSession *session)
+{
+	xmlnode *epDataNode;
+	xmlnode *capNode;
+	char *caps;
+	char *payload;
+	int length;
+
+	epDataNode = xmlnode_new("EndpointData");
+
+	capNode = xmlnode_new_child(epDataNode, "Capabilities");
+	caps = g_strdup_printf("%d:%02d", MSN_CLIENT_ID_CAPABILITIES, MSN_CLIENT_ID_EXT_CAPS);
+	xmlnode_insert_data(capNode, caps, -1);
+	g_free(caps);
+
+	payload = xmlnode_to_str(epDataNode, &length);
+
+	msn_notification_send_uux(session, payload);
+
+	xmlnode_free(epDataNode);
+	g_free(payload);
+}
+
+void msn_notification_send_uux_private_endpointdata(MsnSession *session)
+{
+	xmlnode *private;
+	xmlnode *epname;
+	xmlnode *idle;
+	xmlnode *client_type;
+	xmlnode *state;
+	char *payload;
+	int length;
+
+	private = xmlnode_new("PrivateEndPointData");
+
+	/* TODO: "Pidgin" is a temp EndPointName.. we must use hostid or some.*/
+	epname = xmlnode_new_child(private, "EpName");
+	xmlnode_insert_data(epname, "Pidgin", -1);
+
+	idle = xmlnode_new_child(private, "Idle");
+	xmlnode_insert_data(idle, "false", -1);
+
+	/* TODO: support different client types */
+	/* ClientType info (from amsn guys):
+		0: None
+		1: Computer
+		2: Website
+		3: Mobile / none
+		4: Xbox / phone /mobile
+		9: MsnGroup
+		32: Email member, currently Yahoo!
+	*/
+	client_type = xmlnode_new_child(private, "ClientType");
+	xmlnode_insert_data(client_type, "1", -1);
+
+	state = xmlnode_new_child(private, "State");
+	xmlnode_insert_data(state, msn_state_get_text(msn_state_from_account(session->account)), -1);
+
+	payload = xmlnode_to_str(private, &length);
+
+	msn_notification_send_uux(session, payload);
+
+	xmlnode_free(private);
+	g_free(payload);
+}
+
 /**************************************************************************
  * Message Types
  **************************************************************************/
--- a/libpurple/protocols/msn/notification.h	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/notification.h	Sun Dec 13 09:44:27 2009 +0000
@@ -82,6 +82,12 @@
 void msn_notification_disconnect(MsnNotification *notification);
 void msn_notification_dump_contact(MsnSession *session);
 
+void msn_notification_send_uux(MsnSession *session, const char *payload);
+
+void msn_notification_send_uux_endpointdata(MsnSession *session);
+
+void msn_notification_send_uux_private_endpointdata(MsnSession *session);
+
 /**
  * Closes a notification.
  *
--- a/libpurple/protocols/msn/session.c	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/session.c	Sun Dec 13 09:44:27 2009 +0000
@@ -22,6 +22,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #include "msn.h"
+#include "msnutils.h"
 #include "session.h"
 #include "notification.h"
 #include "oim.h"
@@ -45,7 +46,9 @@
 								 purple_account_get_username(account), NULL);
 	session->oim = msn_oim_new(session);
 
-	session->protocol_ver = WLM_PROT_VER;
+	session->protocol_ver = 0;
+
+	session->guid = rand_guid();
 
 	return session;
 }
@@ -90,6 +93,7 @@
 	msn_userlist_destroy(session->userlist);
 
 	g_free(session->psm);
+	g_free(session->guid);
 	g_free(session->abch_cachekey);
 #if 0
 	g_free(session->blocked_text);
@@ -448,6 +452,11 @@
 		msn_session_sync_users(session);
 	}
 
+	if (session->protocol_ver >= 16) {
+		/* TODO: Send this when updating status instead? */
+		msn_notification_send_uux_endpointdata(session);
+		/*msn_notification_send_uux_private_endpointdata(session);*/
+	}
 	msn_change_status(session);
 }
 
--- a/libpurple/protocols/msn/session.h	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/session.h	Sun Dec 13 09:44:27 2009 +0000
@@ -126,6 +126,7 @@
 
 	GHashTable *soap_table;
 	guint soap_cleanup_handle;
+	char *guid;
 };
 
 /**
--- a/libpurple/protocols/msn/state.c	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/state.c	Sun Dec 13 09:44:27 2009 +0000
@@ -26,6 +26,7 @@
 #include "core.h"
 
 #include "msn.h"
+#include "notification.h"
 #include "state.h"
 
 static const char *away_text[] =
@@ -42,10 +43,6 @@
 	N_("Available")
 };
 
-/* Local Function Prototype*/
-static char *msn_build_psm(const char *psmstr,const char *mediastr,
-						   const char *guidstr);
-
 /*
  * WLM media PSM info build prcedure
  *
@@ -55,7 +52,7 @@
  *	<CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia>"
  */
 static char *
-msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr)
+msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr, guint protocol_ver)
 {
 	xmlnode *dataNode,*psmNode,*mediaNode,*guidNode;
 	char *result;
@@ -81,6 +78,12 @@
 	}
 	xmlnode_insert_child(dataNode, guidNode);
 
+	if (protocol_ver >= 16) {
+		/* TODO: What is this for? */
+		xmlnode *ddpNode = xmlnode_new("DDP");
+		xmlnode_insert_child(dataNode, ddpNode);
+	}
+
 	result = xmlnode_to_str(dataNode, &length);
 	xmlnode_free(dataNode);
 	return result;
@@ -158,55 +161,39 @@
 	return parsed;
 }
 
-/* get the CurrentMedia info from the XML string */
+/* get the CurrentMedia info from the XML node */
 char *
-msn_get_currentmedia(char *xml_str, gsize len)
+msn_get_currentmedia(xmlnode *payloadNode)
 {
-	xmlnode *payloadNode, *currentmediaNode;
+	xmlnode *currentmediaNode;
 	char *currentmedia;
 
 	purple_debug_info("msn", "Get CurrentMedia\n");
-	payloadNode = xmlnode_from_str(xml_str, len);
-	if (!payloadNode) {
-		purple_debug_error("msn", "PSM XML parse Error!\n");
-		return NULL;
-	}
 	currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia");
 	if (currentmediaNode == NULL) {
 		purple_debug_info("msn", "No CurrentMedia Node\n");
-		xmlnode_free(payloadNode);
 		return NULL;
 	}
 	currentmedia = xmlnode_get_data(currentmediaNode);
 
-	xmlnode_free(payloadNode);
-
 	return currentmedia;
 }
 
-/*get the PSM info from the XML string*/
+/* Get the PSM info from the XML node */
 char *
-msn_get_psm(char *xml_str, gsize len)
+msn_get_psm(xmlnode *payloadNode)
 {
-	xmlnode *payloadNode, *psmNode;
+	xmlnode *psmNode;
 	char *psm;
 
 	purple_debug_info("msn", "msn get PSM\n");
-	payloadNode = xmlnode_from_str(xml_str, len);
-	if (!payloadNode) {
-		purple_debug_error("msn", "PSM XML parse Error!\n");
-		return NULL;
-	}
 	psmNode = xmlnode_get_child(payloadNode, "PSM");
 	if (psmNode == NULL) {
 		purple_debug_info("msn", "No PSM status Node\n");
-		xmlnode_free(payloadNode);
 		return NULL;
 	}
 	psm = xmlnode_get_data(psmNode);
 
-	xmlnode_free(payloadNode);
-
 	return psm;
 }
 
@@ -252,8 +239,6 @@
 	PurpleAccount *account;
 	PurplePresence *presence;
 	PurpleStatus *status;
-	MsnCmdProc *cmdproc;
-	MsnTransaction *trans;
 	char *payload;
 	const char *statusline;
 	gchar *statusline_stripped, *media = NULL;
@@ -262,7 +247,6 @@
 	g_return_if_fail(session->notification != NULL);
 
 	account = session->account;
-	cmdproc = session->notification->cmdproc;
 
 	/* Get the PSM string from Purple's Status Line */
 	presence = purple_account_get_presence(account);
@@ -273,13 +257,11 @@
 	statusline_stripped = purple_markup_strip_html(statusline);
 	media = create_media_string(presence);
 	g_free(session->psm);
-	session->psm = msn_build_psm(statusline_stripped, media, NULL);
+	session->psm = msn_build_psm(statusline_stripped, media, session->protocol_ver >= 16 ? session->guid : NULL, session->protocol_ver);
 
 	payload = session->psm;
-	purple_debug_misc("msn", "Sending UUX command with payload: %s\n", payload);
-	trans = msn_transaction_new(cmdproc, "UUX", "%" G_GSIZE_FORMAT, strlen(payload));
-	msn_transaction_set_payload(trans, payload, strlen(payload));
-	msn_cmdproc_send_trans(cmdproc, trans);
+
+	msn_notification_send_uux(session, payload);
 
 	g_free(statusline_stripped);
 	g_free(media);
@@ -327,11 +309,16 @@
 	if (!session->logged_in)
 		return;
 
+	msn_set_psm(session);
+
 	msnobj = msn_user_get_object(user);
 
 	if (msnobj == NULL)
 	{
-		msn_cmdproc_send(cmdproc, "CHG", "%s %d", state_text, caps);
+		if (session->protocol_ver >= 16)
+			msn_cmdproc_send(cmdproc, "CHG", "%s %u:%02u 0", state_text, caps, MSN_CLIENT_ID_EXT_CAPS);
+		else
+			msn_cmdproc_send(cmdproc, "CHG", "%s %d", state_text, caps);
 	}
 	else
 	{
@@ -339,12 +326,15 @@
 
 		msnobj_str = msn_object_to_string(msnobj);
 
-		msn_cmdproc_send(cmdproc, "CHG", "%s %d %s", state_text,
-						 caps, purple_url_encode(msnobj_str));
+		if (session->protocol_ver >= 16)
+			msn_cmdproc_send(cmdproc, "CHG", "%s %u:%02u %s", state_text,
+							 caps, MSN_CLIENT_ID_EXT_CAPS, purple_url_encode(msnobj_str));
+		else
+			msn_cmdproc_send(cmdproc, "CHG", "%s %d %s", state_text,
+							 caps, purple_url_encode(msnobj_str));
 
 		g_free(msnobj_str);
 	}
-	msn_set_psm(session);
 }
 
 const char *
--- a/libpurple/protocols/msn/state.h	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/state.h	Sun Dec 13 09:44:27 2009 +0000
@@ -64,11 +64,11 @@
 /* Parse CurrentMedia string */
 gboolean msn_parse_currentmedia(const char *cmedia, CurrentMedia *media);
 
-/* Get the CurrentMedia info from the XML string */
-char * msn_get_currentmedia(char *xml_str,gsize len);
+/* Get the CurrentMedia info from the XML node */
+char * msn_get_currentmedia(xmlnode *payloadNode);
 
-/*get the PSM info from the XML string*/
-char * msn_get_psm(char *xml_str,gsize len);
+/* Get the PSM info from the XML node */
+char * msn_get_psm(xmlnode *payloadNode);
 
 MsnAwayType msn_state_from_account(PurpleAccount *account);
 
--- a/libpurple/protocols/msn/switchboard.c	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Sun Dec 13 09:44:27 2009 +0000
@@ -949,6 +949,7 @@
 	MsnTransaction *trans;
 	MsnCmdProc *cmdproc;
 	PurpleAccount *account;
+	char *username;
 
 	cmdproc = servconn->cmdproc;
 	g_return_if_fail(cmdproc != NULL);
@@ -957,24 +958,33 @@
 	swboard = cmdproc->data;
 	g_return_if_fail(swboard != NULL);
 
+	if (servconn->session->protocol_ver >= 16)
+		username = g_strdup(purple_account_get_username(account));
+	else
+		username = g_strdup_printf("%s;{%s}",
+		                           purple_account_get_username(account),
+		                           servconn->session->guid);
+
 	if (msn_switchboard_is_invited(swboard))
 	{
 		swboard->empty = FALSE;
 
 		trans = msn_transaction_new(cmdproc, "ANS", "%s %s %s",
-									purple_account_get_username(account),
+									username,
 									swboard->auth_key, swboard->session_id);
 	}
 	else
 	{
 		trans = msn_transaction_new(cmdproc, "USR", "%s %s",
-									purple_account_get_username(account),
+									username,
 									swboard->auth_key);
 	}
 
 	msn_transaction_set_error_cb(trans, ans_usr_error);
 	msn_transaction_set_data(trans, swboard);
 	msn_cmdproc_send_trans(cmdproc, trans);
+
+	g_free(username);
 }
 
 static void
--- a/libpurple/protocols/msn/user.c	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/user.c	Sun Dec 13 09:44:27 2009 +0000
@@ -39,6 +39,8 @@
 	msn_user_set_passport(user, passport);
 	msn_user_set_friendly_name(user, friendly_name);
 
+	user->endpoints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
 	return user;
 }
 
@@ -48,6 +50,9 @@
 {
 	g_return_if_fail(user != NULL);
 
+	if (user->endpoints != NULL)
+		g_hash_table_destroy(user->endpoints);
+
 	if (user->clientcaps != NULL)
 		g_hash_table_destroy(user->clientcaps);
 
@@ -229,6 +234,28 @@
 }
 
 void
+msn_user_set_endpoint_data(MsnUser *user, const char *endpoint, MsnUserEndpoint *data)
+{
+	MsnUserEndpoint *new;
+	g_return_if_fail(user != NULL);
+
+	if (data == NULL) {
+		g_hash_table_remove(user->endpoints, endpoint);
+		return;
+	}
+
+	new = g_hash_table_lookup(user->endpoints, endpoint);
+	if (!new) {
+		new = g_new0(MsnUserEndpoint, 1);
+		new->id = g_strdup(endpoint);
+		g_hash_table_insert(user->endpoints, g_strdup(endpoint), new);
+	}
+
+	new->clientid = data->clientid;
+	new->extcaps = data->extcaps;
+}
+
+void
 msn_user_set_op(MsnUser *user, int list_op)
 {
 	g_return_if_fail(user != NULL);
@@ -397,6 +424,14 @@
 }
 
 void
+msn_user_set_extcaps(MsnUser *user, guint extcaps)
+{
+	g_return_if_fail(user != NULL);
+
+	user->extcaps = extcaps;
+}
+
+void
 msn_user_set_network(MsnUser *user, MsnNetwork network)
 {
 	g_return_if_fail(user != NULL);
@@ -487,6 +522,22 @@
 	return user->clientid;
 }
 
+guint
+msn_user_get_extcaps(const MsnUser *user)
+{
+	g_return_val_if_fail(user != NULL, 0);
+
+	return user->extcaps;
+}
+
+MsnUserEndpoint *
+msn_user_get_endpoint_data(MsnUser *user, const char *endpoint)
+{
+	g_return_val_if_fail(user != NULL, NULL);
+
+	return g_hash_table_lookup(user->endpoints, endpoint);
+}
+
 MsnObject *
 msn_user_get_object(const MsnUser *user)
 {
@@ -511,3 +562,22 @@
 	return user->invite_message;
 }
 
+gboolean
+msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extcap)
+{
+	g_return_val_if_fail(user != NULL, FALSE);
+
+	if (endpoint != NULL) {
+		MsnUserEndpoint *ep = g_hash_table_lookup(user->endpoints, endpoint);
+		if (ep != NULL)
+			return (ep->clientid & capability) == capability
+			    && (ep->extcaps & extcap) == extcap;
+		else
+			return FALSE;
+	}
+
+	return (user->clientid & capability) == capability
+	    && (user->extcaps & extcap) == extcap;
+}
+
+
--- a/libpurple/protocols/msn/user.h	Sun Dec 13 09:40:58 2009 +0000
+++ b/libpurple/protocols/msn/user.h	Sun Dec 13 09:44:27 2009 +0000
@@ -72,6 +72,7 @@
 	char *friendly_name;    /**< The friendly name.             */
 
 	char * uid;				/*< User Id							*/
+	GHashTable *endpoints;	/*< Endpoint-specific data          */
 
 	const char *status;     /**< The state of the user.         */
 	char *statusline;       /**< The state of the user.         */
@@ -98,6 +99,7 @@
 	GHashTable *clientcaps; /**< The client's capabilities.     */
 
 	guint clientid;         /**< The client's ID                */
+	guint extcaps;			/**< The client's extended capabilities */
 
 	MsnNetwork networkid;   /**< The user's network             */
 
@@ -109,6 +111,16 @@
 	char *invite_message;   /**< Invite message of user request */
 };
 
+/**
+ * A specific user endpoint.
+ */
+typedef struct MsnUserEndpoint {
+	char *id;				/**< The client's endpoint ID          */
+	guint clientid;         /**< The client's ID                   */
+	guint extcaps;			/**< The client's extended capabilites */
+
+} MsnUserEndpoint;
+
 /**************************************************************************
  ** @name User API                                                        *
  **************************************************************************/
@@ -253,6 +265,16 @@
 void msn_user_set_uid(MsnUser *user, const char *uid);
 
 /**
+ * Sets endpoint data for a user.
+ *
+ * @param user     The user.
+ * @param endpoint The endpoint.
+ * @param data     The endpoint data.
+ */
+void
+msn_user_set_endpoint_data(MsnUser *user, const char *endpoint, MsnUserEndpoint *data);
+
+/**
  * Sets the client id for a user.
  *
  * @param user     The user.
@@ -261,6 +283,14 @@
 void msn_user_set_clientid(MsnUser *user, guint clientid);
 
 /**
+ * Sets the client id for a user.
+ *
+ * @param user     The user.
+ * @param extcaps  The client's extended capabilities.
+ */
+void msn_user_set_extcaps(MsnUser *user, guint extcaps);
+
+/**
  * Sets the network id for a user.
  *
  * @param user    The user.
@@ -347,6 +377,17 @@
 const char *msn_user_get_mobile_phone(const MsnUser *user);
 
 /**
+ * Gets endpoint data for a user.
+ *
+ * @param user     The user.
+ * @param endpoint The endpoint.
+ *
+ * @return The user's endpoint data.
+ */
+MsnUserEndpoint *
+msn_user_get_endpoint_data(MsnUser *user, const char *endpoint);
+
+/**
  * Returns the client id for a user.
  *
  * @param user    The user.
@@ -356,6 +397,15 @@
 guint msn_user_get_clientid(const MsnUser *user);
 
 /**
+ * Returns the extended capabilities for a user.
+ *
+ * @param user    The user.
+ *
+ * @return The user's extended capabilities.
+ */
+guint msn_user_get_extcaps(const MsnUser *user);
+
+/**
  * Returns the network id for a user.
  *
  * @param user    The user.
@@ -406,6 +456,19 @@
 void msn_user_set_op(MsnUser *user, int list_op);
 void msn_user_unset_op(MsnUser *user, int list_op);
 
+/**
+ * Checks whether a user is capable of some task.
+ *
+ * @param user       The user.
+ * @param endpoint   The endpoint. Can be @NULL to check overall capabilities.
+ * @param capability The capability (including client version).
+ * @param extcap     The extended capability.
+ *
+ * @return Whether the user supports the capability.
+ */
+gboolean
+msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extcap);
+
 /*@}*/