diff libpurple/protocols/msn/notification.c @ 31292:47b6eda87723

propagate from branch 'im.pidgin.pidgin' (head 07d0765c444a097af45c2650f54323afb900a07b) to branch 'im.pidgin.soc.2010.msn-tlc' (head f3998422a4724ab424e4e2328f58fc0504856557)
author masca@cpw.pidgin.im
date Mon, 19 Jul 2010 21:11:32 +0000
parents 88a047f92c39 c7fa7c7aca7d
children 23be655cc688
line wrap: on
line diff
--- a/libpurple/protocols/msn/notification.c	Mon Jul 19 18:25:47 2010 +0000
+++ b/libpurple/protocols/msn/notification.c	Mon Jul 19 21:11:32 2010 +0000
@@ -21,17 +21,19 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
-#include "msn.h"
+
+#include "internal.h"
+#include "cipher.h"
+#include "core.h"
+#include "debug.h"
+
 #include "notification.h"
+
 #include "contact.h"
-#include "state.h"
 #include "error.h"
 #include "msnutils.h"
-#include "page.h"
-
+#include "state.h"
 #include "userlist.h"
-#include "sync.h"
-#include "slplink.h"
 
 static MsnTable *cbs_table;
 
@@ -92,6 +94,7 @@
 {
 	MsnCmdProc *cmdproc;
 	MsnSession *session;
+	MsnTransaction *trans;
 	PurpleAccount *account;
 	GString *vers;
 	const char *ver_str;
@@ -117,7 +120,8 @@
 
 	/* Skip the initial space */
 	ver_str = (vers->str + 1);
-	msn_cmdproc_send(cmdproc, "VER", "%s", ver_str);
+	trans = msn_transaction_new(cmdproc, "VER", "%s", ver_str);
+	msn_cmdproc_send_trans(cmdproc, trans);
 
 	g_string_free(vers, TRUE);
 }
@@ -156,22 +160,30 @@
 msn_got_login_params(MsnSession *session, const char *ticket, const char *response)
 {
 	MsnCmdProc *cmdproc;
+	MsnTransaction *trans;
 
 	cmdproc = session->notification->cmdproc;
 
 	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)
+		trans = msn_transaction_new(cmdproc, "USR", "SSO S %s %s %s", ticket, response, session->guid);
+	else
+		trans = msn_transaction_new(cmdproc, "USR", "SSO S %s %s", ticket, response);
+
+	msn_cmdproc_send_trans(cmdproc, trans);
 }
 
 static void
 cvr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	PurpleAccount *account;
+	MsnTransaction *trans;
 
 	account = cmdproc->session->account;
 
-	msn_cmdproc_send(cmdproc, "USR", "SSO I %s", purple_account_get_username(account));
+	trans = msn_transaction_new(cmdproc, "USR", "SSO I %s", purple_account_get_username(account));
+	msn_cmdproc_send_trans(cmdproc, trans);
 }
 
 static void
@@ -231,22 +243,25 @@
 ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnSession *session;
+	MsnTransaction *trans;
 	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,15 +272,18 @@
 		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!
 	 * reference of http://www.microsoft.com/globaldev/reference/oslocversion.mspx
 	 * to see the Local ID
 	 */
-	msn_cmdproc_send(cmdproc, "CVR",
+	trans = msn_transaction_new(cmdproc, "CVR",
 					"0x0409 winnt 5.1 i386 MSNMSGR 8.5.1302 BC01 %s",
 					 purple_account_get_username(account));
+	msn_cmdproc_send_trans(cmdproc, trans);
 }
 
 /**************************************************************************
@@ -287,12 +305,16 @@
 void
 msn_notification_close(MsnNotification *notification)
 {
+	MsnTransaction *trans;
+
 	g_return_if_fail(notification != NULL);
 
 	if (!notification->in_use)
 		return;
 
-	msn_cmdproc_send_quick(notification->cmdproc, "OUT", NULL, NULL);
+	trans = msn_transaction_new(notification->cmdproc, "OUT", NULL);
+	msn_transaction_set_saveable(trans, FALSE);
+	msn_cmdproc_send_trans(notification->cmdproc, trans);
 
 	msn_notification_disconnect(notification);
 }
@@ -1003,7 +1025,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;
@@ -1024,7 +1047,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
@@ -1034,7 +1061,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,
@@ -1043,17 +1074,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;
@@ -1068,6 +1111,7 @@
 
 	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->extinfo && user->extinfo->phone_mobile && user->extinfo->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);
@@ -1206,7 +1250,8 @@
 	PurpleConnection *gc;
 	MsnUser *user;
 	MsnObject *msnobj;
-	unsigned long clientid;
+	unsigned long clientid, extcaps;
+	char *extcap_str;
 	int networkid;
 	const char *state, *passport, *friendly;
 
@@ -1219,7 +1264,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))
@@ -1237,10 +1285,16 @@
 		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->extinfo && user->extinfo->phone_mobile && user->extinfo->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);
@@ -1508,6 +1562,77 @@
 	/*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);
+	}
+
+	/* Need to shortcut this check, probably... */
+	if (user == user->userlist->session->user) {
+		for (epNode = xmlnode_get_child(payloadNode, "PrivateEndpointData");
+		     epNode;
+		     epNode = xmlnode_get_next_twin(epNode)) {
+			MsnUserEndpoint *ep;
+			xmlnode *nameNode, *clientNode;
+
+			/*	<PrivateEndpointData id='{GUID}'>
+					<EpName>Endpoint Name</EpName>
+					<Idle>true/false</Idle>
+					<ClientType>1</ClientType>
+					<State>NLN</State>
+				</PrivateEndpointData>
+			*/
+			id = xmlnode_get_attrib(epNode, "id");
+			ep = msn_user_get_endpoint_data(user, id);
+
+			if (ep != NULL) {
+				nameNode = xmlnode_get_child(epNode, "EpName");
+				if (nameNode != NULL) {
+					g_free(ep->name);
+					ep->name = xmlnode_get_data(nameNode);
+				}
+
+				clientNode = xmlnode_get_child(epNode, "ClientType");
+				if (clientNode != NULL) {
+					tmp = xmlnode_get_data(clientNode);
+					ep->type = strtoul(tmp, NULL, 10);
+					g_free(tmp);
+				}
+			}
+		}
+	}
+}
+
 static void parse_currentmedia(MsnUser *user, const char *cmedia)
 {
 	char **cmedia_array;
@@ -1574,15 +1699,19 @@
 	PurpleAccount *account;
 	MsnUser *user;
 	const char *passport;
-	char *str;
+	xmlnode *payloadNode;
+	char *psm_str, *str;
 
 	session = cmdproc->session;
 	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) {
-		str = g_strndup(payload, len);
+		char *str = g_strndup(payload, len);
 		purple_debug_info("msn", "unknown user %s, payload is %s\n",
 			passport, str);
 		g_free(str);
@@ -1601,13 +1730,28 @@
 	}
 
 	if (len != 0) {
-		str = msn_get_psm(cmd->payload,len);
-		msn_user_set_statusline(user, str);
-		g_free(str);
-
-		str = msn_get_currentmedia(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_update(user);
+			return;
+		}
+
+		psm_str = msn_get_psm(payloadNode);
+		msn_user_set_statusline(user, psm_str);
+		g_free(psm_str);
+
+		str = msn_get_currentmedia(payloadNode);
 		parse_currentmedia(user, str);
 		g_free(str);
+
+		parse_user_endpoints(user, payloadNode);
+
+		xmlnode_free(payloadNode);
+
 	} else {
 		msn_user_set_statusline(user, NULL);
 	}
@@ -1640,6 +1784,161 @@
 	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");
+	if (session->protocol_ver >= 16)
+		caps = g_strdup_printf("%d:%02d", MSN_CLIENT_ID_CAPABILITIES, MSN_CLIENT_ID_EXT_CAPS);
+	else
+		caps = g_strdup_printf("%d", MSN_CLIENT_ID_CAPABILITIES);
+	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;
+	const char *name;
+	xmlnode *epname;
+	xmlnode *idle;
+	GHashTable *ui_info;
+	const gchar *ui_type;
+	xmlnode *client_type;
+	xmlnode *state;
+	char *payload;
+	int length;
+
+	private = xmlnode_new("PrivateEndpointData");
+
+	name = purple_account_get_string(session->account, "endpoint-name", NULL);
+	epname = xmlnode_new_child(private, "EpName");
+	xmlnode_insert_data(epname, name, -1);
+
+	idle = xmlnode_new_child(private, "Idle");
+	xmlnode_insert_data(idle, "false", -1);
+
+	/* 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");
+	ui_info = purple_core_get_ui_info();
+	ui_type = ui_info ? g_hash_table_lookup(ui_info, "client_type") : NULL;
+	if (ui_type) {
+		if (strcmp(ui_type, "pc") == 0)
+			xmlnode_insert_data(client_type, "1", -1);
+		else if (strcmp(ui_type, "web") == 0)
+			xmlnode_insert_data(client_type, "2", -1);
+		else if (strcmp(ui_type, "phone") == 0)
+			xmlnode_insert_data(client_type, "3", -1);
+		else if (strcmp(ui_type, "handheld") == 0)
+			xmlnode_insert_data(client_type, "3", -1);
+		else
+			xmlnode_insert_data(client_type, "1", -1);
+	}
+	else
+		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);
+}
+
+static void
+ubn_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+			 size_t len)
+{
+	/* Do Nothing, right now. */
+	if (payload != NULL)
+		purple_debug_info("msn", "UBN payload:\n%s\n", payload);
+}
+
+static void
+ubn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_misc("msn", "UBN received from %s.\n", cmd->params[0]);
+	cmdproc->last_cmd->payload_cb  = ubn_cmd_post;
+	cmd->payload_len = atoi(cmd->params[2]);
+}
+
+static void
+uun_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+			 size_t len)
+{
+	/* Do Nothing, right now. */
+	if (payload != NULL)
+		purple_debug_info("msn", "UUN payload:\n%s\n", payload);
+}
+
+static void
+uun_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	if (strcmp(cmd->params[1], "OK") != 0) {
+		purple_debug_misc("msn", "UUN received.\n");
+		cmdproc->last_cmd->payload_cb = uun_cmd_post;
+		cmd->payload_len = atoi(cmd->params[1]);
+	}
+	else
+		purple_debug_misc("msn", "UUN OK received.\n");
+}
+
+void
+msn_notification_send_uun(MsnSession *session, const char *user,
+                          MsnUnifiedNotificationType type, const char *payload)
+{
+	MsnTransaction *trans;
+	MsnCmdProc *cmdproc;
+	size_t len = strlen(payload);
+
+	cmdproc = session->notification->cmdproc;
+	purple_debug_misc("msn", "Sending UUN command %d to %s with payload: %s\n",
+	                  type, user, payload);
+	trans = msn_transaction_new(cmdproc, "UUN", "%s %d %" G_GSIZE_FORMAT,
+	                            user, type, len);
+	msn_transaction_set_payload(trans, payload, len);
+	msn_cmdproc_send_trans(cmdproc, trans);
+}
+
 /**************************************************************************
  * Message Types
  **************************************************************************/
@@ -1657,39 +1956,39 @@
 		/* This isn't an official message. */
 		return;
 
-	if ((value = msn_message_get_attr(msg, "kv")) != NULL)
+	if ((value = msn_message_get_header_value(msg, "kv")) != NULL)
 	{
 		g_free(session->passport_info.kv);
 		session->passport_info.kv = g_strdup(value);
 	}
 
-	if ((value = msn_message_get_attr(msg, "sid")) != NULL)
+	if ((value = msn_message_get_header_value(msg, "sid")) != NULL)
 	{
 		g_free(session->passport_info.sid);
 		session->passport_info.sid = g_strdup(value);
 	}
 
-	if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL)
+	if ((value = msn_message_get_header_value(msg, "MSPAuth")) != NULL)
 	{
 		g_free(session->passport_info.mspauth);
 		session->passport_info.mspauth = g_strdup(value);
 	}
 
-	if ((value = msn_message_get_attr(msg, "ClientIP")) != NULL)
+	if ((value = msn_message_get_header_value(msg, "ClientIP")) != NULL)
 	{
 		g_free(session->passport_info.client_ip);
 		session->passport_info.client_ip = g_strdup(value);
 	}
 
-	if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL)
+	if ((value = msn_message_get_header_value(msg, "ClientPort")) != NULL)
 	{
 		session->passport_info.client_port = ntohs(atoi(value));
 	}
 
-	if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL)
+	if ((value = msn_message_get_header_value(msg, "LoginTime")) != NULL)
 		session->passport_info.sl = atol(value);
 
-	if ((value = msn_message_get_attr(msg, "EmailEnabled")) != NULL)
+	if ((value = msn_message_get_header_value(msg, "EmailEnabled")) != NULL)
 		session->passport_info.email_enabled = (gboolean)atol(value);
 
 	/*starting retrieve the contact list*/
@@ -2093,6 +2392,9 @@
 	msn_table_add_cmd(cbs_table, NULL, "UBX", ubx_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "UUX", uux_cmd);
 
+	msn_table_add_cmd(cbs_table, NULL, "UBN", ubn_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "UUN", uun_cmd);
+
 	msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd);
 
 	msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd);