changeset 23540:0fc1d2df836e

propagate from branch 'im.pidgin.pidgin' (head 5e9671c654b021e77695d4a863fb55e27a831901) to branch 'im.pidgin.cpw.qulogic.msn' (head b062f14d1bcd8e6c28b2b05977aa19a4bfd4867c)
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Sat, 21 Jun 2008 07:36:16 +0000
parents 8ea901db8e34 (current diff) c1cba867b3da (diff)
children 54f7e507ea7d
files
diffstat 20 files changed, 2107 insertions(+), 1000 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/msn/command.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/command.c	Sat Jun 21 07:36:16 2008 +0000
@@ -69,18 +69,22 @@
 /*
  * set command Payload length
  */
-static void
+static gboolean
 msn_set_payload_len(MsnCommand *cmd)
 {
 	char *param;
 	int len = 0;
+	gboolean has_payload = FALSE;
 
 	if (msn_check_payload_cmd(cmd->command) && (cmd->param_count > 0)){
 		param = cmd->params[cmd->param_count - 1];
 		len = is_num(param) ? atoi(param) : 0;
+		has_payload = TRUE;
 	}
 
 	cmd->payload_len = len;
+
+	return has_payload;
 }
 
 MsnCommand *
@@ -103,15 +107,17 @@
 
 	if (cmd->params != NULL)
 	{
-		char *param;
 		int c;
 
 		for (c = 0; cmd->params[c]; c++);
 		cmd->param_count = c;
 
-		param = cmd->params[0];
-
-		cmd->trId = is_num(param) ? atoi(param) : 0;
+		if (cmd->param_count) {
+			char *param = cmd->params[0];
+			cmd->trId = is_num(param) ? atoi(param) : 0;
+		} else {
+			cmd->trId = 0;
+		}
 	}
 	else
 	{
@@ -120,8 +126,8 @@
 
 	/* khc: Huh! */
 	/*add payload Length checking*/
-	msn_set_payload_len(cmd);
-	purple_debug_info("MSNP14","get payload len:%" G_GSIZE_FORMAT "\n", cmd->payload_len);
+	if (msn_set_payload_len(cmd))
+		purple_debug_info("MSNP14","get payload len:%" G_GSIZE_FORMAT "\n", cmd->payload_len);
 
 	msn_command_ref(cmd);
 
--- a/libpurple/protocols/msn/contact.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/contact.c	Sat Jun 21 07:36:16 2008 +0000
@@ -1,5 +1,5 @@
 /**
- * @file contact.c
+ * @file contact.c 
  * 	get MSN contacts via SOAP request
  *	created by MaYuan<mayuan2006@gmail.com>
  *
@@ -29,6 +29,7 @@
 #include "xmlnode.h"
 #include "group.h"
 #include "soap2.h"
+#include "nexus.h"
 
 const char *MsnSoapPartnerScenarioText[] =
 {
@@ -49,29 +50,10 @@
 };
 
 typedef struct {
-	MsnContact *contact;
+	MsnSession *session;
 	MsnSoapPartnerScenario which;
 } GetContactListCbData;
 
-/* new a contact */
-MsnContact *
-msn_contact_new(MsnSession *session)
-{
-	MsnContact *contact;
-
-	contact = g_new0(MsnContact, 1);
-	contact->session = session;
-
-	return contact;
-}
-
-/* destroy the contact */
-void
-msn_contact_destroy(MsnContact *contact)
-{
-	g_free(contact);
-}
-
 MsnCallbackState *
 msn_callback_state_new(MsnSession *session)
 {
@@ -82,6 +64,22 @@
 	return state;
 }
 
+MsnCallbackState *
+msn_callback_state_dup(MsnCallbackState *state)
+{
+	MsnCallbackState *new_state = g_new0(MsnCallbackState, 1);
+
+	new_state->session = state->session;
+	new_state->who = g_strdup(state->who);
+	new_state->uid = g_strdup(state->uid);
+	new_state->old_group_name = g_strdup(state->old_group_name);
+	new_state->new_group_name = g_strdup(state->new_group_name);
+	new_state->guid = g_strdup(state->guid);
+	/* The rest should be made new */
+
+	return new_state;
+}	
+
 void
 msn_callback_state_free(MsnCallbackState *state)
 {
@@ -93,6 +91,7 @@
 	g_free(state->old_group_name);
 	g_free(state->new_group_name);
 	g_free(state->guid);
+	xmlnode_free(state->body);
 
 	g_free(state);
 }
@@ -177,56 +176,60 @@
 	return 0;
 }
 
-/*get User Type*/
-static int
-msn_get_user_type(char *type)
+/* get Network */
+/* QuLogic: These names don't really refer to the MsnNetwork,
+ *          but I haven't yet written the code to properly use them.
+ */
+static MsnNetwork
+msn_get_network(char *type)
 {
 	g_return_val_if_fail(type != NULL, 0);
 
 	if (!strcmp(type,"Regular")) {
-		return MSN_USER_TYPE_PASSPORT;
+		return MSN_NETWORK_PASSPORT;
 	}
 	if (!strcmp(type,"Live")) {
-		return MSN_USER_TYPE_PASSPORT;
+		return MSN_NETWORK_PASSPORT;
 	}
 	if (!strcmp(type,"LivePending")) {
-		return MSN_USER_TYPE_PASSPORT;
+		return MSN_NETWORK_PASSPORT;
 	}
 
-	return MSN_USER_TYPE_UNKNOWN;
+	return MSN_NETWORK_UNKNOWN;
 }
 
 /* Create the AddressBook in the server, if we don't have one */
 static void
 msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	if (resp && msn_soap_xml_get(resp->xml, "Body/Fault") == NULL) {
+	if (resp && xmlnode_get_child(resp->xml, "Body/Fault") == NULL) {
 		purple_debug_info("msnab", "Address Book successfully created!\n");
-		msn_get_address_book((MsnContact *)data, MSN_PS_INITIAL, NULL, NULL);
+		msn_get_address_book((MsnSession *)data, MSN_PS_INITIAL, NULL, NULL);
 	} else {
 		purple_debug_info("msnab", "Address Book creation failed!\n");
 	}
 }
 
 static void
-msn_create_address_book(MsnContact * contact)
+msn_create_address_book(MsnSession *session)
 {
 	gchar *body;
 
-	g_return_if_fail(contact != NULL);
-	g_return_if_fail(contact->session != NULL);
-	g_return_if_fail(contact->session->user != NULL);
-	g_return_if_fail(contact->session->user->passport != NULL);
-
+	g_return_if_fail(session != NULL);
+	g_return_if_fail(session->user != NULL);
+	g_return_if_fail(session->user->passport != NULL);
+	
 	purple_debug_info("msnab","Creating an Address Book.\n");
 
-	body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport);
+	body = g_markup_printf_escaped(MSN_ADD_ADDRESSBOOK_TEMPLATE,
+		msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS),
+		session->user->passport);
 
-	msn_soap_message_send(contact->session,
+	msn_soap_message_send(session,
 		msn_soap_message_new(MSN_ADD_ADDRESSBOOK_SOAP_ACTION,
 			xmlnode_from_str(body, -1)),
 		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, msn_create_address_cb,
-		contact);
+		session);
 
 	g_free(body);
 }
@@ -259,7 +262,7 @@
 {
 	xmlnode *type;
 
-	if ((type = msn_soap_xml_get(service, "Info/Handle/Type"))) {
+	if ((type = xmlnode_get_child(service, "Info/Handle/Type"))) {
 		char *type_str = xmlnode_get_data(type);
 
 		if (g_str_equal(type_str, "Profile")) {
@@ -273,7 +276,7 @@
 			purple_account_set_string(session->account,	"CLLastChange",
 				lastchange_str);
 
-			for (membership = msn_soap_xml_get(service,
+			for (membership = xmlnode_get_child(service,
 					"Memberships/Membership");
 				 membership; membership = xmlnode_get_next_twin(membership)) {
 
@@ -285,7 +288,7 @@
 				purple_debug_info("msncl", "MemberRole role: %s, list: %d\n",
 					role_str, list);
 
-				for (member = msn_soap_xml_get(membership, "Members/Member");
+				for (member = xmlnode_get_child(membership, "Members/Member");
 					 member; member = xmlnode_get_next_twin(member)) {
 					const char *member_type = xmlnode_get_attrib(member, "type");
 					if (g_str_equal(member_type, "PassportMember")) {
@@ -310,7 +313,7 @@
 
 /*parse contact list*/
 static void
-msn_parse_contact_list(MsnContact *contact, xmlnode *node)
+msn_parse_contact_list(MsnSession *session, xmlnode *node)
 {
 	xmlnode *fault, *faultnode;
 
@@ -321,18 +324,18 @@
 	 *
 	 * this is not handled yet
 	 */
-	if ((fault = msn_soap_xml_get(node, "Body/Fault"))) {
+	if ((fault = xmlnode_get_child(node, "Body/Fault"))) {
 		if ((faultnode = xmlnode_get_child(fault, "faultstring"))) {
 			char *faultstring = xmlnode_get_data(faultnode);
 			purple_debug_info("msncl", "Retrieving contact list failed: %s\n",
 				faultstring);
 			g_free(faultstring);
 		}
-		if ((faultnode = msn_soap_xml_get(fault, "detail/errorcode"))) {
+		if ((faultnode = xmlnode_get_child(fault, "detail/errorcode"))) {
 			char *errorcode = xmlnode_get_data(faultnode);
 
 			if (g_str_equal(errorcode, "ABDoesNotExist")) {
-				msn_create_address_book(contact);
+				msn_create_address_book(session);
 				g_free(errorcode);
 				return;
 			}
@@ -340,14 +343,14 @@
 			g_free(errorcode);
 		}
 
-		msn_get_contact_list(contact, MSN_PS_INITIAL, NULL);
+		msn_get_contact_list(session, MSN_PS_INITIAL, NULL);
 	} else {
 		xmlnode *service;
 
-		for (service = msn_soap_xml_get(node, "Body/FindMembershipResponse/"
+		for (service = xmlnode_get_child(node, "Body/FindMembershipResponse/"
 				"FindMembershipResult/Services/Service");
 			 service; service = xmlnode_get_next_twin(service)) {
-			msn_parse_each_service(contact->session, service);
+			msn_parse_each_service(session, service);
 		}
 	}
 }
@@ -357,8 +360,7 @@
 	gpointer data)
 {
 	GetContactListCbData *cb_data = data;
-	MsnContact *contact = cb_data->contact;
-	MsnSession *session = contact->session;
+	MsnSession *session = cb_data->session;
 
 	g_return_if_fail(session != NULL);
 
@@ -368,7 +370,7 @@
 
 		purple_debug_misc("msncl","Got the contact list!\n");
 
-		msn_parse_contact_list(cb_data->contact, resp->xml);
+		msn_parse_contact_list(session, resp->xml);
 		abLastChange = purple_account_get_string(session->account,
 			"ablastChange", NULL);
 		dynamicItemLastChange = purple_account_get_string(session->account,
@@ -379,9 +381,9 @@
 			/* XXX: this should be enabled when we can correctly do partial
 			   syncs with the server. Currently we need to retrieve the whole
 			   list to detect sync issues */
-			msn_get_address_book(contact, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange);
+			msn_get_address_book(session, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange);
 #else
-			msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL);
+			msn_get_address_book(session, MSN_PS_INITIAL, NULL, NULL);
 #endif
 		}
 	}
@@ -391,12 +393,12 @@
 
 /*SOAP  get contact list*/
 void
-msn_get_contact_list(MsnContact * contact,
+msn_get_contact_list(MsnSession *session,
 	const MsnSoapPartnerScenario partner_scenario, const char *update_time)
 {
 	gchar *body = NULL;
 	gchar *update_str = NULL;
-	GetContactListCbData cb_data = { contact, partner_scenario };
+	GetContactListCbData cb_data = { session, partner_scenario };
 	const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario];
 
 	purple_debug_misc("MSNCL","Getting Contact List.\n");
@@ -406,9 +408,11 @@
 		update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML,update_time);
 	}
 
-	body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, update_str ? update_str : "");
+	body = g_markup_printf_escaped(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str,
+		msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS),
+		update_str ? update_str : "");
 
-	msn_soap_message_send(contact->session,
+	msn_soap_message_send(session,
 		msn_soap_message_new(MSN_GET_CONTACT_SOAP_ACTION,
 			xmlnode_from_str(body, -1)),
 		MSN_CONTACT_SERVER, MSN_GET_CONTACT_POST_URL,
@@ -419,9 +423,8 @@
 }
 
 static void
-msn_parse_addressbook_groups(MsnContact *contact, xmlnode *node)
+msn_parse_addressbook_groups(MsnSession *session, xmlnode *node)
 {
-	MsnSession *session = contact->session;
 	xmlnode *group;
 
 	purple_debug_info("MSNAB","msn_parse_addressbook_groups()\n");
@@ -507,18 +510,19 @@
 }
 
 static void
-msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
+msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node)
 {
-	MsnSession *session = contact->session;
 	xmlnode *contactNode;
-	char *passport = NULL, *Name = NULL, *uid = NULL, *type = NULL, *mobile_number = NULL;
+	char *passport = NULL, *Name = NULL, *uid = NULL, *type = NULL, *mobile_number = NULL, *alias = NULL;
 	gboolean mobile = FALSE;
+	PurpleConnection *pc = purple_account_get_connection(session->account);
 
 	for(contactNode = xmlnode_get_child(node, "Contact"); contactNode;
 				contactNode = xmlnode_get_next_twin(contactNode)) {
 		xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds, *messenger_user;
+		xmlnode *annotation;
 		MsnUser *user;
-		MsnUserType usertype;
+		MsnNetwork networkId;
 
 		if (!(contactId = xmlnode_get_child(contactNode,"contactId"))
 				|| !(contactInfo = xmlnode_get_child(contactNode, "contactInfo"))
@@ -527,10 +531,11 @@
 
 		g_free(passport);
 		g_free(Name);
+		g_free(alias);
 		g_free(uid);
 		g_free(type);
 		g_free(mobile_number);
-		passport = Name = uid = type = mobile_number = NULL;
+		passport = Name = uid = type = mobile_number = alias = NULL;
 		mobile = FALSE;
 
 		uid = xmlnode_get_data(contactId);
@@ -558,7 +563,7 @@
 			g_free(is_messenger_user);
 		}
 
-		usertype = msn_get_user_type(type);
+		networkId = msn_get_network(type);
 		passportName = xmlnode_get_child(contactInfo, "passportName");
 		if (passportName == NULL) {
 			xmlnode *emailsNode, *contactEmailNode, *emailNode;
@@ -589,7 +594,7 @@
 				if(msnEnabled && !strcmp(msnEnabled, "true")) {
 					/*Messenger enabled, Get the Passport*/
 					purple_debug_info("MsnAB", "Yahoo User %s\n", passport ? passport : "(null)");
-					usertype = MSN_USER_TYPE_YAHOO;
+					networkId = MSN_NETWORK_YAHOO;
 					g_free(msnEnabled);
 					break;
 				} else {
@@ -611,15 +616,24 @@
 		else
 			Name = g_strdup(passport);
 
+		for (annotation = xmlnode_get_child(contactInfo, "annotations/Annotation");
+				annotation; annotation = xmlnode_get_next_twin(annotation)) {
+			char *name;
+			name = xmlnode_get_data(xmlnode_get_child(annotation, "Name"));
+			if (!strcmp(name, "AB.NickName"))
+				alias = xmlnode_get_data(xmlnode_get_child(annotation, "Value"));
+			g_free(name);
+		}
+
 		mobile = msn_parse_addressbook_mobile(contactInfo, &mobile_number);
 
-		purple_debug_misc("MsnAB","passport:{%s} uid:{%s} display:{%s} mobile:{%s} mobile number:{%s}\n",
-			passport, uid ? uid : "(null)", Name ? Name : "(null)",
+		purple_debug_misc("MsnAB","passport:{%s} uid:{%s} display:{%s} alias: {%s} mobile:{%s} mobile number:{%s}\n",
+			passport, uid ? uid : "(null)", Name ? Name : "(null)", alias ? alias : "(null)",
 			mobile ? "true" : "false", mobile_number ? mobile_number : "(null)");
 
 		user = msn_userlist_find_add_user(session->userlist, passport, Name);
 		msn_user_set_uid(user, uid);
-		msn_user_set_type(user, usertype);
+		msn_user_set_network(user, networkId);
 		msn_user_set_mobile_phone(user, mobile_number);
 
 		groupIds = xmlnode_get_child(contactInfo, "groupIds");
@@ -645,6 +659,8 @@
 			purple_prpl_got_user_status(session->account, user->passport, "mobile", NULL);
 			purple_prpl_got_user_status(session->account, user->passport, "available", NULL);
 		}
+		if (alias)
+			purple_serv_got_private_alias(pc, passport, alias);
 	}
 
 	g_free(passport);
@@ -655,18 +671,15 @@
 }
 
 static gboolean
-msn_parse_addressbook(MsnContact * contact, xmlnode *node)
+msn_parse_addressbook(MsnSession *session, xmlnode *node)
 {
-	MsnSession * session;
 	xmlnode *result;
 	xmlnode *groups;
 	xmlnode *contacts;
 	xmlnode *abNode;
 	xmlnode *fault;
 
-	session = contact->session;
-
-	if ((fault = msn_soap_xml_get(node, "Body/Fault"))) {
+	if ((fault = xmlnode_get_child(node, "Body/Fault"))) {
 		xmlnode *faultnode;
 
 		if ((faultnode = xmlnode_get_child(fault, "faultstring"))) {
@@ -675,7 +688,7 @@
 			g_free(faultstring);
 		}
 
-		if ((faultnode = msn_soap_xml_get(fault, "detail/errorcode"))) {
+		if ((faultnode = xmlnode_get_child(fault, "detail/errorcode"))) {
 			gchar *errorcode = xmlnode_get_data(faultnode);
 
 			purple_debug_info("MSNAB", "Error Code: %s\n", errorcode);
@@ -690,7 +703,7 @@
 		return FALSE;
 	}
 
-	result = msn_soap_xml_get(node, "Body/ABFindAllResponse/ABFindAllResult");
+	result = xmlnode_get_child(node, "Body/ABFindAllResponse/ABFindAllResult");
 	if(result == NULL){
 		purple_debug_misc("MSNAB","receive no address book update\n");
 		return TRUE;
@@ -701,7 +714,7 @@
 	/*Process Group List*/
 	groups = xmlnode_get_child(result,"groups");
 	if (groups != NULL) {
-		msn_parse_addressbook_groups(contact, groups);
+		msn_parse_addressbook_groups(session, groups);
 	}
 
 	/*add a default No group to set up the no group Membership*/
@@ -726,7 +739,7 @@
 	purple_debug_info("MSNAB","process contact list...\n");
 	contacts =xmlnode_get_child(result,"contacts");
 	if (contacts != NULL) {
-		msn_parse_addressbook_contacts(contact, contacts);
+		msn_parse_addressbook_contacts(session, contacts);
 	}
 
 	abNode =xmlnode_get_child(result,"ab");
@@ -753,19 +766,16 @@
 static void
 msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	MsnContact *contact = data;
-	MsnSession *session;
+	MsnSession *session = data;
 
 	if (resp == NULL)
 		return;
 
-	g_return_if_fail(contact != NULL);
-	session = contact->session;
 	g_return_if_fail(session != NULL);
 
 	purple_debug_misc("MSNAB", "Got the Address Book!\n");
 
-	if (msn_parse_addressbook(contact, resp->xml)) {
+	if (msn_parse_addressbook(session, resp->xml)) {
 		if (!session->logged_in) {
 			msn_send_privacy(session->account->gc);
 			msn_notification_dump_contact(session);
@@ -776,7 +786,7 @@
 		  send timestamps)
 		*/
 		/*
-		msn_get_address_book(contact, NULL, NULL);
+		msn_get_address_book(session, NULL, NULL);
 		*/
 		msn_session_disconnect(session);
 		purple_connection_error_reason(session->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to retrieve MSN Address Book"));
@@ -785,7 +795,7 @@
 
 /*get the address book*/
 void
-msn_get_address_book(MsnContact *contact,
+msn_get_address_book(MsnSession *session,
 	MsnSoapPartnerScenario partner_scenario, const char *LastChanged,
 	const char *dynamicItemLastChange)
 {
@@ -799,18 +809,120 @@
 	else if (LastChanged != NULL)
 		update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML, LastChanged);
 
-	body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], update_str ? update_str : "");
+	body = g_markup_printf_escaped(MSN_GET_ADDRESS_TEMPLATE,
+		MsnSoapPartnerScenarioText[partner_scenario],
+		msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS),
+		update_str ? update_str : "");
 
-	msn_soap_message_send(contact->session,
+	msn_soap_message_send(session,
 		msn_soap_message_new(MSN_GET_ADDRESS_SOAP_ACTION,
 			xmlnode_from_str(body, -1)),
 		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, msn_get_address_cb,
-		contact);
+		session);
 
 	g_free(update_str);
 	g_free(body);
 }
 
+/***************************************************************
+ * Contact Operations
+ ***************************************************************/
+
+static const char *
+msn_contact_operation_str(MsnCallbackAction action)
+{
+	/* Make sure this is large enough when adding more */
+	static char buf[BUF_LEN];
+	buf[0] = '\0';
+
+	if (action & MSN_ADD_BUDDY)
+		strcat(buf, "Adding Buddy,");
+	if (action & MSN_MOVE_BUDDY)
+		strcat(buf, "Moving Buddy,");
+	if (action & MSN_ACCEPTED_BUDDY)
+		strcat(buf, "Accepted Buddy,");
+	if (action & MSN_DENIED_BUDDY)
+		strcat(buf, "Denied Buddy,");
+	if (action & MSN_ADD_GROUP)
+		strcat(buf, "Adding Group,");
+	if (action & MSN_DEL_GROUP)
+		strcat(buf, "Deleting Group,");
+	if (action & MSN_RENAME_GROUP)
+		strcat(buf, "Renaming Group,");
+	if (action & MSN_UPDATE_INFO)
+		strcat(buf, "Updating Contact Info,");
+
+	return buf;
+}
+
+static gboolean msn_contact_request(MsnCallbackState *state);
+
+static void
+msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
+{
+	MsnCallbackState *state = data;
+	xmlnode *faultcode;
+	char *faultcode_str;
+
+	if (resp == NULL) {
+		purple_debug_error("MSNCL",
+		                   "Operation {%s} failed. No response received from server.\n",
+		                   msn_contact_operation_str(state->action));
+		return;
+	}
+
+	faultcode = xmlnode_get_child(resp->xml, "Body/Fault/faultcode");
+
+	if (faultcode == NULL) {
+		/* No errors */
+		if (state->cb)
+			((MsnSoapCallback)state->cb)(req, resp, data);
+		msn_callback_state_free(state);
+		return;
+	}
+
+	faultcode_str = xmlnode_get_data(faultcode);
+
+	if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) {
+		purple_debug_info("MSNCL",
+		                  "Contact Operation {%s} failed because of bad token."
+		                  " Updating token now and retrying operation.\n",
+		                  msn_contact_operation_str(state->action));
+		/* Token has expired, so renew it, and try again later */
+		msn_nexus_update_token(state->session->nexus, MSN_AUTH_CONTACTS,
+		                       (GSourceFunc)msn_contact_request, data);
+	}
+	else
+	{
+		/* We don't know how to respond to this faultcode, so just log it */
+		/* XXX: Probably should notify the user or undo the change or something? */
+		char *str = xmlnode_to_str(xmlnode_get_child(resp->xml, "Body/Fault"), NULL);
+		purple_debug_error("MSNCL", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), str);
+		g_free(str);
+		msn_callback_state_free(state);
+	}
+
+	g_free(faultcode_str);
+}
+
+static gboolean
+msn_contact_request(MsnCallbackState *state)
+{
+	if (state->token == NULL)
+		state->token = xmlnode_get_child(state->body,
+			"Header/ABAuthHeader/TicketToken");
+	/* delete old & replace with new token */
+	xmlnode_free(state->token->child);
+	xmlnode_insert_data(state->token,
+		msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1);
+	msn_soap_message_send(state->session,
+		msn_soap_message_new(state->post_action, xmlnode_copy(state->body)),
+		MSN_CONTACT_SERVER, state->post_url, msn_contact_request_cb, state);
+	return FALSE;
+}
+
 static void
 msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
 	gpointer data)
@@ -818,33 +930,31 @@
 	MsnCallbackState *state = data;
 	MsnSession *session = state->session;
 
+	MsnUserList *userlist;
+	MsnUser *user;
+
 	g_return_if_fail(session != NULL);
 
-	if (resp != NULL) {
-		MsnUserList *userlist = session->userlist;
-		MsnUser *user;
+	userlist = session->userlist;
 
-		purple_debug_info("MSNCL","Contact added successfully\n");
+	purple_debug_info("MSNCL","Contact added successfully\n");
 
-		// the code this block is replacing didn't send ADL for yahoo contacts,
-		// but i haven't confirmed this is WLM's behaviour wrt yahoo contacts
-		if ( !msn_user_is_yahoo(session->account, state->who) ) {
-			msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
-			msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
-		}
-
-		msn_notification_send_fqy(session, state->who);
-
-		user = msn_userlist_find_add_user(userlist, state->who, state->who);
-		msn_user_add_group_id(user, state->guid);
+	// the code this block is replacing didn't send ADL for yahoo contacts,
+	// but i haven't confirmed this is WLM's behaviour wrt yahoo contacts
+	if ( !msn_user_is_yahoo(session->account, state->who) ) {
+		msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
+		msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
 	}
 
-	msn_callback_state_free(state);
+	msn_notification_send_fqy(session, state->who);
+
+	user = msn_userlist_find_add_user(userlist, state->who, state->who);
+	msn_user_add_group_id(user, state->guid);
 }
 
 /* add a Contact in MSN_INDIVIDUALS_GROUP */
 void
-msn_add_contact(MsnContact *contact, MsnCallbackState *state, const char *passport)
+msn_add_contact(MsnSession *session, MsnCallbackState *state, const char *passport)
 {
 	gchar *body = NULL;
 	gchar *contact_xml = NULL;
@@ -866,11 +976,11 @@
 	contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
 	body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE, contact_xml);
 
-	msn_soap_message_send(contact->session,
-		msn_soap_message_new(MSN_CONTACT_ADD_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
-		msn_add_contact_read_cb, state);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_CONTACT_ADD_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_add_contact_read_cb;
+	msn_contact_request(state);
 
 	g_free(contact_xml);
 	g_free(body);
@@ -887,41 +997,35 @@
 
 	userlist = state->session->userlist;
 
-	if (resp != NULL) {
-		if (msn_userlist_add_buddy_to_group(userlist, state->who,
-				state->new_group_name)) {
-			purple_debug_info("MSNCL", "Contact %s added to group %s successfully!\n", state->who, state->new_group_name);
-		} else {
-			purple_debug_info("MSNCL","Contact %s added to group %s successfully on server, but failed in the local list\n", state->who, state->new_group_name);
-		}
+	if (msn_userlist_add_buddy_to_group(userlist, state->who,
+			state->new_group_name)) {
+		purple_debug_info("MSNCL", "Contact %s added to group %s successfully!\n", state->who, state->new_group_name);
+	} else {
+		purple_debug_info("MSNCL","Contact %s added to group %s successfully on server, but failed in the local list\n", state->who, state->new_group_name);
+	}
 
-		if (state->action & MSN_ADD_BUDDY) {
-			MsnUser *user = msn_userlist_find_user(userlist, state->who);
-
-        	if ( !msn_user_is_yahoo(state->session->account, state->who) ) {
+	if (state->action & MSN_ADD_BUDDY) {
+		MsnUser *user = msn_userlist_find_user(userlist, state->who);
 
-				msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
-				msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
-	        }
-	        msn_notification_send_fqy(state->session, state->who);
+		if ( !msn_user_is_yahoo(state->session->account, state->who) ) {
+			msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
+			msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
+		}
+		msn_notification_send_fqy(state->session, state->who);
 
-			if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
-				msn_del_contact_from_list(state->session->contact, NULL, state->who, MSN_LIST_PL);
-				msn_callback_state_free(state);
-				return;
-			}
-		}
-
-		if (state->action & MSN_MOVE_BUDDY) {
-			msn_del_contact_from_group(state->session->contact, state->who, state->old_group_name);
+		if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
+			msn_del_contact_from_list(state->session, NULL, state->who, MSN_LIST_PL);
+			return;
 		}
 	}
 
-	msn_callback_state_free(state);
+	if (state->action & MSN_MOVE_BUDDY) {
+		msn_del_contact_from_group(state->session, state->who, state->old_group_name);
+	}
 }
 
 void
-msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
+msn_add_contact_to_group(MsnSession *session, MsnCallbackState *state,
 			 const char *passport, const char *groupId)
 {
 	MsnUserList *userlist;
@@ -931,26 +1035,22 @@
 	g_return_if_fail(passport != NULL);
 	g_return_if_fail(groupId != NULL);
 
-	g_return_if_fail(contact != NULL);
-	g_return_if_fail(contact->session != NULL);
-	g_return_if_fail(contact->session->userlist != NULL);
+	g_return_if_fail(session != NULL);
 
-	userlist = contact->session->userlist;
+	userlist = session->userlist;
 
 	if (!strcmp(groupId, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(groupId, MSN_NON_IM_GROUP_ID)) {
 
 		user = msn_userlist_find_add_user(userlist, passport, passport);
 
 		if (state->action & MSN_ADD_BUDDY) {
-			msn_add_contact(contact, state, passport);
+			msn_add_contact(session, state, passport);
 			return;
 		}
 
 		if (state->action & MSN_MOVE_BUDDY) {
 			msn_user_add_group_id(user, groupId);
-			msn_del_contact_from_group(contact, passport, state->old_group_name);
-		} else {
-			msn_callback_state_free(state);
+			msn_del_contact_from_group(session, passport, state->old_group_name);
 		}
 
 		return;
@@ -974,11 +1074,11 @@
 
 	body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE, groupId, contact_xml);
 
-	msn_soap_message_send(state->session,
-		msn_soap_message_new(MSN_ADD_CONTACT_GROUP_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
-		msn_add_contact_to_group_read_cb, state);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_ADD_CONTACT_GROUP_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_add_contact_to_group_read_cb;
+	msn_contact_request(state);
 
 	g_free(contact_xml);
 	g_free(body);
@@ -989,24 +1089,19 @@
 	gpointer data)
 {
 	MsnCallbackState *state = data;
-
-	if (resp != NULL) {
-		MsnUserList *userlist = state->session->userlist;
-		MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid);
-
-		purple_debug_info("MSNCL","Delete contact successful\n");
+	MsnUserList *userlist = state->session->userlist;
+	MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid);
 
-		if (user != NULL) {
-			msn_userlist_remove_user(userlist, user);
-		}
+	purple_debug_info("MSNCL","Delete contact successful\n");
+
+	if (user != NULL) {
+		msn_userlist_remove_user(userlist, user);
 	}
-
-	msn_callback_state_free(state);
 }
 
 /*delete a Contact*/
 void
-msn_delete_contact(MsnContact *contact, const char *contactId)
+msn_delete_contact(MsnSession *session, const char *contactId)
 {
 	gchar *body = NULL;
 	gchar *contact_id_xml = NULL ;
@@ -1015,17 +1110,18 @@
 	g_return_if_fail(contactId != NULL);
 	contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, contactId);
 
-	state = msn_callback_state_new(contact->session);
+	state = msn_callback_state_new(session);
 	msn_callback_state_set_uid(state, contactId);
 
 	/* build SOAP request */
 	purple_debug_info("MSNCL","Deleting contact with contactId: %s\n", contactId);
 	body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE, contact_id_xml);
-	msn_soap_message_send(contact->session,
-		msn_soap_message_new(MSN_CONTACT_DEL_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
-		msn_delete_contact_read_cb, state);
+
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_CONTACT_DEL_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_delete_contact_read_cb;
+	msn_contact_request(state);
 
 	g_free(contact_id_xml);
 	g_free(body);
@@ -1037,20 +1133,16 @@
 {
 	MsnCallbackState *state = data;
 
-	if (resp != NULL) {
-		if (msn_userlist_rem_buddy_from_group(state->session->userlist,
-				state->who, state->old_group_name)) {
-			purple_debug_info("MSNCL", "Contact %s deleted successfully from group %s\n", state->who, state->old_group_name);
-		} else {
-			purple_debug_info("MSNCL", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name);
-		}
+	if (msn_userlist_rem_buddy_from_group(state->session->userlist,
+			state->who, state->old_group_name)) {
+		purple_debug_info("MSNCL", "Contact %s deleted successfully from group %s\n", state->who, state->old_group_name);
+	} else {
+		purple_debug_info("MSNCL", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name);
 	}
-
-	msn_callback_state_free(state);
 }
 
 void
-msn_del_contact_from_group(MsnContact *contact, const char *passport, const char *group_name)
+msn_del_contact_from_group(MsnSession *session, const char *passport, const char *group_name)
 {
 	MsnUserList * userlist;
 	MsnUser *user;
@@ -1060,11 +1152,9 @@
 
 	g_return_if_fail(passport != NULL);
 	g_return_if_fail(group_name != NULL);
-	g_return_if_fail(contact != NULL);
-	g_return_if_fail(contact->session != NULL);
-	g_return_if_fail(contact->session->userlist != NULL);
+	g_return_if_fail(session != NULL);
 
-	userlist = contact->session->userlist;
+	userlist = session->userlist;
 
 	groupId = msn_userlist_find_group_id(userlist, group_name);
 	if (groupId != NULL) {
@@ -1086,7 +1176,7 @@
 		return;
 	}
 
-	state = msn_callback_state_new(contact->session);
+	state = msn_callback_state_new(session);
 	msn_callback_state_set_who(state, passport);
 	msn_callback_state_set_guid(state, groupId);
 	msn_callback_state_set_old_group_name(state, group_name);
@@ -1094,11 +1184,11 @@
 	contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
 	body = g_strdup_printf(MSN_CONTACT_DEL_GROUP_TEMPLATE, contact_id_xml, groupId);
 
-	msn_soap_message_send(contact->session,
-		msn_soap_message_new(MSN_CONTACT_DEL_GROUP_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
-		msn_del_contact_from_group_read_cb, state);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_CONTACT_DEL_GROUP_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_del_contact_from_group_read_cb;
+	msn_contact_request(state);
 
 	g_free(contact_id_xml);
 	g_free(body);
@@ -1109,32 +1199,75 @@
 msn_update_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
 	gpointer data)
 {
-	if (resp)
-		purple_debug_info("MSN CL","Contact updated successfully\n");
-	else
-		purple_debug_info("MSN CL","Contact updated successfully\n");
+	purple_debug_info("MSN CL","Contact updated successfully\n");
 }
 
-/* Update a contact's nickname */
+/* Update a contact's info */
 void
-msn_update_contact(MsnContact *contact, const char* nickname)
+msn_update_contact(MsnSession *session, const char *passport, MsnContactUpdateType type, const char* value)
 {
-	gchar *body = NULL, *escaped_nickname;
+	MsnCallbackState *state;
+	xmlnode *contact;
+	xmlnode *contact_info;
+	xmlnode *changes;
+
+	purple_debug_info("MSN CL","Update contact information with new %s: %s\n",
+		type==MSN_UPDATE_DISPLAY ? "display name" : "alias", value);
+	purple_debug_info("msncl", "passport=%s\n", passport);
+	g_return_if_fail(passport != NULL);
+	contact_info = xmlnode_new("contactInfo");
+	changes = xmlnode_new("propertiesChanged");
 
-	purple_debug_info("MSN CL","Update contact information with new friendly name: %s\n", nickname);
+	switch (type) {
+		xmlnode *annotations;
+		xmlnode *display;
+		xmlnode *a, *n, *v;
+		case MSN_UPDATE_DISPLAY:
+			display = xmlnode_new_child(contact_info, "displayName");
+			xmlnode_insert_data(display, value, -1);
+			xmlnode_insert_data(changes, "DisplayName", -1);
+			break;
 
-	escaped_nickname = g_markup_escape_text(nickname, -1);
+		case MSN_UPDATE_ALIAS:
+			annotations = xmlnode_new_child(contact_info, "annotations");
+			xmlnode_insert_data(changes, "Annotation ", -1);
 
-	body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, escaped_nickname);
+			a = xmlnode_new_child(annotations, "Annotation");
+			n = xmlnode_new_child(a, "Name");
+			xmlnode_insert_data(n, "AB.NickName", -1);
+			v = xmlnode_new_child(a, "Value");
+			xmlnode_insert_data(v, value, -1);
+			break;
+
+		default:
+			g_return_if_reached();
+	}
+
+
+
+	state = msn_callback_state_new(session);
 
-	msn_soap_message_send(contact->session,
-		msn_soap_message_new(MSN_CONTACT_UPDATE_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
-		msn_update_contact_read_cb, NULL);
+	state->body = xmlnode_from_str(MSN_CONTACT_UPDATE_TEMPLATE, -1);
+	state->action = MSN_UPDATE_INFO;
+	state->post_action = MSN_CONTACT_UPDATE_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_update_contact_read_cb;
+
+	contact = xmlnode_get_child(state->body, "Body/ABContactUpdate/contacts/Contact");
+	xmlnode_insert_child(contact, contact_info);
+	xmlnode_insert_child(contact, changes);
 
-	g_free(escaped_nickname);
-	g_free(body);
+	if (!strcmp(passport, "Me")) {
+		xmlnode *contactType = xmlnode_new_child(contact_info, "contactType");
+		xmlnode_insert_data(contactType, "Me", -1);
+	} else {
+		MsnUser *user = msn_userlist_find_user(session->userlist, passport);
+		xmlnode *contactId = xmlnode_new_child(contact, "contactId");
+		msn_callback_state_set_uid(state, user->uid);
+		xmlnode_insert_data(contactId, state->uid, -1);
+	}
+
+	msn_contact_request(state);
 }
 
 static void
@@ -1144,74 +1277,70 @@
 	MsnCallbackState *state = data;
 	MsnSession *session = state->session;
 
-	if (resp != NULL) {
-		purple_debug_info("MSN CL", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
+	purple_debug_info("MSN CL", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
 
-		if (state->list_id == MSN_LIST_PL) {
-			MsnUser *user = msn_userlist_find_user(session->userlist, state->who);
+	if (state->list_id == MSN_LIST_PL) {
+		MsnUser *user = msn_userlist_find_user(session->userlist, state->who);
+		MsnCallbackState *new_state = msn_callback_state_dup(state);
 
-			if (user != NULL)
-				msn_user_unset_op(user, MSN_LIST_PL_OP);
+		if (user != NULL)
+			msn_user_unset_op(user, MSN_LIST_PL_OP);
 
-			msn_add_contact_to_list(session->contact, state, state->who, MSN_LIST_RL);
-			return;
-		} else if (state->list_id == MSN_LIST_AL) {
-			purple_privacy_permit_remove(session->account, state->who, TRUE);
-			msn_add_contact_to_list(session->contact, NULL, state->who, MSN_LIST_BL);
-		} else if (state->list_id == MSN_LIST_BL) {
-			purple_privacy_deny_remove(session->account, state->who, TRUE);
-			msn_add_contact_to_list(session->contact, NULL, state->who, MSN_LIST_AL);
-		}
+		msn_add_contact_to_list(session, new_state, state->who, MSN_LIST_RL);
+		return;
+	} else if (state->list_id == MSN_LIST_AL) {
+		purple_privacy_permit_remove(session->account, state->who, TRUE);
+		msn_add_contact_to_list(session, NULL, state->who, MSN_LIST_BL);
+	} else if (state->list_id == MSN_LIST_BL) {
+		purple_privacy_deny_remove(session->account, state->who, TRUE);
+		msn_add_contact_to_list(session, NULL, state->who, MSN_LIST_AL);
 	}
 
-	msn_callback_state_free(state);
 }
 
 void
-msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state,
+msn_del_contact_from_list(MsnSession *session, MsnCallbackState *state,
 			  const gchar *passport, const MsnListId list)
 {
 	gchar *body = NULL, *member = NULL;
 	MsnSoapPartnerScenario partner_scenario;
 	MsnUser *user;
 
-	g_return_if_fail(contact != NULL);
+	g_return_if_fail(session != NULL);
 	g_return_if_fail(passport != NULL);
 	g_return_if_fail(list < 5);
 
 	purple_debug_info("MSN CL", "Deleting contact %s from %s list\n", passport, MsnMemberRole[list]);
 
 	if (state == NULL) {
-		state = msn_callback_state_new(contact->session);
+		state = msn_callback_state_new(session);
 	}
 	msn_callback_state_set_list_id(state, list);
 	msn_callback_state_set_who(state, passport);
 
 	if (list == MSN_LIST_PL) {
-		g_return_if_fail(contact->session != NULL);
-		g_return_if_fail(contact->session->userlist != NULL);
+		g_return_if_fail(session->userlist != NULL);
 
-		user = msn_userlist_find_user(contact->session->userlist, passport);
+		user = msn_userlist_find_user(session->userlist, passport);
 
 		partner_scenario = MSN_PS_CONTACT_API;
 		member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, user->membership_id[MSN_LIST_PL]);
 	} else {
 		/* list == MSN_LIST_AL || list == MSN_LIST_BL */
 		partner_scenario = MSN_PS_BLOCK_UNBLOCK;
-
+		
 		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
 	}
 
-	body = g_strdup_printf( MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE,
-			        MsnSoapPartnerScenarioText[partner_scenario],
-			        MsnMemberRole[list],
-			        member);
+	body = g_strdup_printf(MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE,
+		MsnSoapPartnerScenarioText[partner_scenario],
+		MsnMemberRole[list], member);
 
-	msn_soap_message_send(contact->session,
-		msn_soap_message_new(MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_SHARE_POST_URL,
-		msn_del_contact_from_list_read_cb, state);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION;
+	state->post_url = MSN_SHARE_POST_URL;
+	state->cb = msn_del_contact_from_list_read_cb;
+	msn_contact_request(state);
 
 	g_free(member);
 	g_free(body);
@@ -1225,47 +1354,41 @@
 
 	g_return_if_fail(state != NULL);
 	g_return_if_fail(state->session != NULL);
-	g_return_if_fail(state->session->contact != NULL);
+
+	purple_debug_info("MSN CL", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
 
-	if (resp != NULL) {
-		purple_debug_info("MSN CL", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
-
-		if (state->list_id == MSN_LIST_RL) {
-			MsnUser *user = msn_userlist_find_user(state->session->userlist, state->who);
+	if (state->list_id == MSN_LIST_RL) {
+		MsnUser *user = msn_userlist_find_user(state->session->userlist, state->who);
 
-			if (user != NULL) {
-				msn_user_set_op(user, MSN_LIST_RL_OP);
-			}
-
-			if (state->action & MSN_DENIED_BUDDY) {
+		if (user != NULL) {
+			msn_user_set_op(user, MSN_LIST_RL_OP);
+		}
 
-				msn_add_contact_to_list(state->session->contact, NULL, state->who, MSN_LIST_BL);
-			} else if (state->list_id == MSN_LIST_AL) {
-				purple_privacy_permit_add(state->session->account, state->who, TRUE);
-			} else if (state->list_id == MSN_LIST_BL) {
-				purple_privacy_deny_add(state->session->account, state->who, TRUE);
-			}
+		if (state->action & MSN_DENIED_BUDDY) {
+			msn_add_contact_to_list(state->session, NULL, state->who, MSN_LIST_BL);
+		} else if (state->list_id == MSN_LIST_AL) {
+			purple_privacy_permit_add(state->session->account, state->who, TRUE);
+		} else if (state->list_id == MSN_LIST_BL) {
+			purple_privacy_deny_add(state->session->account, state->who, TRUE);
 		}
 	}
-
-	msn_callback_state_free(state);
 }
 
 void
-msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
+msn_add_contact_to_list(MsnSession *session, MsnCallbackState *state,
 			const gchar *passport, const MsnListId list)
 {
 	gchar *body = NULL, *member = NULL;
 	MsnSoapPartnerScenario partner_scenario;
 
-	g_return_if_fail(contact != NULL);
+	g_return_if_fail(session != NULL);
 	g_return_if_fail(passport != NULL);
 	g_return_if_fail(list < 5);
 
 	purple_debug_info("MSN CL", "Adding contact %s to %s list\n", passport, MsnMemberRole[list]);
 
 	if (state == NULL) {
-		state = msn_callback_state_new(contact->session);
+		state = msn_callback_state_new(session);
 	}
 	msn_callback_state_set_list_id(state, list);
 	msn_callback_state_set_who(state, passport);
@@ -1275,15 +1398,14 @@
 	member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, state->who);
 
 	body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE,
-			       MsnSoapPartnerScenarioText[partner_scenario],
-			       MsnMemberRole[list],
-			       member);
+		MsnSoapPartnerScenarioText[partner_scenario],
+		MsnMemberRole[list], member);
 
-	msn_soap_message_send(contact->session,
-		msn_soap_message_new(MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_SHARE_POST_URL,
-		msn_add_contact_to_list_read_cb, state);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION;
+	state->post_url = MSN_SHARE_POST_URL;
+	state->cb = msn_add_contact_to_list_read_cb;
+	msn_contact_request(state);
 
 	g_free(member);
 	g_free(body);
@@ -1293,24 +1415,23 @@
 static void
 msn_gleams_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	if (resp != NULL)
-		purple_debug_info("MSNP14","Gleams read done\n");
-	else
-		purple_debug_info("MSNP14","Gleams read failed\n");
+	purple_debug_info("MSNP14","Gleams read done\n");
 }
 
 /*get the gleams info*/
 void
-msn_get_gleams(MsnContact *contact)
+msn_get_gleams(MsnSession *session)
 {
 	MsnSoapReq *soap_request;
 
 	purple_debug_info("MSNP14","msn get gleams info...\n");
-	msn_soap_message_send(contact->session,
-		msn_soap_message_new(MSN_GET_GLEAMS_SOAP_ACTION,
-			xmlnode_from_str(MSN_GLEAMS_TEMPLATE, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
-		msn_gleams_read_cb, NULL);
+
+	state = msn_callback_state_new(session);
+	state->body = xmlnode_from_str(MSN_GLEAMS_TEMPLATE, -1);
+	state->post_action = MSN_GET_GLEAMS_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_gleams_read_cb;
+	msn_contact_request(state);
 }
 #endif
 
@@ -1323,68 +1444,62 @@
 msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
 	MsnCallbackState *state = data;
+	MsnSession *session;
+	MsnUserList *userlist;
 
 	purple_debug_info("MSNCL", "Group request successful.\n");
 
 	g_return_if_fail(state->session != NULL);
 	g_return_if_fail(state->session->userlist != NULL);
-	g_return_if_fail(state->session->contact != NULL);
+
+	session = state->session;
+	userlist = session->userlist;
 
-	if (resp == NULL) {
-		msn_callback_state_free(state);
-		return;
+	if (state->action & MSN_RENAME_GROUP) {
+		msn_userlist_rename_group_id(session->userlist,
+					     state->guid,
+					     state->new_group_name);
 	}
 
-	if (state) {
-		MsnSession *session = state->session;
-		MsnUserList *userlist = session->userlist;
-
-		if (state->action & MSN_RENAME_GROUP) {
-			msn_userlist_rename_group_id(session->userlist,
-						     state->guid,
-						     state->new_group_name);
-		}
+	if (state->action & MSN_ADD_GROUP) {
+		/* the response is taken from
+		   http://telepathy.freedesktop.org/wiki/Pymsn/MSNP/ContactListActions
+		   should copy it to msnpiki some day */
+		xmlnode *guid_node = xmlnode_get_child(resp->xml,
+			"Body/ABGroupAddResponse/ABGroupAddResult/guid");
 
-		if (state->action & MSN_ADD_GROUP) {
-			/* the response is taken from
-			   http://telepathy.freedesktop.org/wiki/Pymsn/MSNP/ContactListActions
-			   should copy it to msnpiki some day */
-			xmlnode *guid_node = msn_soap_xml_get(resp->xml,
-				"Body/ABGroupAddResponse/ABGroupAddResult/guid");
+		if (guid_node) {
+			char *guid = xmlnode_get_data(guid_node);
 
-			if (guid_node) {
-				char *guid = xmlnode_get_data(guid_node);
-
-				/* create and add the new group to the userlist */
-				purple_debug_info("MSNCL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid);
-				msn_group_new(session->userlist, guid, state->new_group_name);
+			/* create and add the new group to the userlist */
+			purple_debug_info("MSNCL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid);
+			msn_group_new(session->userlist, guid, state->new_group_name);
 
-				if (state->action & MSN_ADD_BUDDY) {
-					msn_userlist_add_buddy(session->userlist,
-						state->who,
-						state->new_group_name);
-				} else if (state->action & MSN_MOVE_BUDDY) {
-					msn_add_contact_to_group(session->contact, state, state->who, guid);
-					g_free(guid);
-					return;
-				}
-				g_free(guid);
-			} else {
-				purple_debug_info("MSNCL", "Adding group %s failed\n",
+			if (state->action & MSN_ADD_BUDDY) {
+				msn_userlist_add_buddy(session->userlist,
+					state->who,
 					state->new_group_name);
+			} else if (state->action & MSN_MOVE_BUDDY) {
+				/* This will be freed when the add contact callback fires */
+				MsnCallbackState *new_state = msn_callback_state_dup(state);
+				msn_add_contact_to_group(session, new_state, state->who, guid); 
+				g_free(guid);
+				return;
 			}
+			g_free(guid);
+		} else {
+			purple_debug_info("MSNCL", "Adding group %s failed\n",
+				state->new_group_name);
 		}
-
-		if (state->action & MSN_DEL_GROUP) {
-			GList *l;
+	}
 
-			msn_userlist_remove_group_id(session->userlist, state->guid);
-			for (l = userlist->users; l != NULL; l = l->next) {
-				msn_user_remove_group_id( (MsnUser *)l->data, state->guid);
-			}
+	if (state->action & MSN_DEL_GROUP) {
+		GList *l;
+
+		msn_userlist_remove_group_id(session->userlist, state->guid);
+		for (l = userlist->users; l != NULL; l = l->next) {
+			msn_user_remove_group_id( (MsnUser *)l->data, state->guid);
 		}
-
-		msn_callback_state_free(state);
 	}
 }
 
@@ -1393,6 +1508,7 @@
 msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_name)
 {
 	char *body = NULL;
+	char *escaped_group_name = NULL;
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(group_name != NULL);
@@ -1409,14 +1525,16 @@
 	/* escape group name's html special chars so it can safely be sent
 	* in a XML SOAP request
 	*/
-	body = g_markup_printf_escaped(MSN_GROUP_ADD_TEMPLATE, group_name);
+	escaped_group_name = g_markup_escape_text(group_name, -1);
+	body = g_strdup_printf(MSN_GROUP_ADD_TEMPLATE, escaped_group_name);
 
-	msn_soap_message_send(session,
-		msn_soap_message_new(MSN_GROUP_ADD_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
-		msn_group_read_cb, state);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_GROUP_ADD_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_group_read_cb;
+	msn_contact_request(state);
 
+	g_free(escaped_group_name);
 	g_free(body);
 }
 
@@ -1454,11 +1572,11 @@
 
 	body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE, guid);
 
-	msn_soap_message_send(session,
-		msn_soap_message_new(MSN_GROUP_DEL_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
-		msn_group_read_cb, state);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_GROUP_DEL_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_group_read_cb;
+	msn_contact_request(state);
 
 	g_free(body);
 }
@@ -1470,6 +1588,7 @@
 	gchar *body = NULL;
 	const gchar * guid;
 	MsnCallbackState *state;
+	char *escaped_group_name;
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(session->userlist != NULL);
@@ -1487,20 +1606,22 @@
 	msn_callback_state_set_new_group_name(state, new_group_name);
 
 	if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) {
-		msn_add_group(session, state, new_group_name);
+		MsnCallbackState *new_state = msn_callback_state_dup(state);
+		msn_add_group(session, new_state, new_group_name);
 		// XXX move every buddy there (we probably need to fix concurrent SOAP reqs first)
 	}
 
 	msn_callback_state_set_action(state, MSN_RENAME_GROUP);
 
-	body = g_markup_printf_escaped(MSN_GROUP_RENAME_TEMPLATE,
-		guid, new_group_name);
+	escaped_group_name = g_markup_escape_text(new_group_name, -1);
+	body = g_strdup_printf(MSN_GROUP_RENAME_TEMPLATE, guid, escaped_group_name);
 
-	msn_soap_message_send(session,
-		msn_soap_message_new(MSN_GROUP_RENAME_SOAP_ACTION,
-			xmlnode_from_str(body, -1)),
-		MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
-		msn_group_read_cb, state);
+	state->body = xmlnode_from_str(body, -1);
+	state->post_action = MSN_GROUP_RENAME_SOAP_ACTION;
+	state->post_url = MSN_ADDRESS_BOOK_POST_URL;
+	state->cb = msn_group_read_cb;
+	msn_contact_request(state);
 
+	g_free(escaped_group_name);
 	g_free(body);
 }
--- a/libpurple/protocols/msn/contact.h	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/contact.h	Sat Jun 21 07:36:16 2008 +0000
@@ -25,15 +25,20 @@
 #ifndef _MSN_CONTACT_H_
 #define _MSN_CONTACT_H_
 
+#include "session.h"
+
 #define MSN_CONTACT_SERVER	"contacts.msn.com"
 
 /* Get Contact List */
 
 #define MSN_GET_CONTACT_POST_URL	"/abservice/SharingService.asmx"
 #define MSN_GET_CONTACT_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/FindMembership"
-#define MSN_GET_CONTACT_UPDATE_XML "<View>Full</View>"\
+
+#define MSN_GET_CONTACT_UPDATE_XML \
+	"<View>Full</View>"\
 	"<deltasOnly>true</deltasOnly>"\
 	"<lastChange>%s</lastChange>"
+
 #define MSN_GET_CONTACT_TEMPLATE	"<?xml version='1.0' encoding='utf-8'?>"\
 "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
 	"<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
@@ -44,6 +49,7 @@
 		 "</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest xmlns=\"http://www.msn.com/webservices/AddressBook\">false</ManagedGroupRequest>"\
+			"<TicketToken>%s</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
@@ -72,7 +78,11 @@
 #define MSN_ADD_ADDRESSBOOK_SOAP_ACTION     "http://www.msn.com/webservices/AddressBook/ABAdd"
 
 #define MSN_ADD_ADDRESSBOOK_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
-"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
@@ -81,6 +91,7 @@
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>%s</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -98,7 +109,8 @@
 /* Get AddressBook */
 #define MSN_GET_ADDRESS_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABFindAll"
 #define MSN_GET_ADDRESS_FULL_TIME	"0001-01-01T00:00:00.0000000-08:00"
-#define MSN_GET_ADDRESS_UPDATE_XML "<deltasOnly>true</deltasOnly>"\
+#define MSN_GET_ADDRESS_UPDATE_XML \
+	"<deltasOnly>true</deltasOnly>"\
 	"<lastChange>%s</lastChange>"
 
 #define MSN_GET_GLEAM_UPDATE_XML \
@@ -107,7 +119,11 @@
 	"<dynamicItemLastChange>%s</dynamicItemLastChange>"
 
 #define MSN_GET_ADDRESS_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
-"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
@@ -116,6 +132,7 @@
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>%s</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -131,7 +148,11 @@
 /*Gleams SOAP request template*/
 #define MSN_GET_GLEAMS_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll"
 #define MSN_GLEAMS_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
-"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
@@ -140,6 +161,7 @@
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -157,26 +179,40 @@
  * Contact Management SOAP actions
  *******************************************************/
 
-/* Add a new contact t*/
+/* Add a new contact */
 #define MSN_CONTACT_ADD_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactAdd"
-#define MSN_CONTACT_LIVE_PENDING_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><contactType>LivePending</contactType><passportName>%s</passportName><isMessengerUser>true</isMessengerUser></contactInfo></Contact>"
+#define MSN_CONTACT_LIVE_PENDING_XML \
+	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+		"<contactInfo>"\
+			"<contactType>LivePending</contactType>"\
+			"<passportName>%s</passportName>"\
+			"<isMessengerUser>true</isMessengerUser>"\
+		"</contactInfo>"\
+	"</Contact>"
 
-#define MSN_CONTACT_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-				"<contactInfo>"\
-					"<passportName>%s</passportName>"\
-					"<isSmtp>false</isSmtp>"\
-					"<isMessengerUser>true</isMessengerUser>"\
-				"</contactInfo>"\
-			"</Contact>"
+#define MSN_CONTACT_XML	\
+	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+		"<contactInfo>"\
+			"<passportName>%s</passportName>"\
+			"<isSmtp>false</isSmtp>"\
+			"<isMessengerUser>true</isMessengerUser>"\
+		"</contactInfo>"\
+	"</Contact>"
 
-#define MSN_CONTACT_DISPLAYNAME_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><displayName>%s</displayName><passportName>%s</passportName><isMessengerUser>true</isMessengerUser></contactInfo></Contact>"
-
-#define MSN_ADD_CONTACT_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>ContactSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABContactAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts><options><EnableAllowListManagement>true</EnableAllowListManagement></options></ABContactAdd></soap:Body></soap:Envelope>"
+#define MSN_CONTACT_DISPLAYNAME_XML	\
+	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+		"<contactInfo>"\
+			"<displayName>%s</displayName>"\
+			"<passportName>%s</passportName>"\
+			"<isMessengerUser>true</isMessengerUser>"\
+		"</contactInfo>"\
+	"</Contact>"
 
-/* Add a contact to a group */
-#define MSN_ADD_CONTACT_GROUP_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupContactAdd"
-#define MSN_ADD_CONTACT_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
-"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+#define MSN_ADD_CONTACT_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
@@ -185,6 +221,36 @@
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABContactAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<contacts>%s</contacts>"\
+			"<options>"\
+				"<EnableAllowListManagement>true</EnableAllowListManagement>"\
+			"</options>"\
+		"</ABContactAdd>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+/* Add a contact to a group */
+#define MSN_ADD_CONTACT_GROUP_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupContactAdd"
+#define MSN_ADD_CONTACT_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>ContactSave</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -207,17 +273,39 @@
 /* Delete a contact from the Contact List */
 #define MSN_CONTACT_DEL_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactDelete"
 #define MSN_CONTACT_ID_XML		"<Contact><contactId>%s</contactId></Contact>"
-#define MSN_DEL_CONTACT_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts></ABContactDelete></soap:Body></soap:Envelope>"
+#define MSN_DEL_CONTACT_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>Timer</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<contacts>%s</contacts>"\
+		"</ABContactDelete>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
 
 /* Remove a contact from a group */
 #define MSN_CONTACT_DEL_GROUP_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupContactDelete"
-#define MSN_CONTACT_DEL_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts><groupFilter><groupIds><guid>%s</guid></groupIds></groupFilter></ABGroupContactDelete></soap:Body></soap:Envelope>"
-
-
-/* Update Contact Nickname */
-#define MSN_CONTACT_UPDATE_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactUpdate"
-#define MSN_CONTACT_UPDATE_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
-"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+#define MSN_CONTACT_DEL_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
@@ -226,6 +314,40 @@
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABGroupContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<contacts>%s</contacts>"\
+			"<groupFilter>"\
+				"<groupIds>"\
+					"<guid>%s</guid>"\
+				"</groupIds>"\
+			"</groupFilter>"\
+		"</ABGroupContactDelete>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+
+/* Update Contact Information */
+#define MSN_CONTACT_UPDATE_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactUpdate"
+#define MSN_CONTACT_UPDATE_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>Timer</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -233,18 +355,13 @@
 			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
 			"<contacts>"\
 				"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
-					"<contactInfo>"\
-						"<contactType>Me</contactType>"\
-						"<displayName>%s</displayName>"\
-					"</contactInfo>"\
-					"<propertiesChanged>DisplayName</propertiesChanged>"\
+					""\
 				"</Contact>"\
 			"</contacts>"\
 		"</ABContactUpdate>"\
 	"</soap:Body>"\
 "</soap:Envelope>"
 
-
 /*******************************************************
  * Add/Delete contact from lists SOAP actions
  *******************************************************/
@@ -255,22 +372,28 @@
 #define MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/AddMember"
 #define MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/DeleteMember"
 
-#define MSN_MEMBER_PASSPORT_XML	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
-					"<Type>Passport</Type>"\
-					"<State>Accepted</State>"\
-					"<PassportName>%s</PassportName>"\
-				"</Member>"
+#define MSN_MEMBER_PASSPORT_XML	\
+	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+		"<Type>Passport</Type>"\
+		"<State>Accepted</State>"\
+		"<PassportName>%s</PassportName>"\
+	"</Member>"
 
-#define MSN_MEMBER_MEMBERSHIPID_XML	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
-						"<Type>Passport</Type>"\
-						"<MembershipId>%u</MembershipId>"\
-						"<State>Accepted</State>"\
-					"</Member>"
+#define MSN_MEMBER_MEMBERSHIPID_XML	\
+	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+		"<Type>Passport</Type>"\
+		"<MembershipId>%u</MembershipId>"\
+		"<State>Accepted</State>"\
+	"</Member>"
 
 /* first delete contact from allow list */
 
 #define MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
-"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
@@ -279,6 +402,7 @@
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -301,7 +425,11 @@
 "</soap:Envelope>"
 
 #define MSN_CONTACT_ADD_TO_LIST_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
-"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
 	"<soap:Header>"\
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
@@ -310,6 +438,7 @@
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
 		"</ABAuthHeader>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -339,36 +468,124 @@
 
 /* add a group */
 #define MSN_GROUP_ADD_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupAdd"
-#define MSN_GROUP_ADD_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>GroupSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupAddOptions><fRenameOnMsgrConflict>false</fRenameOnMsgrConflict></groupAddOptions><groupInfo><GroupInfo><name>%s</name><groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType><fMessenger>false</fMessenger><annotations><Annotation><Name>MSN.IM.Display</Name><Value>1</Value></Annotation></annotations></GroupInfo></groupInfo></ABGroupAdd></soap:Body></soap:Envelope>"
+#define MSN_GROUP_ADD_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>GroupSave</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<groupAddOptions>"\
+				"<fRenameOnMsgrConflict>false</fRenameOnMsgrConflict>"\
+			"</groupAddOptions>"\
+			"<groupInfo>"\
+				"<GroupInfo>"\
+					"<name>%s</name>"\
+					"<groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType>"\
+					"<fMessenger>false</fMessenger>"\
+					"<annotations>"\
+						"<Annotation>"\
+							"<Name>MSN.IM.Display</Name>"\
+							"<Value>1</Value>"\
+						"</Annotation>"\
+					"</annotations>"\
+				"</GroupInfo>"\
+			"</groupInfo>"\
+		"</ABGroupAdd>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
 
 /* delete a group */
 #define MSN_GROUP_DEL_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupDelete"
-#define MSN_GROUP_DEL_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupFilter><groupIds><guid>%s</guid></groupIds></groupFilter></ABGroupDelete></soap:Body></soap:Envelope>"
+#define MSN_GROUP_DEL_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>Timer</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<groupFilter>"\
+				"<groupIds>"\
+					"<guid>%s</guid>"\
+				"</groupIds>"\
+			"</groupFilter>"\
+		"</ABGroupDelete>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
 
 /* change a group's name */
 #define MSN_GROUP_RENAME_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupUpdate"
-#define MSN_GROUP_RENAME_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groups><Group><groupId>%s</groupId><groupInfo><name>%s</name></groupInfo><propertiesChanged>GroupName </propertiesChanged></Group></groups></ABGroupUpdate></soap:Body></soap:Envelope>"
+#define MSN_GROUP_RENAME_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope"\
+	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
+	" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\
+	" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>Timer</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+			"<TicketToken>EMPTY</TicketToken>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABGroupUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<groups>"\
+				"<Group>"\
+					"<groupId>%s</groupId>"\
+					"<groupInfo>"\
+						"<name>%s</name>"\
+					"</groupInfo>"\
+					"<propertiesChanged>GroupName </propertiesChanged>"\
+				"</Group>"\
+			"</groups>"\
+		"</ABGroupUpdate>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
 
 typedef enum
 {
-	MSN_ADD_BUDDY			= 0x01,
-	MSN_MOVE_BUDDY			= 0x02,
-	MSN_ACCEPTED_BUDDY		= 0x04,
-	MSN_DENIED_BUDDY		= 0x08,
-	MSN_ADD_GROUP			= 0x10,
-	MSN_DEL_GROUP			= 0x20,
-	MSN_RENAME_GROUP		= 0x40,
+	MSN_ADD_BUDDY       = 0x01,
+	MSN_MOVE_BUDDY      = 0x02,
+	MSN_ACCEPTED_BUDDY  = 0x04,
+	MSN_DENIED_BUDDY    = 0x08,
+	MSN_ADD_GROUP       = 0x10,
+	MSN_DEL_GROUP       = 0x20,
+	MSN_RENAME_GROUP    = 0x40,
+	MSN_UPDATE_INFO     = 0x80,
 } MsnCallbackAction;
 
-typedef struct _MsnContact MsnContact;
-
-struct _MsnContact
-{
-	MsnSession *session;
-
-	MsnSoapConn *soapconn;
-};
-
 typedef struct _MsnCallbackState MsnCallbackState;
 
 struct _MsnCallbackState
@@ -381,6 +598,11 @@
 	MsnListId list_id;
 	MsnCallbackAction action;
 	MsnSession *session;
+	xmlnode *body;
+	xmlnode *token;
+	const gchar *post_action;
+	const gchar *post_url;
+	/* MsnSoapCallback */ void *cb;
 };
 
 typedef enum
@@ -392,13 +614,18 @@
 	MSN_PS_BLOCK_UNBLOCK
 } MsnSoapPartnerScenario;
 
+typedef enum
+{
+	MSN_UPDATE_DISPLAY,	/* Real display name */
+	MSN_UPDATE_ALIAS,	/* Aliased display name */
+	MSN_UPDATE_COMMENT
+} MsnContactUpdateType;
+
 /************************************************
  * function prototype
  ************************************************/
-MsnContact * msn_contact_new(MsnSession *session);
-void msn_contact_destroy(MsnContact *contact);
-
 MsnCallbackState * msn_callback_state_new(MsnSession *session);
+MsnCallbackState * msn_callback_state_dup(MsnCallbackState *state);
 void msn_callback_state_free(MsnCallbackState *state);
 void msn_callback_state_set_who(MsnCallbackState *state, const gchar *who);
 void msn_callback_state_set_uid(MsnCallbackState *state, const gchar *uid);
@@ -411,24 +638,24 @@
 void msn_callback_state_set_action(MsnCallbackState *state,
 				   MsnCallbackAction action);
 
-void msn_contact_connect(MsnContact *contact);
-void msn_get_contact_list(MsnContact * contact,
+void msn_contact_connect(MsnSession *session);
+void msn_get_contact_list(MsnSession *session,
 			  const MsnSoapPartnerScenario partner_scenario,
 			  const char *update);
-void msn_get_address_book(MsnContact *contact,
+void msn_get_address_book(MsnSession *session,
 			  const MsnSoapPartnerScenario partner_scenario,
 			  const char * update, const char * gupdate);
 
 /* contact SOAP operations */
-void msn_update_contact(MsnContact *contact, const char* nickname);
+void msn_update_contact(MsnSession *session, const char *passport, MsnContactUpdateType type, const char* value);
 
-void msn_add_contact(MsnContact *contact, MsnCallbackState *state,
+void msn_add_contact(MsnSession *session, MsnCallbackState *state,
 		     const char *passport);
-void msn_delete_contact(MsnContact *contact, const char *contactId);
+void msn_delete_contact(MsnSession *session, const char *contactId);
 
-void msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
+void msn_add_contact_to_group(MsnSession *session, MsnCallbackState *state,
 			      const char *passport, const char *groupId);
-void msn_del_contact_from_group(MsnContact *contact, const char *passport,
+void msn_del_contact_from_group(MsnSession *session, const char *passport,
 				const char *group_name);
 /* group operations */
 void msn_add_group(MsnSession *session, MsnCallbackState *state,
@@ -438,9 +665,9 @@
 						   const char *new_group_name);
 
 /* lists operations */
-void msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
+void msn_add_contact_to_list(MsnSession *session, MsnCallbackState *state,
 			     const gchar *passport, const MsnListId list);
-void msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state,
+void msn_del_contact_from_list(MsnSession *session, MsnCallbackState *state,
 			       const gchar *passport, const MsnListId list);
 
 void msn_contact_connect_init(MsnSoapConn *soapconn);
--- a/libpurple/protocols/msn/msn.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Sat Jun 21 07:36:16 2008 +0000
@@ -27,6 +27,7 @@
 
 #include "msn.h"
 #include "accountopt.h"
+#include "contact.h"
 #include "msg.h"
 #include "page.h"
 #include "pluginpref.h"
@@ -393,6 +394,29 @@
 }
 
 static void
+msn_show_blocked_text(PurplePluginAction *action)
+{
+	PurpleConnection *pc = (PurpleConnection *) action->context;
+	MsnSession *session;
+	char *title;
+
+	session = pc->proto_data;
+
+	title = g_strdup_printf(_("Blocked Text for %s"), session->account->username);
+	if (session->blocked_text == NULL) {
+		purple_notify_formatted(pc, title, title, NULL, _("No text is blocked for this account."), NULL, NULL);
+	} else {
+		char *blocked_text;
+		blocked_text = g_strdup_printf(_("MSN servers are currently blocking the following regular expressions:<br/>%s"),
+		                               session->blocked_text);
+		
+		purple_notify_formatted(pc, title, title, NULL, blocked_text, NULL, NULL);
+		g_free(blocked_text);
+	}
+	g_free(title);
+}
+
+static void
 msn_show_hotmail_inbox(PurplePluginAction *action)
 {
 	PurpleConnection *gc;
@@ -567,6 +591,30 @@
 	return "msn";
 }
 
+static const char *
+msn_list_emblems(PurpleBuddy *b)
+{
+	MsnUser *user = b->proto_data;
+
+	if (user != NULL) {
+		if (user->clientid & MSN_CLIENT_CAP_BOT)
+			return "bot";
+		if (user->clientid & MSN_CLIENT_CAP_WIN_MOBILE)
+			return "hiptop"; /* XXX: Rename to Mobile / Use different icon? */
+#if 0
+		/* XXX: Since we don't support this, there's no point in showing it just yet */
+		if (user->clientid & MSN_CLIENT_CAP_SCHANNEL)
+			return "secure";
+#endif
+		if (user->clientid & MSN_CLIENT_CAP_WEBMSGR)
+			return "web";
+		if (user->networkid == MSN_NETWORK_YAHOO)
+			return "yahoo";
+	}
+
+	return NULL;
+}
+
 /*
  * Set the User status text
  */
@@ -808,6 +856,11 @@
 			msn_show_set_mobile_pages);
 	m = g_list_append(m, act);
 
+	m = g_list_append(m, NULL);
+	act = purple_plugin_action_new(_("View Blocked Text..."),
+			msn_show_blocked_text);
+	m = g_list_append(m, act);
+
 	account = purple_connection_get_account(gc);
 	user = msn_normalize(account, purple_account_get_username(account));
 
@@ -1042,6 +1095,8 @@
 {
 	PurpleAccount *account;
 	PurpleBuddy *buddy = purple_find_buddy(gc->account, who);
+	MsnSession *session;
+	MsnSwitchBoard *swboard;
 	MsnMessage *msg;
 	char *msgformat;
 	char *msgtext;
@@ -1051,6 +1106,9 @@
 	account = purple_connection_get_account(gc);
 	username = purple_account_get_username(account);
 
+	session = gc->proto_data;
+	swboard = msn_session_find_swboard(session, who);
+
 	if (buddy) {
 		PurplePresence *p = purple_buddy_get_presence(buddy);
 		if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) {
@@ -1062,10 +1120,12 @@
 	}
 
 	msn_import_html(message, &msgformat, &msgtext);
-	if(msn_user_is_online(account, who)||
-		msn_user_is_yahoo(account, who)){
-		/*User online,then send Online Instant Message*/
-
+	if (msn_user_is_online(account, who)||
+		msn_user_is_yahoo(account, who) ||
+		swboard != NULL){
+		/*User online or have a swboard open because it's invisible
+		 * and sent us a message,then send Online Instant Message*/
+ 
 		if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564)
 		{
 			g_free(msgformat);
@@ -1084,13 +1144,10 @@
 		purple_debug_info("MSNP14","prepare to send online Message\n");
 		if (g_ascii_strcasecmp(who, username))
 		{
-			MsnSession *session;
-			MsnSwitchBoard *swboard;
 			MsnEmoticon *smile;
 			GSList *smileys;
 			GString *emoticons = NULL;
 
-			session = gc->proto_data;
 			if(msn_user_is_yahoo(account,who)){
 				/*we send the online and offline Message to Yahoo User via UBM*/
 				purple_debug_info("MSNP14","send to Yahoo User\n");
@@ -1145,19 +1202,20 @@
 		}
 
 		msn_message_destroy(msg);
-	}else	{
+	} else {
 		/*send Offline Instant Message,only to MSN Passport User*/
-		MsnSession *session;
 		char *friendname;
 
 		purple_debug_info("MSNP14","prepare to send offline Message\n");
-		session = gc->proto_data;
 
 		friendname = msn_encode_mime(account->username);
 		msn_oim_prep_send_msg_info(session->oim,
 			purple_account_get_username(account),
-			friendname, who,	message);
+			friendname, who, msgtext);
 		msn_oim_send_msg(session->oim);
+
+		g_free(msgformat);
+		g_free(msgtext);
 		g_free(friendname);
 	}
 
@@ -1369,10 +1427,10 @@
 		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
 
 		/* delete contact from Block list and add it to Allow in the callback */
-		msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL);
+		msn_del_contact_from_list(session, NULL, who, MSN_LIST_BL);
 	} else {
 		/* just add the contact to Allow list */
-		msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_AL);
+		msn_add_contact_to_list(session, NULL, who, MSN_LIST_AL);
 	}
 
 
@@ -1397,10 +1455,10 @@
 		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
 
 		/* delete contact from Allow list and add it to Block in the callback */
-		msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL);
+		msn_del_contact_from_list(session, NULL, who, MSN_LIST_AL);
 	} else {
 		/* just add the contact to Block list */
-		msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_BL);
+		msn_add_contact_to_list(session, NULL, who, MSN_LIST_BL);
 	}
 
 	msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
@@ -1423,7 +1481,7 @@
 
 	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
 
-	msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL);
+	msn_del_contact_from_list(session, NULL, who, MSN_LIST_AL);
 
 	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
 		msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
@@ -1446,7 +1504,7 @@
 
 	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
 
-	msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL);
+	msn_del_contact_from_list(session, NULL, who, MSN_LIST_BL);
 
 	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
 		msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
@@ -1574,6 +1632,15 @@
 	}
 }
 
+static void msn_alias_buddy(PurpleConnection *pc, const char *name, const char *alias)
+{
+	MsnSession *session;
+
+	session = pc->proto_data;
+
+	msn_update_contact(session, name, MSN_UPDATE_ALIAS, alias);
+}
+
 static void
 msn_group_buddy(PurpleConnection *gc, const char *who,
 				const char *old_group_name, const char *new_group_name)
@@ -1739,7 +1806,7 @@
 }
 
 static void msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data,
-		const gchar *url_text, size_t len, const gchar *error_message);
+		const gchar *url_text, gsize len, const gchar *error_message);
 
 #endif
 
@@ -2375,7 +2442,7 @@
 	NULL,					/* protocol_options */
 	{"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND},	/* icon_spec */
 	msn_list_icon,			/* list_icon */
-	NULL,				/* list_emblems */
+	msn_list_emblems,		/* list_emblems */
 	msn_status_text,		/* status_text */
 	msn_tooltip_text,		/* tooltip_text */
 	msn_status_types,		/* away_states */
@@ -2411,7 +2478,7 @@
 	NULL,					/* register_user */
 	NULL,					/* get_cb_info */
 	NULL,					/* get_cb_away */
-	NULL,					/* alias_buddy */
+	msn_alias_buddy,		/* alias_buddy */
 	msn_group_buddy,		/* group_buddy */
 	msn_rename_group,		/* rename_group */
 	NULL,					/* buddy_free */
--- a/libpurple/protocols/msn/msn.h	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/msn.h	Sat Jun 21 07:36:16 2008 +0000
@@ -66,12 +66,10 @@
 /* Windows Live Messenger Server*/
 #define WLM_SERVER			"muser.messenger.hotmail.com"
 #define WLM_PORT			1863
-#define WLM_PROT_VER		13
-/*This MSNP14 Support chat with Yahoo Messenger*/
-#define WLM_YAHOO_PROT_VER	14
+#define WLM_PROT_VER		15
 
-#define WLM_MAX_PROTOCOL	14
-#define WLM_MIN_PROTOCOL	13
+#define WLM_MAX_PROTOCOL	15
+#define WLM_MIN_PROTOCOL	15
 
 #define MSN_TYPING_RECV_TIMEOUT 6
 #define MSN_TYPING_SEND_TIMEOUT	4
@@ -107,18 +105,25 @@
 
 typedef enum
 {
-	MSN_CLIENT_CAP_WIN_MOBILE = 0x00001,
-	MSN_CLIENT_CAP_UNKNOWN_1  = 0x00002,
-	MSN_CLIENT_CAP_INK_GIF    = 0x00004,
-	MSN_CLIENT_CAP_INK_ISF    = 0x00008,
-	MSN_CLIENT_CAP_VIDEO_CHAT = 0x00010,
-	MSN_CLIENT_CAP_BASE       = 0x00020,
-	MSN_CLIENT_CAP_MSNMOBILE  = 0x00040,
-	MSN_CLIENT_CAP_MSNDIRECT  = 0x00080,
-	MSN_CLIENT_CAP_WEBMSGR    = 0x00100,
-	MSN_CLIENT_CAP_DIRECTIM   = 0x04000,
-	MSN_CLIENT_CAP_WINKS      = 0x08000,
-	MSN_CLIENT_CAP_SEARCH     = 0x10000
+	MSN_CLIENT_CAP_WIN_MOBILE = 0x000001,
+	MSN_CLIENT_CAP_INK_GIF    = 0x000004,
+	MSN_CLIENT_CAP_INK_ISF    = 0x000008,
+	MSN_CLIENT_CAP_VIDEO_CHAT = 0x000010,
+	MSN_CLIENT_CAP_PACKET     = 0x000020,
+	MSN_CLIENT_CAP_MSNMOBILE  = 0x000040,
+	MSN_CLIENT_CAP_MSNDIRECT  = 0x000080,
+	MSN_CLIENT_CAP_WEBMSGR    = 0x000200,
+	MSN_CLIENT_CAP_TGW        = 0x000800,
+	MSN_CLIENT_CAP_SPACE      = 0x001000,
+	MSN_CLIENT_CAP_MCE        = 0x002000,
+	MSN_CLIENT_CAP_DIRECTIM   = 0x004000,
+	MSN_CLIENT_CAP_WINKS      = 0x008000,
+	MSN_CLIENT_CAP_SEARCH     = 0x010000,
+	MSN_CLIENT_CAP_BOT        = 0x020000,
+	MSN_CLIENT_CAP_VOICEIM    = 0x040000,
+	MSN_CLIENT_CAP_SCHANNEL   = 0x080000,
+	MSN_CLIENT_CAP_SIP_INVITE = 0x100000,
+	MSN_CLIENT_CAP_SDRIVE     = 0x400000
 
 } MsnClientCaps;
 
@@ -129,19 +134,18 @@
 	MSN_CLIENT_VER_6_1 = 0x20,	/* MSNC2 */
 	MSN_CLIENT_VER_6_2 = 0x30,	/* MSNC3 */
 	MSN_CLIENT_VER_7_0 = 0x40,	/* MSNC4 */
-	MSN_CLIENT_VER_7_5 = 0x50	/* MSNC5 */
+	MSN_CLIENT_VER_7_5 = 0x50,	/* MSNC5 */
+	MSN_CLIENT_VER_8_0 = 0x60,	/* MSNC6 */
+	MSN_CLIENT_VER_8_1 = 0x70,	/* MSNC7 */
+	MSN_CLIENT_VER_8_5 = 0x80	/* MSNC8 */
 
 } MsnClientVerId;
 
 #define MSN_CLIENT_ID_VERSION      MSN_CLIENT_VER_7_0
-#define MSN_CLIENT_ID_RESERVED_1   0x00
-#define MSN_CLIENT_ID_RESERVED_2   0x00
-#define MSN_CLIENT_ID_CAPABILITIES MSN_CLIENT_CAP_BASE
+#define MSN_CLIENT_ID_CAPABILITIES MSN_CLIENT_CAP_PACKET
 
 #define MSN_CLIENT_ID \
 	((MSN_CLIENT_ID_VERSION    << 24) | \
-	 (MSN_CLIENT_ID_RESERVED_1 << 16) | \
-	 (MSN_CLIENT_ID_RESERVED_2 <<  8) | \
 	 (MSN_CLIENT_ID_CAPABILITIES))
 
 void msn_act_id(PurpleConnection *gc, const char *entry);
--- a/libpurple/protocols/msn/msnutils.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Sat Jun 21 07:36:16 2008 +0000
@@ -514,8 +514,8 @@
 {
 		PurpleCipher *cipher;
 		PurpleCipherContext *context;
-		char *productKey = MSNP13_WLM_PRODUCT_KEY,
-			 *productID  = MSNP13_WLM_PRODUCT_ID,
+		char *productKey = MSNP15_WLM_PRODUCT_KEY,
+			 *productID  = MSNP15_WLM_PRODUCT_ID,
 			 *hexChars   = "0123456789abcdef",
 			 buf[BUFSIZE];
 		unsigned char md5Hash[16], *newHash;
--- a/libpurple/protocols/msn/nexus.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/nexus.c	Sat Jun 21 07:36:16 2008 +0000
@@ -26,7 +26,23 @@
 #include "nexus.h"
 #include "notification.h"
 
-#undef NEXUS_LOGIN_TWN
+/**************************************************************************
+ * Valid Ticket Tokens
+ **************************************************************************/
+
+#define SSO_VALID_TICKET_DOMAIN 0
+#define SSO_VALID_TICKET_POLICY 1
+static char *ticket_domains[][2] = {
+	/* http://msnpiki.msnfanatic.com/index.php/MSNP15:SSO */
+	/* {"Domain", "Policy Ref URI"}, Purpose */
+	{"messengerclear.live.com", NULL},       /* Authentication for messenger. */
+	{"messenger.msn.com", "?id=507"},        /* Authentication for receiving OIMs. */
+	{"contacts.msn.com", "MBI"},             /* Authentication for the Contact server. */
+	{"messengersecure.live.com", "MBI_SSL"}, /* Authentication for sending OIMs. */
+	{"spaces.live.com", "MBI"},              /* Authentication for the Windows Live Spaces */
+	{"livecontacts.live.com", "MBI"},        /* Live Contacts API, a simplified version of the Contacts SOAP service */
+	{"storage.live.com", "MBI"},             /* Storage REST API */
+};
 
 /**************************************************************************
  * Main
@@ -36,12 +52,17 @@
 msn_nexus_new(MsnSession *session)
 {
 	MsnNexus *nexus;
+	int i;
 
 	nexus = g_new0(MsnNexus, 1);
 	nexus->session = session;
 
-	nexus->challenge_data = g_hash_table_new_full(g_str_hash,
-		g_str_equal, g_free, g_free);
+	nexus->token_len = sizeof(ticket_domains) / sizeof(char *[2]);
+	nexus->tokens = g_new0(MsnTicketToken, nexus->token_len);
+
+	for (i = 0; i < nexus->token_len; i++)
+		nexus->tokens[i].token = g_hash_table_new_full(g_str_hash, g_str_equal,
+		                                               g_free, g_free);
 
 	return nexus;
 }
@@ -49,79 +70,293 @@
 void
 msn_nexus_destroy(MsnNexus *nexus)
 {
-	if (nexus->challenge_data != NULL)
-		g_hash_table_destroy(nexus->challenge_data);
+	int i;
+	for (i = 0; i < nexus->token_len; i++) {
+		g_hash_table_destroy(nexus->tokens[i].token);
+		g_free(nexus->tokens[i].secret);
+	}
+
+	g_free(nexus->tokens);
+	g_free(nexus->policy);
+	g_free(nexus->nonce);
+	g_free(nexus->cipher);
+	g_free(nexus->secret);
+	g_free(nexus);
+}
+
+/**************************************************************************
+ * RPS/SSO Authentication
+ **************************************************************************/
+
+static char *
+rps_create_key(const char *key, int key_len, const char *data, size_t data_len)
+{
+	const guchar magic[] = "WS-SecureConversation";
+	const int magic_len = sizeof(magic) - 1;
+
+	PurpleCipherContext *hmac;
+	guchar hash1[20], hash2[20], hash3[20], hash4[20];
+	char *result;
+
+	hmac = purple_cipher_context_new_by_name("hmac", NULL);
+
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
+	purple_cipher_context_append(hmac, magic, magic_len);
+	purple_cipher_context_append(hmac, (guchar *)data, data_len);
+	purple_cipher_context_digest(hmac, sizeof(hash1), hash1, NULL);
+
+	purple_cipher_context_reset(hmac, NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
+	purple_cipher_context_append(hmac, hash1, 20);
+	purple_cipher_context_append(hmac, magic, magic_len);
+	purple_cipher_context_append(hmac, (guchar *)data, data_len);
+	purple_cipher_context_digest(hmac, sizeof(hash2), hash2, NULL);
+
+	purple_cipher_context_reset(hmac, NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
+	purple_cipher_context_append(hmac, hash1, 20);
+	purple_cipher_context_digest(hmac, sizeof(hash3), hash3, NULL);
+
+	purple_cipher_context_reset(hmac, NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len);
+	purple_cipher_context_append(hmac, hash3, sizeof(hash3));
+	purple_cipher_context_append(hmac, magic, magic_len);
+	purple_cipher_context_append(hmac, (guchar *)data, data_len);
+	purple_cipher_context_digest(hmac, sizeof(hash4), hash4, NULL);
+
+	purple_cipher_context_destroy(hmac);
+
+	result = g_malloc(24);
+	memcpy(result, hash2, sizeof(hash2));
+	memcpy(result + sizeof(hash2), hash4, 4);
+
+	return result;
+}
+
+static char *
+des3_cbc(const char *key, const char *iv, const char *data, int len, gboolean decrypt)
+{
+	PurpleCipherContext *des3;
+	char *out;
+	size_t outlen;
 
-	g_free(nexus->challenge_data_str);
-	g_free(nexus);
+	des3 = purple_cipher_context_new_by_name("des3", NULL);
+	purple_cipher_context_set_key(des3, (guchar *)key);
+	purple_cipher_context_set_batch_mode(des3, PURPLE_CIPHER_BATCH_MODE_CBC);
+	purple_cipher_context_set_iv(des3, (guchar *)iv, 8);
+
+	out = g_malloc(len);
+	if (decrypt)
+		purple_cipher_context_decrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
+	else
+		purple_cipher_context_encrypt(des3, (guchar *)data, len, (guchar *)out, &outlen);
+
+	purple_cipher_context_destroy(des3);
+
+	return out;
+}
+
+#define CRYPT_MODE_CBC 1
+#define CIPHER_TRIPLE_DES 0x6603
+#define HASH_SHA1 0x8004
+static char *
+msn_rps_encrypt(MsnNexus *nexus)
+{
+	MsnUsrKey *usr_key;
+	const char magic1[] = "SESSION KEY HASH";
+	const char magic2[] = "SESSION KEY ENCRYPTION";
+	PurpleCipherContext *hmac;
+	size_t len;
+	guchar hash[20];
+	char *key1, *key2, *key3;
+	gsize key1_len;
+	int *iv;
+	char *nonce_fixed;
+	char *cipher;
+	char *response;
+
+	usr_key = g_malloc(sizeof(MsnUsrKey));
+	usr_key->size = GUINT32_TO_LE(28);
+	usr_key->crypt_mode = GUINT32_TO_LE(CRYPT_MODE_CBC);
+	usr_key->cipher_type = GUINT32_TO_LE(CIPHER_TRIPLE_DES);
+	usr_key->hash_type = GUINT32_TO_LE(HASH_SHA1);
+	usr_key->iv_len = GUINT32_TO_LE(8);
+	usr_key->hash_len = GUINT32_TO_LE(20);
+	usr_key->cipher_len = GUINT32_TO_LE(72);
+
+	key1 = (char *)purple_base64_decode((const char *)nexus->tokens[MSN_AUTH_MESSENGER].secret, &key1_len);
+	key2 = rps_create_key(key1, key1_len, magic1, sizeof(magic1) - 1);
+	key3 = rps_create_key(key1, key1_len, magic2, sizeof(magic2) - 1);
+
+	iv = (int *)usr_key->iv;
+	iv[0] = rand();
+	iv[1] = rand();
+
+	len = strlen(nexus->nonce);
+	hmac = purple_cipher_context_new_by_name("hmac", NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key2, 24);
+	purple_cipher_context_append(hmac, (guchar *)nexus->nonce, len);
+	purple_cipher_context_digest(hmac, 20, hash, NULL);
+	purple_cipher_context_destroy(hmac);
+
+	/* We need to pad this to 72 bytes, apparently */
+	nonce_fixed = g_malloc(len + 8);
+	memcpy(nonce_fixed, nexus->nonce, len);
+	memset(nonce_fixed + len, 0x08, 8);
+	cipher = des3_cbc(key3, usr_key->iv, nonce_fixed, len + 8, FALSE);
+	g_free(nonce_fixed);
+
+	memcpy(usr_key->hash, hash, 20);
+	memcpy(usr_key->cipher, cipher, 72);
+
+	g_free(key1);
+	g_free(key2);
+	g_free(key3);
+	g_free(cipher);
+
+	response = purple_base64_encode((guchar *)usr_key, sizeof(MsnUsrKey));
+
+	g_free(usr_key);
+
+	return response;
 }
 
 /**************************************************************************
  * Login
  **************************************************************************/
 
+/* Used to specify which token to update when only doing single updates */
+typedef struct _MsnNexusUpdateData MsnNexusUpdateData;
+struct _MsnNexusUpdateData {
+	MsnNexus *nexus;
+	int id;
+	GSourceFunc cb;
+	gpointer data;
+};
+
+static gboolean
+nexus_parse_token(MsnNexus *nexus, int id, xmlnode *node)
+{
+	char *token_str, *expiry_str;
+	const char *id_str;
+	char **elems, **cur, **tokens;
+	xmlnode *token = xmlnode_get_child(node, "RequestedSecurityToken/BinarySecurityToken");
+	xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
+	xmlnode *expires = xmlnode_get_child(node, "LifeTime/Expires");
+
+	if (!token)
+		return FALSE;
+
+	/* Use the ID that the server sent us */
+	if (id == -1) {
+		id_str = xmlnode_get_attrib(token, "Id");
+		if (id_str == NULL)
+			return FALSE;
+
+		id = atol(id_str + 7) - 1;	/* 'Compact#' or 'PPToken#' */
+		if (id >= nexus->token_len)
+			return FALSE;	/* Where did this come from? */
+	}
+
+	token_str = xmlnode_get_data(token);
+	if (token_str == NULL)
+		return FALSE;
+
+	g_hash_table_remove_all(nexus->tokens[id].token);
+
+	elems = g_strsplit(token_str, "&", 0);
+
+	for (cur = elems; *cur != NULL; cur++) {
+		tokens = g_strsplit(*cur, "=", 2);
+		g_hash_table_insert(nexus->tokens[id].token, tokens[0], tokens[1]);
+		/* Don't free each of the tokens, only the array. */
+		g_free(tokens);
+	}
+	g_strfreev(elems);
+	g_free(token_str);
+
+	if (secret)
+		nexus->tokens[id].secret = xmlnode_get_data(secret);
+	else
+		nexus->tokens[id].secret = NULL;
+
+	/* Yay for MS using ISO-8601 */
+	expiry_str = xmlnode_get_data(expires);
+	nexus->tokens[id].expiry = purple_str_to_time(expiry_str,
+		FALSE, NULL, NULL, NULL);
+	g_free(expiry_str);
+
+	purple_debug_info("msnp15", "Updated ticket for domain '%s', expires at %" G_GINT64_FORMAT ".\n",
+	                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN],
+	                  (gint64)nexus->tokens[id].expiry);
+	return TRUE;
+}
+
+static gboolean
+nexus_parse_collection(MsnNexus *nexus, int id, xmlnode *collection)
+{
+	xmlnode *node;
+	gboolean result;
+
+	node = xmlnode_get_child(collection, "RequestSecurityTokenResponse");
+
+	if (!node)
+		return FALSE;
+
+	result = TRUE;
+	for (; node && result; node = node->next) {
+		xmlnode *endpoint = xmlnode_get_child(node, "AppliesTo/EndpointReference/Address");
+		char *address = xmlnode_get_data(endpoint);
+
+		if (g_str_equal(address, "http://Passport.NET/tb")) {
+			/* This node contains the stuff for updating tokens. */
+			char *data;
+			xmlnode *cipher = xmlnode_get_child(node, "RequestedSecurityToken/EncryptedData/CipherData/CipherValue");
+			xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret");
+
+			nexus->cipher = xmlnode_get_data(cipher);
+			data = xmlnode_get_data(secret);
+			nexus->secret = (char *)purple_base64_decode(data, NULL);
+			g_free(data);
+
+		} else {
+			result = nexus_parse_token(nexus, id, node);
+		}
+		g_free(address);
+	}
+
+	return result;
+}
+
 static void
 nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
 	MsnNexus *nexus = data;
 	MsnSession *session = nexus->session;
-	xmlnode *node;
+	const char *ticket;
+	char *response;
 
 	if (resp == NULL) {
 		msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect"));
 		return;
 	}
 
-	node = msn_soap_xml_get(resp->xml,	"Body/"
-		"RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse");
-
-	for (; node; node = node->next) {
-		xmlnode *token = msn_soap_xml_get(node,
-			"RequestedSecurityToken/BinarySecurityToken");
-
-		if (token) {
-			char *token_str = xmlnode_get_data(token);
-			char **elems, **cur, **tokens;
-			char *msn_twn_t, *msn_twn_p, *cert_str;
-
-			if (token_str == NULL) continue;
-
-			elems = g_strsplit(token_str, "&", 0);
-
-			for (cur = elems; *cur != NULL; cur++){
-				tokens = g_strsplit(*cur, "=", 2);
-				g_hash_table_insert(nexus->challenge_data, tokens[0], tokens[1]);
-				/* Don't free each of the tokens, only the array. */
-				g_free(tokens);
-			}
-
-			g_free(token_str);
-			g_strfreev(elems);
-
-			msn_twn_t = g_hash_table_lookup(nexus->challenge_data, "t");
-			msn_twn_p = g_hash_table_lookup(nexus->challenge_data, "p");
-
-			/*setup the t and p parameter for session*/
-			g_free(session->passport_info.t);
-			session->passport_info.t = g_strdup(msn_twn_t);
-
-			g_free(session->passport_info.p);
-			session->passport_info.p = g_strdup(msn_twn_p);
-
-			cert_str = g_strdup_printf("t=%s&p=%s",msn_twn_t,msn_twn_p);
-			msn_got_login_params(session, cert_str);
-
-			purple_debug_info("MSN Nexus","Close nexus connection!\n");
-			g_free(cert_str);
-			msn_nexus_destroy(nexus);
-			session->nexus = NULL;
-
-			return;
-		}
+	if (!nexus_parse_collection(nexus, -1,
+	                            xmlnode_get_child(resp->xml,
+	                                              "Body/RequestSecurityTokenResponseCollection"))) {
+		msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Invalid response"));
+		return;
 	}
 
-	/* we must have failed! */
-	msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication: cannot find authenticate token in server response"));
+	ticket = msn_nexus_get_token_str(nexus, MSN_AUTH_MESSENGER);
+	response = msn_rps_encrypt(nexus);
+	msn_got_login_params(session, ticket, response);
+	g_free(response);
 }
 
 /*when connect, do the SOAP Style windows Live ID authentication */
@@ -129,92 +364,258 @@
 msn_nexus_connect(MsnNexus *nexus)
 {
 	MsnSession *session = nexus->session;
-	char *ru,*lc,*id,*tw,*ct,*kpp,*kv,*ver,*rn,*tpf;
-	char *fs0,*fs;
 	const char *username;
 	char *password;
-	char *tail;
-#ifdef NEXUS_LOGIN_TWN
-	char *challenge_str;
-#else
-	char *rst1_str,*rst2_str,*rst3_str;
-#endif
+	GString *domains;
+	char *request;
+	int i;
 
 	MsnSoapMessage *soap;
 
 	purple_debug_info("MSN Nexus","Starting Windows Live ID authentication\n");
 	msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
 
-	/*prepare the Windows Live ID authentication token*/
 	username = purple_account_get_username(session->account);
 	password = g_strndup(purple_connection_get_password(session->account->gc), 16);
 
-	lc =	(char *)g_hash_table_lookup(nexus->challenge_data, "lc");
-	id =	(char *)g_hash_table_lookup(nexus->challenge_data, "id");
-	tw =	(char *)g_hash_table_lookup(nexus->challenge_data, "tw");
-	fs0=	(char *)g_hash_table_lookup(nexus->challenge_data, "fs");
-	ru =	(char *)g_hash_table_lookup(nexus->challenge_data, "ru");
-	ct =	(char *)g_hash_table_lookup(nexus->challenge_data, "ct");
-	kpp=	(char *)g_hash_table_lookup(nexus->challenge_data, "kpp");
-	kv =	(char *)g_hash_table_lookup(nexus->challenge_data, "kv");
-	ver=	(char *)g_hash_table_lookup(nexus->challenge_data, "ver");
-	rn =	(char *)g_hash_table_lookup(nexus->challenge_data, "rn");
-	tpf=	(char *)g_hash_table_lookup(nexus->challenge_data, "tpf");
+	purple_debug_info("msnp15", "Logging on %s, with policy '%s', nonce '%s'\n",
+	                  username, nexus->policy, nexus->nonce);
+
+	domains = g_string_new(NULL);
+	for (i = 0; i < nexus->token_len; i++) {
+		g_string_append_printf(domains, MSN_SSO_RST_TEMPLATE,
+		                       i+1,
+		                       ticket_domains[i][SSO_VALID_TICKET_DOMAIN],
+		                       ticket_domains[i][SSO_VALID_TICKET_POLICY] != NULL ?
+		                           ticket_domains[i][SSO_VALID_TICKET_POLICY] :
+		                           nexus->policy);
+	}
+
+	request = g_strdup_printf(MSN_SSO_TEMPLATE, username, password, domains->str);
+	g_free(password);
+	g_string_free(domains, TRUE);
+
+	soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1));
+	g_free(request);
+	msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL,
+	                      nexus_got_response_cb, nexus);
+}
 
-	/*
-	 * add some fail-safe code to avoid windows Purple Crash bug #1540454
-	 * If any of these string is NULL, will return Authentication Fail!
-	 * for when windows g_strdup_printf() implementation get NULL point,It crashed!
-	 */
-	if(!(lc && id && tw && ru && ct && kpp && kv && ver && tpf)){
-		purple_debug_error("MSN Nexus","WLM Authenticate Key Error!\n");
-		msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication Failed"));
-		g_free(password);
-		msn_nexus_destroy(nexus);
-		session->nexus = NULL;
+static void
+nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
+{
+	MsnNexusUpdateData *ud = data;
+	MsnNexus *nexus = ud->nexus;
+	char iv[8] = {0,0,0,0,0,0,0,0};
+	xmlnode *enckey;
+	char *tmp;
+	char *nonce;
+	gsize len;
+	char *key;
+
+#if 0
+	char *decrypted_pp;
+#endif
+	char *decrypted_data;
+
+	purple_debug_info("msnp15", "Got Update Response for %s.\n", ticket_domains[ud->id][SSO_VALID_TICKET_DOMAIN]);
+
+	enckey = xmlnode_get_child(resp->xml, "Header/Security/DerivedKeyToken");
+	while (enckey) {
+		if (g_str_equal(xmlnode_get_attrib(enckey, "Id"), "EncKey"))
+			break;
+		enckey = xmlnode_get_next_twin(enckey);
+	}
+	if (!enckey) {
+		purple_debug_info("msnp15", "Invalid Response.\n");
 		return;
 	}
 
-	/*
-	 * in old MSN NS server's "USR TWN S" return,didn't include fs string
-	 * so we use a default "1" for fs.
-	 */
-	if(fs0){
-		fs = g_strdup(fs0);
-	}else{
-		fs = g_strdup("1");
+	tmp = xmlnode_get_data(xmlnode_get_child(enckey, "Nonce"));
+	nonce = (char *)purple_base64_decode(tmp, &len);
+	key = rps_create_key(nexus->secret, 24, nonce, len);
+	g_free(tmp);
+	g_free(nonce);
+
+#if 0
+	/* Don't know what this is for yet */
+	tmp = xmlnode_get_data(xmlnode_get_child(resp->xml,
+		"Header/EncryptedPP/EncryptedData/CipherData/CipherValue"));
+	if (tmp) {
+		decrypted_pp = des3_cbc(key, iv, tmp, len, TRUE);
+		g_free(tmp);
+		purple_debug_info("msnp15", "Got Response Header EncryptedPP: %s\n", decrypted_pp);
+		g_free(decrypted_pp);
+	}
+#endif
+
+	tmp = xmlnode_get_data(xmlnode_get_child(resp->xml,
+		"Body/EncryptedData/CipherData/CipherValue"));
+	if (tmp) {
+		char *unescaped;
+		xmlnode *rstresponse;
+
+		unescaped = (char *)purple_base64_decode(tmp, &len);
+		g_free(tmp);
+
+		decrypted_data = des3_cbc(key, iv, unescaped, len, TRUE);
+		g_free(unescaped);
+		purple_debug_info("msnp15", "Got Response Body EncryptedData: %s\n", decrypted_data);
+
+		rstresponse = xmlnode_from_str(decrypted_data, -1);
+		if (g_str_equal(rstresponse->name, "RequestSecurityTokenResponse"))
+			nexus_parse_token(nexus, ud->id, rstresponse);
+		else
+			nexus_parse_collection(nexus, ud->id, rstresponse);
+		g_free(decrypted_data);
 	}
 
-#ifdef NEXUS_LOGIN_TWN
-	challenge_str = g_strdup_printf(
-		"lc=%s&amp;id=%s&amp;tw=%s&amp;fs=%s&amp;ru=%s&amp;ct=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s&amp;tpf=%s\r\n",
-		lc,id,tw,fs,ru,ct,kpp,kv,ver,rn,tpf
-		);
+	if (ud->cb)
+		purple_timeout_add(0, ud->cb, ud->data);
 
-	/*build the SOAP windows Live ID XML body */
-	tail = g_strdup_printf(TWN_ENVELOP_TEMPLATE, username, password, challenge_str);
-	g_free(challenge_str);
-#else
-	rst1_str = g_strdup_printf(
-		"id=%s&amp;tw=%s&amp;fs=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s",
-		id,tw,fs,kpp,kv,ver,rn
-		);
-	rst2_str = g_strdup_printf(
-		"fs=%s&amp;id=%s&amp;kv=%s&amp;rn=%s&amp;tw=%s&amp;ver=%s",
-		fs,id,kv,rn,tw,ver
-		);
-	rst3_str = g_strdup_printf("id=%s",id);
-	tail = g_strdup_printf(TWN_LIVE_ENVELOP_TEMPLATE,username,password,rst1_str,rst2_str,rst3_str);
-	g_free(rst1_str);
-	g_free(rst2_str);
-	g_free(rst3_str);
-#endif
-	g_free(fs);
-	g_free(password);
-
-	soap = msn_soap_message_new(NULL, xmlnode_from_str(tail, -1));
-	g_free(tail);
-	msn_soap_message_send(nexus->session, soap, MSN_TWN_SERVER, TWN_POST_URL,
-		nexus_got_response_cb, nexus);
+	g_free(ud);
 }
 
+void
+msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data)
+{
+	MsnSession *session = nexus->session;
+	MsnNexusUpdateData *ud;
+	PurpleCipherContext *sha1;
+	PurpleCipherContext *hmac;
+
+	char *key;
+
+	guchar digest[20];
+
+	struct tm *tm;
+	time_t now;
+	char *now_str;
+	char *timestamp;
+	char *timestamp_b64;
+
+	char *domain;
+	char *domain_b64;
+
+	char *signedinfo;
+	gint32 nonce[6];
+	int i;
+	char *nonce_b64;
+	char *signature_b64;
+	guchar signature[20];
+
+	char *request;
+	MsnSoapMessage *soap;
+
+	purple_debug_info("msnp15",
+	                  "Updating ticket for user '%s' on domain '%s'\n",
+	                  purple_account_get_username(session->account),
+	                  ticket_domains[id][SSO_VALID_TICKET_DOMAIN]);
+
+	ud = g_new0(MsnNexusUpdateData, 1);
+	ud->nexus = nexus;
+	ud->id = id;
+	ud->cb = cb;
+	ud->data = data;
+
+	sha1 = purple_cipher_context_new_by_name("sha1", NULL);
+
+	domain = g_strdup_printf(MSN_SSO_RST_TEMPLATE,
+	                         id,
+	                         ticket_domains[id][SSO_VALID_TICKET_DOMAIN],
+	                         ticket_domains[id][SSO_VALID_TICKET_POLICY] != NULL ?
+	                             ticket_domains[id][SSO_VALID_TICKET_POLICY] :
+	                             nexus->policy);
+	purple_cipher_context_append(sha1, (guchar *)domain, strlen(domain));
+	purple_cipher_context_digest(sha1, 20, digest, NULL);
+	domain_b64 = purple_base64_encode(digest, 20);
+
+	now = time(NULL);
+	tm = gmtime(&now);
+	now_str = g_strdup(purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm));
+	now += 5*60;
+	tm = gmtime(&now);
+	timestamp = g_strdup_printf(MSN_SSO_TIMESTAMP_TEMPLATE,
+	                            now_str,
+	                            purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm));
+	purple_cipher_context_reset(sha1, NULL);
+	purple_cipher_context_append(sha1, (guchar *)timestamp, strlen(timestamp));
+	purple_cipher_context_digest(sha1, 20, digest, NULL);
+	timestamp_b64 = purple_base64_encode(digest, 20);
+	g_free(now_str);
+
+	purple_cipher_context_destroy(sha1);
+
+	signedinfo = g_strdup_printf(MSN_SSO_SIGNEDINFO_TEMPLATE,
+	                             id,
+	                             domain_b64,
+	                             timestamp_b64);
+
+	for (i = 0; i < 6; i++)
+		nonce[i] = rand();
+	nonce_b64 = purple_base64_encode((guchar *)&nonce, sizeof(nonce));
+
+	key = rps_create_key(nexus->secret, 24, (char *)nonce, sizeof(nonce));
+	hmac = purple_cipher_context_new_by_name("hmac", NULL);
+	purple_cipher_context_set_option(hmac, "hash", "sha1");
+	purple_cipher_context_set_key_with_len(hmac, (guchar *)key, 24);
+	purple_cipher_context_append(hmac, (guchar *)signedinfo, strlen(signedinfo));
+	purple_cipher_context_digest(hmac, 20, signature, NULL);
+	purple_cipher_context_destroy(hmac);
+	signature_b64 = purple_base64_encode(signature, 20);
+
+	request = g_strdup_printf(MSN_SSO_TOKEN_UPDATE_TEMPLATE,
+	                          nexus->cipher,
+	                          nonce_b64,
+	                          timestamp,
+	                          signedinfo,
+	                          signature_b64,
+	                          domain);
+
+	g_free(nonce_b64);
+	g_free(domain_b64);
+	g_free(timestamp_b64);
+	g_free(timestamp);
+	g_free(key);
+	g_free(signature_b64);
+	g_free(signedinfo);
+	g_free(domain);
+
+	soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1));
+	g_free(request);
+	msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL,
+	                      nexus_got_update_cb, ud);
+}
+
+GHashTable *
+msn_nexus_get_token(MsnNexus *nexus, MsnAuthDomains id)
+{
+	g_return_val_if_fail(nexus != NULL, NULL);
+	g_return_val_if_fail(id < nexus->token_len, NULL);
+
+	return nexus->tokens[id].token;
+}
+
+const char *
+msn_nexus_get_token_str(MsnNexus *nexus, MsnAuthDomains id)
+{
+	static char buf[1024];
+	GHashTable *token = msn_nexus_get_token(nexus, id);
+	const char *msn_t;
+	const char *msn_p;
+	gint ret;
+
+	g_return_val_if_fail(token != NULL, NULL);
+
+	msn_t = g_hash_table_lookup(token, "t");
+	msn_p = g_hash_table_lookup(token, "p");
+
+	g_return_val_if_fail(msn_t != NULL, NULL);
+	g_return_val_if_fail(msn_p != NULL, NULL);
+
+	ret = g_snprintf(buf, sizeof(buf) - 1, "t=%s&p=%s", msn_t, msn_p);
+	g_return_val_if_fail(ret != -1, NULL);
+
+	return buf;
+}
+
--- a/libpurple/protocols/msn/nexus.h	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/nexus.h	Sat Jun 21 07:36:16 2008 +0000
@@ -26,125 +26,210 @@
 
 #include "soap.h"
 
-/*#define MSN_TWN_SERVER	"loginnet.passport.com"*/
-#define MSN_TWN_SERVER	"login.live.com"
+/* Index into ticket_tokens in nexus.c Keep updated! */
+typedef enum
+{
+	MSN_AUTH_MESSENGER     = 0,
+	MSN_AUTH_MESSENGER_WEB = 1,
+	MSN_AUTH_CONTACTS      = 2,
+	MSN_AUTH_LIVE_SECURE   = 3,
+	MSN_AUTH_SPACES        = 4,
+	MSN_AUTH_LIVE_CONTACTS = 5,
+	MSN_AUTH_STORAGE       = 6
+} MsnAuthDomains;
 
-#define TWN_START_TOKEN		"<wsse:BinarySecurityToken Id=\"PPToken1\">"
-#define TWN_END_TOKEN		"</wsse:BinarySecurityToken>"
+#define MSN_SSO_SERVER	"login.live.com"
+#define SSO_POST_URL	"/RST.srf"
+
+#define MSN_SSO_RST_TEMPLATE \
+"<wst:RequestSecurityToken xmlns=\"http://schemas.xmlsoap.org/ws/2004/04/trust\" Id=\"RST%d\">"\
+	"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+	"<wsp:AppliesTo xmlns=\"http://schemas.xmlsoap.org/ws/2002/12/policy\">"\
+		"<wsa:EndpointReference xmlns=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\">"\
+			"<wsa:Address>%s</wsa:Address>"\
+		"</wsa:EndpointReference>"\
+	"</wsp:AppliesTo>"\
+	"<wsse:PolicyReference xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"%s\"></wsse:PolicyReference>"\
+"</wst:RequestSecurityToken>"
 
-#define TWN_POST_URL			"/RST.srf"
-#define TWN_ENVELOP_TEMPLATE 	"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"\
-						"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\
-						"<Header>"\
-						"<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\
-						"<ps:HostingApp>{3:B}</ps:HostingApp>"\
-						"<ps:BinaryVersion>4</ps:BinaryVersion>"\
-						"<ps:UIVersion>1</ps:UIVersion>"\
-						"<ps:Cookies></ps:Cookies>"\
-						"<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>"\
-						"</ps:AuthInfo>"\
-						"<wsse:Security>"\
-						"<wsse:UsernameToken Id=\"user\">"\
-						"<wsse:Username>%s</wsse:Username>"\
-						"<wsse:Password>%s</wsse:Password>"\
-						"</wsse:UsernameToken>"\
-						"</wsse:Security>"\
-						"</Header>"\
-						"<Body>"\
-						"<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\
-						"<wst:RequestSecurityToken Id=\"RST0\">"\
-						"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-						"<wsp:AppliesTo>"\
-						"<wsa:EndpointReference>"\
+#define MSN_SSO_TEMPLATE "<?xml version='1.0' encoding='utf-8'?>"\
+"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\""\
+	" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\""\
+	" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\""\
+	" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""\
+	" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\""\
+	" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\""\
+	" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\
+	"<Header>"\
+		"<ps:AuthInfo"\
+			" xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\""\
+			" Id=\"PPAuthInfo\">"\
+			"<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>"\
+			"<ps:BinaryVersion>4</ps:BinaryVersion>"\
+			"<ps:UIVersion>1</ps:UIVersion>"\
+			"<ps:Cookies></ps:Cookies>"\
+			"<ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>"\
+		"</ps:AuthInfo>"\
+		"<wsse:Security>"\
+			"<wsse:UsernameToken Id=\"user\">"\
+				"<wsse:Username>%s</wsse:Username>"\
+				"<wsse:Password>%s</wsse:Password>"\
+			"</wsse:UsernameToken>"\
+		"</wsse:Security>"\
+	"</Header>"\
+	"<Body>"\
+		"<ps:RequestMultipleSecurityTokens"\
+			" xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\""\
+			" Id=\"RSTS\">"\
+			"<wst:RequestSecurityToken Id=\"RST0\">"\
+				"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+				"<wsp:AppliesTo>"\
+					"<wsa:EndpointReference>"\
 						"<wsa:Address>http://Passport.NET/tb</wsa:Address>"\
-						"</wsa:EndpointReference>"\
-						"</wsp:AppliesTo>"\
-						"</wst:RequestSecurityToken>"\
-						"<wst:RequestSecurityToken Id=\"RST1\">"\
-						"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-						"<wsp:AppliesTo>"\
-						"<wsa:EndpointReference>"\
-						"<wsa:Address>messenger.msn.com</wsa:Address>"\
-						"</wsa:EndpointReference>"\
-						"</wsp:AppliesTo>"\
-						"<wsse:PolicyReference URI=\"?%s\">"\
-						"</wsse:PolicyReference>"\
-						"</wst:RequestSecurityToken>"\
-						"</ps:RequestMultipleSecurityTokens>"\
-						"</Body>"\
-						"</Envelope>"
+					"</wsa:EndpointReference>"\
+				"</wsp:AppliesTo>"\
+			"</wst:RequestSecurityToken>"\
+			"%s"	/* Other RSTn tokens */\
+		"</ps:RequestMultipleSecurityTokens>"\
+	"</Body>"\
+"</Envelope>"
+
+#define MSN_SSO_AUTHINFO_TEMPLATE \
+"<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\
+	"<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>"\
+	"<ps:BinaryVersion>4</ps:BinaryVersion>"\
+	"<ps:UIVersion>1</ps:UIVersion>"\
+	"<ps:Cookies></ps:Cookies>"\
+	"<ps:RequestParams>AQAAAAIAAABsYwQAAAA0MTA1</ps:RequestParams>"\
+"</ps:AuthInfo>"
+/* Not sure what's editable here, so I'll just hard-code the SHA1 hash */
+#define MSN_SSO_AUTHINFO_SHA1_BASE64 "d2IeTF4DAkPEa/tVETHznsivEpc="
+
+#define MSN_SSO_TIMESTAMP_TEMPLATE \
+"<wsu:Timestamp xmlns=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" Id=\"Timestamp\">"\
+	"<wsu:Created>%s</wsu:Created>"\
+	"<wsu:Expires>%s</wsu:Expires>"\
+"</wsu:Timestamp>"
 
-#define TWN_LIVE_START_TOKEN	"<wsse:BinarySecurityToken Id=\"PPToken1\">"
-#define TWN_LIVE_END_TOKEN	"</wsse:BinarySecurityToken>"
-#define TWN_LIVE_ENVELOP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"\
-"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\
-  "<Header>"\
-    "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\
-      "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>"\
-      "<ps:BinaryVersion>4</ps:BinaryVersion>"\
-      "<ps:UIVersion>1</ps:UIVersion>"\
-      "<ps:Cookies></ps:Cookies>"\
-      "<ps:RequestParams>AQAAAAIAAABsYwQAAAAyMDUy</ps:RequestParams>"\
-    "</ps:AuthInfo>"\
-    "<wsse:Security>"\
-      "<wsse:UsernameToken Id=\"user\">"\
-        "<wsse:Username>%s</wsse:Username>"\
-        "<wsse:Password>%s</wsse:Password>"\
-      "</wsse:UsernameToken>"\
-    "</wsse:Security>"\
-  "</Header>"\
-  "<Body>"\
-    "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\
-      "<wst:RequestSecurityToken Id=\"RST0\">"\
-        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-        "<wsp:AppliesTo>"\
-          "<wsa:EndpointReference>"\
-            "<wsa:Address>http://Passport.NET/tb</wsa:Address>"\
-          "</wsa:EndpointReference>"\
-        "</wsp:AppliesTo>"\
-      "</wst:RequestSecurityToken>"\
-      "<wst:RequestSecurityToken Id=\"RST1\">"\
-        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-        "<wsp:AppliesTo>"\
-          "<wsa:EndpointReference>"\
-            "<wsa:Address>messenger.msn.com</wsa:Address>"\
-          "</wsa:EndpointReference>"\
-        "</wsp:AppliesTo>"\
-        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
-      "</wst:RequestSecurityToken>"\
-      "<wst:RequestSecurityToken Id=\"RST2\">"\
-        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-        "<wsp:AppliesTo>"\
-          "<wsa:EndpointReference>"\
-            "<wsa:Address>contacts.msn.com</wsa:Address>"\
-         "</wsa:EndpointReference>"\
-        "</wsp:AppliesTo>"\
-       "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
-     " </wst:RequestSecurityToken>"\
-      "<wst:RequestSecurityToken Id=\"RST3\">"\
-        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
-        "<wsp:AppliesTo>"\
-          "<wsa:EndpointReference>"\
-            "<wsa:Address>voice.messenger.msn.com</wsa:Address>"\
-          "</wsa:EndpointReference>"\
-       " </wsp:AppliesTo>"\
-        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
-      "</wst:RequestSecurityToken>"\
-    "</ps:RequestMultipleSecurityTokens>"\
-  "</Body>"\
+#define MSN_SSO_SIGNEDINFO_TEMPLATE \
+"<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"\
+	"<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></CanonicalizationMethod>"\
+	"<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\"></SignatureMethod>"\
+	"<Reference URI=\"#RST%d\">"\
+		"<Transforms>"\
+			"<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform>"\
+		"</Transforms>"\
+		"<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>"\
+		"<DigestValue>%s</DigestValue>"\
+	"</Reference>"\
+	"<Reference URI=\"#Timestamp\">"\
+		"<Transforms>"\
+			"<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform>"\
+		"</Transforms>"\
+		"<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>"\
+		"<DigestValue>%s</DigestValue>"\
+	"</Reference>"\
+	"<Reference URI=\"#PPAuthInfo\">"\
+		"<Transforms>"\
+			"<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform>"\
+		"</Transforms>"\
+		"<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>"\
+		"<DigestValue>" MSN_SSO_AUTHINFO_SHA1_BASE64 "</DigestValue>"\
+	"</Reference>"\
+"</SignedInfo>"
+
+#define MSN_SSO_TOKEN_UPDATE_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<Envelope"\
+	" xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\""\
+	" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\""\
+	" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\""\
+	" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\""\
+	" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""\
+	" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\""\
+	" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\""\
+	" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\
+	"<Header>"\
+		MSN_SSO_AUTHINFO_TEMPLATE /* ps:AuthInfo */ \
+		"<wsse:Security>"\
+			"<EncryptedData xmlns=\"http://www.w3.org/2001/04/xmlenc#\" Id=\"BinaryDAToken0\" Type=\"http://www.w3.org/2001/04/xmlenc#Element\">"\
+				"<EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#tripledes-cbc\"></EncryptionMethod>"\
+				"<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">"\
+					"<ds:KeyName>http://Passport.NET/STS</ds:KeyName>"\
+				"</ds:KeyInfo>"\
+				"<CipherData>"\
+					"<CipherValue>%s</CipherValue>"\
+				"</CipherData>"\
+			"</EncryptedData>"\
+			"<wssc:DerivedKeyToken Id=\"SignKey\">"\
+				"<wsse:RequestedTokenReference>"\
+					"<wsse:KeyIdentifier ValueType=\"http://docs.oasis-open.org/wss/2004/XX/oasis-2004XX-wss-saml-token-profile-1.0#SAMLAssertionID\" />"\
+					"<wsse:Reference URI=\"#BinaryDAToken0\" />"\
+				"</wsse:RequestedTokenReference>"\
+				"<wssc:Nonce>%s</wssc:Nonce>"\
+			"</wssc:DerivedKeyToken>"\
+			"%s" /* wsu:Timestamp */\
+			"<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"\
+				"%s" /* SignedInfo */\
+				"<SignatureValue>%s</SignatureValue>"\
+				"<KeyInfo>"\
+					"<wsse:SecurityTokenReference>"\
+						"<wsse:Reference URI=\"#SignKey\" />"\
+					"</wsse:SecurityTokenReference>"\
+				"</KeyInfo>"\
+			"</Signature>"\
+		"</wsse:Security>"\
+	"</Header>"\
+	"<Body>"\
+		"%s" /* wst:RequestSecurityToken */ \
+	"</Body>"\
 "</Envelope>"
 
+typedef struct _MsnUsrKey MsnUsrKey;
+struct _MsnUsrKey
+{
+	int size; // 28. Does not count data
+	int crypt_mode; // CRYPT_MODE_CBC (1)
+	int cipher_type; // TripleDES (0x6603)
+	int hash_type; // SHA1 (0x8004)
+	int iv_len;    // 8
+	int hash_len;  // 20
+	int cipher_len; // 72
+	// Data
+	char iv[8];
+	char hash[20];
+	char cipher[72];
+};
+
+typedef struct _MsnTicketToken MsnTicketToken;
+struct _MsnTicketToken {
+	GHashTable *token;
+	char *secret;
+	time_t expiry;
+};
+
 typedef struct _MsnNexus MsnNexus;
 
 struct _MsnNexus
 {
 	MsnSession *session;
-	char * challenge_data_str;
-	GHashTable *challenge_data;
+
+	/* From server via USR command */
+	char *policy;
+	char *nonce;
+
+	/* From server via SOAP stuff */
+	char *cipher;
+	char *secret;
+	MsnTicketToken *tokens;
+	int token_len;
 };
 
 void msn_nexus_connect(MsnNexus *nexus);
 MsnNexus *msn_nexus_new(MsnSession *session);
 void msn_nexus_destroy(MsnNexus *nexus);
+GHashTable *msn_nexus_get_token(MsnNexus *nexus, MsnAuthDomains id);
+const char *msn_nexus_get_token_str(MsnNexus *nexus, MsnAuthDomains id);
+void msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data);
+#endif /* _MSN_NEXUS_H_ */
 
-#endif /* _MSN_NEXUS_H_ */
--- a/libpurple/protocols/msn/notification.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/notification.c	Sat Jun 21 07:36:16 2008 +0000
@@ -23,6 +23,7 @@
  */
 #include "msn.h"
 #include "notification.h"
+#include "contact.h"
 #include "state.h"
 #include "error.h"
 #include "msnutils.h"
@@ -104,7 +105,6 @@
 
 	vers = g_string_new("");
 
-/*	for (i = session->protocol_ver; i >= WLM_MIN_PROTOCOL; i--) */
 	for (i = WLM_MAX_PROTOCOL; i >= WLM_MIN_PROTOCOL; i--)
 		g_string_append_printf(vers, " MSNP%d", i);
 
@@ -195,7 +195,7 @@
  **************************************************************************/
 
 void
-msn_got_login_params(MsnSession *session, const char *login_params)
+msn_got_login_params(MsnSession *session, const char *ticket, const char *response)
 {
 	MsnCmdProc *cmdproc;
 
@@ -203,7 +203,7 @@
 
 	msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END);
 
-	msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params);
+	msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s", ticket, response);
 }
 
 static void
@@ -212,8 +212,8 @@
 	PurpleAccount *account;
 
 	account = cmdproc->session->account;
-	msn_cmdproc_send(cmdproc, "USR", "TWN I %s",
-					 purple_account_get_username(account));
+
+	msn_cmdproc_send(cmdproc, "USR", "SSO I %s", purple_account_get_username(account));
 }
 
 static void
@@ -239,32 +239,14 @@
 //		msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
 		//TODO we should use SOAP contact to fetch contact list
 	}
-	else if (!g_ascii_strcasecmp(cmd->params[1], "TWN"))
+	else if (!g_ascii_strcasecmp(cmd->params[1], "SSO"))
 	{
-		/* Passport authentication */
-		char **elems, **cur, **tokens;
+		/* RPS authentication */
 
 		session->nexus = msn_nexus_new(session);
 
-		/* Parse the challenge data. */
-		session->nexus->challenge_data_str = g_strdup(cmd->params[3]);
-		elems = g_strsplit(cmd->params[3], ",", 0);
-
-		for (cur = elems; *cur != NULL; cur++)
-		{
-			tokens = g_strsplit(*cur, "=", 2);
-			if(tokens[0] && tokens[1])
-			{
-				purple_debug_info("MSNP14","challenge %p,key:%s,value:%s\n",
-									session->nexus->challenge_data,tokens[0],tokens[1]);
-				g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]);
-				/* Don't free each of the tokens, only the array. */
-				g_free(tokens);
-			} else
-				g_strfreev(tokens);
-		}
-
-		g_strfreev(elems);
+		session->nexus->policy = g_strdup(cmd->params[3]);
+		session->nexus->nonce = g_strdup(cmd->params[4]);
 
 		msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_START);
 
@@ -327,14 +309,15 @@
 	}
 
 	/*
-	 * Windows Live Messenger 8.0
+	 * 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",
 //					 "0x0409 winnt 5.1 i386 MSG80BETA 8.0.0689 msmsgs %s",
-					"0x0804 winnt 5.1 i386 MSNMSGR 8.0.0792 msmsgs %s",
+//					"0x0804 winnt 5.1 i386 MSNMSGR 8.0.0792 msmsgs %s",
+					"0x0409 winnt 5.1 i386 MSNMSGR 8.5.1288 msmsgs %s",
 					 purple_account_get_username(account));
 }
 
@@ -545,7 +528,7 @@
 	context = purple_cipher_context_new(cipher, NULL);
 	purple_cipher_context_append(context, (const guchar *)cmd->params[1],
 							   strlen(cmd->params[1]));
-	challenge_resp = MSNP13_WLM_PRODUCT_KEY;
+	challenge_resp = MSNP15_WLM_PRODUCT_KEY;
 
 	purple_cipher_context_append(context, (const guchar *)challenge_resp,
 							   strlen(challenge_resp));
@@ -560,7 +543,7 @@
 	msn_handle_chl(cmd->params[1], buf);
 #endif
 //	purple_debug_info("MSNP14","<<challenge:{%s}:{%s}\n",cmd->params[1],buf);
-	trans = msn_transaction_new(cmdproc, "QRY", "%s 32", MSNP13_WLM_PRODUCT_ID);
+	trans = msn_transaction_new(cmdproc, "QRY", "%s 32", MSNP15_WLM_PRODUCT_ID);
 
 	msn_transaction_set_payload(trans, buf, 32);
 
@@ -572,7 +555,7 @@
  **************************************************************************/
 /* add contact to xmlnode */
 static void
-msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnUserType type)
+msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnNetwork networkId)
 {
 	xmlnode *d_node,*c_node;
 	char **tokens;
@@ -581,7 +564,7 @@
 
 	g_return_if_fail(passport != NULL);
 
-	purple_debug_info("MSNP14","Passport: %s, type: %d\n", passport, type);
+	purple_debug_info("MSNP14","Passport: %s, type: %d\n", passport, networkId);
 	tokens = g_strsplit(passport, "@", 2);
 	email = tokens[0];
 	domain = tokens[1];
@@ -619,12 +602,12 @@
 	g_snprintf(fmt_str, sizeof(fmt_str), "%d", list_op);
 	xmlnode_set_attrib(c_node, "l", fmt_str);
 
-	if (type != MSN_USER_TYPE_UNKNOWN)
-		g_snprintf(fmt_str, sizeof(fmt_str), "%d", type);
+	if (networkId != MSN_NETWORK_UNKNOWN)
+		g_snprintf(fmt_str, sizeof(fmt_str), "%d", networkId);
 	else if (msn_user_is_yahoo(session->account, passport))
-		g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_USER_TYPE_YAHOO);
+		g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_NETWORK_YAHOO);
 	else
-		g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_USER_TYPE_PASSPORT);
+		g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_NETWORK_PASSPORT);
 
 	/*mobile*/
 	//type_str = g_strdup_printf("4");
@@ -670,7 +653,7 @@
 			continue;
 
 		msn_add_contact_xml(session, adl_node, user->passport,
-			user->list_op & MSN_LIST_OP_MASK, user->type);
+			user->list_op & MSN_LIST_OP_MASK, user->networkid);
 
 		/* each ADL command may contain up to 150 contacts */
 		if (++adl_count % 150 == 0 || l->next == NULL) {
@@ -782,7 +765,7 @@
 			if (list_op & MSN_LIST_RL_OP) {
 				/* someone is adding us */
 //				got_new_entry(cmdproc->session->account->gc, passport, decoded_friendly_name);
-				msn_get_contact_list(cmdproc->session->contact, MSN_PS_PENDING_LIST, NULL);
+				msn_get_contact_list(cmdproc->session, MSN_PS_PENDING_LIST, NULL);
 			}
 
 //			g_free(decoded_friendly_name);
@@ -839,9 +822,8 @@
 {
 	purple_debug_info("MSN Notification","FQY payload:\n%s\n", payload);
 	g_return_if_fail(cmdproc->session != NULL);
-	g_return_if_fail(cmdproc->session->contact != NULL);
 //	msn_notification_post_adl(cmdproc, payload, len);
-//	msn_get_address_book(cmdproc->session->contact, MSN_AB_SAVE_CONTACT, NULL, NULL);
+//	msn_get_address_book(cmdproc->session, MSN_AB_SAVE_CONTACT, NULL, NULL);
 }
 
 static void
@@ -1036,7 +1018,7 @@
 	MsnUser *user;
 	MsnObject *msnobj;
 	unsigned long clientid;
-	int wlmclient;
+	int networkid;
 	const char *state, *passport, *friendly;
 
 	session = cmdproc->session;
@@ -1046,7 +1028,7 @@
 	state    = cmd->params[1];
 	passport = cmd->params[2];
 	/*if a contact is actually on the WLM part or the yahoo part*/
-	wlmclient = atoi(cmd->params[3]);
+	networkid = atoi(cmd->params[3]);
 	friendly = purple_url_decode(cmd->params[4]);
 
 	user = msn_userlist_find_user(session->userlist, passport);
@@ -1055,7 +1037,7 @@
 
 	msn_user_set_friendly_name(user, friendly);
 
-	if (session->protocol_ver >= 9 && cmd->param_count == 8)
+	if (cmd->param_count == 8)
 	{
 		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6]));
 		msn_user_set_object(user, msnobj);
@@ -1063,6 +1045,8 @@
 
 	clientid = strtoul(cmd->params[5], NULL, 10);
 	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+');
+	msn_user_set_clientid(user, clientid);
+	msn_user_set_network(user, networkid);
 
 	msn_user_set_state(user, state);
 	msn_user_update(user);
@@ -1144,7 +1128,7 @@
 	MsnUser *user;
 	MsnObject *msnobj;
 	unsigned long clientid;
-	int wlmclient;
+	int networkid;
 	const char *state, *passport, *friendly, *old_friendly;
 
 	session = cmdproc->session;
@@ -1153,7 +1137,7 @@
 
 	state    = cmd->params[0];
 	passport = cmd->params[1];
-	wlmclient = atoi(cmd->params[2]);
+	networkid = atoi(cmd->params[2]);
 	friendly = purple_url_decode(cmd->params[3]);
 
 	user = msn_userlist_find_user(session->userlist, passport);
@@ -1165,22 +1149,22 @@
 		msn_user_set_friendly_name(user, friendly);
 	}
 
-	if (session->protocol_ver >= 9)
+	if (cmd->param_count == 7)
 	{
-		if (cmd->param_count == 7)
-		{
-			msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
-			msn_user_set_object(user, msnobj);
-		}
-		else
-		{
-			msn_user_set_object(user, NULL);
-		}
+		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
+		msn_user_set_object(user, msnobj);
+	}
+	else
+	{
+		msn_user_set_object(user, NULL);
 	}
 
 	clientid = strtoul(cmd->params[4], NULL, 10);
 	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+');
 
+	msn_user_set_clientid(user, clientid);
+	msn_user_set_network(user, networkid);
+
 	msn_user_set_state(user, state);
 	msn_user_update(user);
 }
@@ -1292,7 +1276,7 @@
 			if (!strcmp(type, "MFN")) {
 				friendlyname = purple_url_decode(cmd->params[2]);
 
-				msn_update_contact(session->contact, friendlyname);
+				msn_update_contact(session, "Me", MSN_UPDATE_DISPLAY, friendlyname);
 
 				purple_connection_set_display_name(
 					purple_account_get_connection(session->account),
@@ -1629,9 +1613,8 @@
 gcf_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
 			 size_t len)
 {
-	xmlnode * root;
-	gchar * buf;
-	int xmllen;
+	xmlnode *root;
+	xmlnode *policy;
 
 	g_return_if_fail(cmd->payload != NULL);
 
@@ -1641,12 +1624,32 @@
 		return;
 	}
 
-	buf = xmlnode_to_formatted_str(root, &xmllen);
+
+	g_free(cmdproc->session->blocked_text);
+	cmdproc->session->blocked_text = NULL;
+
+	/* We need a get_child with attrib... */
+	policy = xmlnode_get_child(root, "Policy");
+	while (policy) {
+		if (g_str_equal(xmlnode_get_attrib(policy, "type"), "SHIELDS"))
+			break;
+		policy = xmlnode_get_next_twin(policy);
+	}
 
-	/* get the payload content */
-	purple_debug_info("MSNP14","GCF command payload:\n%.*s\n", xmllen, buf);
+	if (policy) {
+		GString *blocked = g_string_new(NULL);
+		xmlnode *imtext = xmlnode_get_child(policy,
+		                                    "config/block/regexp/imtext");
+		while (imtext) {
+			const char *value = xmlnode_get_attrib(imtext, "value");
+			g_string_append_printf(blocked, "%s<br/>\n",
+			                       purple_base64_decode(value, NULL));
+			imtext = xmlnode_get_next_twin(imtext);
+		}
 
-	g_free(buf);
+		cmdproc->session->blocked_text = g_string_free(blocked, FALSE);
+	}
+
 	xmlnode_free(root);
 }
 
@@ -1774,17 +1777,16 @@
 
 	/*starting retrieve the contact list*/
 	clLastChange = purple_account_get_string(session->account, "CLLastChange", NULL);
-	session->contact = msn_contact_new(session);
 #ifdef MSN_PARTIAL_LISTS
 	/* msn_userlist_load defeats all attempts at trying to detect blist sync issues */
 	msn_userlist_load(session);
-	msn_get_contact_list(session->contact, MSN_PS_INITIAL, clLastChange);
+	msn_get_contact_list(session, MSN_PS_INITIAL, clLastChange);
 #else
 	/* always get the full list? */
-	msn_get_contact_list(session->contact, MSN_PS_INITIAL, NULL);
+	msn_get_contact_list(session, MSN_PS_INITIAL, NULL);
 #endif
 #if 0
-	msn_contact_connect(session->contact);
+	msn_contact_connect(session);
 #endif
 }
 
@@ -2037,7 +2039,7 @@
 	adl_node->child = NULL;
 
 	msn_add_contact_xml(notification->session, adl_node, who, list_op,
-						MSN_USER_TYPE_PASSPORT);
+						MSN_NETWORK_PASSPORT);
 
 	payload = xmlnode_to_str(adl_node,&payload_len);
 	xmlnode_free(adl_node);
@@ -2063,7 +2065,7 @@
 	rml_node = xmlnode_new("ml");
 	rml_node->child = NULL;
 
-	msn_add_contact_xml(notification->session, rml_node, who, list_op, MSN_USER_TYPE_PASSPORT);
+	msn_add_contact_xml(notification->session, rml_node, who, list_op, MSN_NETWORK_PASSPORT);
 
 	payload = xmlnode_to_str(rml_node, &payload_len);
 	xmlnode_free(rml_node);
--- a/libpurple/protocols/msn/notification.h	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/notification.h	Sat Jun 21 07:36:16 2008 +0000
@@ -25,6 +25,11 @@
 #define _MSN_NOTIFICATION_H_
 
 /*MSN protocol challenge info*/
+
+/*MSNP15 challenge: WLM 8.5.1288.816*/
+#define MSNP15_WLM_PRODUCT_KEY "ILTXC!4IXB5FB*PX"
+#define MSNP15_WLM_PRODUCT_ID "PROD0119GSJUC$18"
+
 /*MSNP13 challenge*/
 #define MSNP13_WLM_PRODUCT_KEY	"O4BG@C7BWLYQX?5G"
 #define MSNP13_WLM_PRODUCT_ID	"PROD01065C%ZFN6F"
@@ -81,6 +86,6 @@
  */
 void msn_notification_close(MsnNotification *notification);
 
-void msn_got_login_params(MsnSession *session, const char *login_params);
+void msn_got_login_params(MsnSession *session, const char *ticket, const char *response);
 
 #endif /* _MSN_NOTIFICATION_H_ */
--- a/libpurple/protocols/msn/oim.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/oim.c	Sat Jun 21 07:36:16 2008 +0000
@@ -41,6 +41,7 @@
 } MsnOimRecvData;
 
 /*Local Function Prototype*/
+static void msn_parse_oim_xml(MsnOim *oim, xmlnode *node);
 static void msn_oim_post_single_get_msg(MsnOim *oim, char *msgid);
 static MsnOimSendReq *msn_oim_new_send_req(const char *from_member,
 										   const char *friendname,
@@ -114,6 +115,155 @@
 }
 
 /****************************************
+ * Manage OIM Tokens
+ ****************************************/
+typedef struct _MsnOimRequestData {
+	MsnOim *oim;
+	gboolean send;
+	const char *action;
+	const char *host;
+	const char *url;
+	xmlnode *body;
+	MsnSoapCallback cb;
+	gpointer cb_data;
+} MsnOimRequestData;
+
+static void msn_oim_request_helper(MsnOimRequestData *data);
+
+static void
+msn_oim_request_cb(MsnSoapMessage *request, MsnSoapMessage *response,
+	gpointer req_data)
+{
+	MsnOimRequestData *data = (MsnOimRequestData *)req_data;
+	xmlnode *fault = NULL;
+	xmlnode *faultcode = NULL;
+
+	fault = xmlnode_get_child(response->xml, "Body/Fault");
+	if (fault)
+		faultcode = xmlnode_get_child(fault, "faultcode");
+
+	if (faultcode) {
+		gchar *faultcode_str = xmlnode_get_data(faultcode);
+
+		if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) {
+			purple_debug_error("msnp15", "OIM Request Error, Updating token now.");
+			msn_nexus_update_token(data->oim->session->nexus,
+				data->send ? MSN_AUTH_LIVE_SECURE : MSN_AUTH_MESSENGER_WEB,
+				(GSourceFunc)msn_oim_request_helper, data);
+			g_free(faultcode_str);
+			return;
+
+		} else if (faultcode_str && g_str_equal(faultcode_str, "q0:AuthenticationFailed")) {
+			if (xmlnode_get_child(fault, "detail/RequiredAuthPolicy") != NULL) {
+				purple_debug_error("msnp15", "OIM Request Error, Updating token now.");
+				msn_nexus_update_token(data->oim->session->nexus,
+					data->send ? MSN_AUTH_LIVE_SECURE : MSN_AUTH_MESSENGER_WEB,
+					(GSourceFunc)msn_oim_request_helper, data);
+				g_free(faultcode_str);
+				return;
+			}
+		}
+		g_free(faultcode_str);
+	}
+
+	if (data->cb)
+		data->cb(request, response, data->cb_data);
+	xmlnode_free(data->body);
+	g_free(data);
+}
+
+static void
+msn_oim_request_helper(MsnOimRequestData *data)
+{
+	MsnSession *session = data->oim->session;
+
+	if (data->send) {
+		/* The Sending of OIM's uses a different token for some reason. */
+		xmlnode *ticket;
+		ticket = xmlnode_get_child(data->body, "Header/Ticket");
+		xmlnode_set_attrib(ticket, "passport",
+			msn_nexus_get_token_str(session->nexus, MSN_AUTH_LIVE_SECURE));
+	}
+	else
+	{
+		xmlnode *passport;
+		xmlnode *xml_t;
+		xmlnode *xml_p;
+		GHashTable *token;
+		const char *msn_t;
+		const char *msn_p;
+
+		token = msn_nexus_get_token(session->nexus, MSN_AUTH_MESSENGER_WEB);
+		g_return_if_fail(token != NULL);
+
+		msn_t = g_hash_table_lookup(token, "t");
+		msn_p = g_hash_table_lookup(token, "p");
+
+		g_return_if_fail(msn_t != NULL);
+		g_return_if_fail(msn_p != NULL);
+
+		passport = xmlnode_get_child(data->body, "Header/PassportCookie");
+		xml_t = xmlnode_get_child(passport, "t");
+		xml_p = xmlnode_get_child(passport, "p");
+
+		/* frees old token text, or the 'EMPTY' text if first time */
+		xmlnode_free(xml_t->child);
+		xmlnode_free(xml_p->child);
+
+		xmlnode_insert_data(xml_t, msn_t, -1);
+		xmlnode_insert_data(xml_p, msn_p, -1);
+	}
+
+	msn_soap_message_send(session,
+		msn_soap_message_new(data->action, xmlnode_copy(data->body)),
+		data->host, data->url, msn_oim_request_cb, data);
+}
+
+
+static void
+msn_oim_make_request(MsnOim *oim, gboolean send, const char *action,
+	const char *host, const char *url, xmlnode *body, MsnSoapCallback cb,
+	gpointer cb_data)
+{
+	MsnOimRequestData *data = g_new0(MsnOimRequestData, 1);
+	data->oim = oim;
+	data->send = send;
+	data->action = action;
+	data->host = host;
+	data->url = url;
+	data->body = body;
+	data->cb = cb;
+	data->cb_data = cb_data;
+
+	msn_oim_request_helper(data);
+}
+
+/****************************************
+ * OIM GetMetadata request
+ * **************************************/
+static void
+msn_oim_get_metadata_cb(MsnSoapMessage *request, MsnSoapMessage *response,
+	gpointer data)
+{
+	MsnOim *oim = data;
+
+	if (response) {
+		msn_parse_oim_xml(oim,
+			xmlnode_get_child(response->xml, "Body/GetMetadataResponse/MD"));
+	}
+}
+
+/* Post to get the OIM Metadata */
+static void
+msn_oim_get_metadata(MsnOim *oim)
+{
+	msn_oim_make_request(oim, FALSE, MSN_OIM_GET_METADATA_ACTION,
+		MSN_OIM_RETRIEVE_HOST, MSN_OIM_RETRIEVE_URL,
+		xmlnode_from_str(MSN_OIM_GET_METADATA_TEMPLATE, -1),
+		msn_oim_get_metadata_cb, oim);
+}
+
+/****************************************
  * OIM send SOAP request
  * **************************************/
 /*encode the message to OIM Message Format*/
@@ -148,7 +298,7 @@
 	if (response == NULL) {
 		purple_debug_info("MSNP14", "cannot send OIM: %s\n", msg->oim_msg);
 	} else {
-		xmlnode	*faultNode = msn_soap_xml_get(response->xml, "Body/Fault");
+		xmlnode	*faultNode = xmlnode_get_child(response->xml, "Body/Fault");
 
 		if (faultNode == NULL) {
 			/*Send OK! return*/
@@ -160,7 +310,7 @@
 				char *faultcode_str = xmlnode_get_data(faultcode);
 
 				if (g_str_equal(faultcode_str, "q0:AuthenticationFailed")) {
-					xmlnode *challengeNode = msn_soap_xml_get(faultNode,
+					xmlnode *challengeNode = xmlnode_get_child(faultNode,
 						"detail/LockKeyChallenge");
 
 					if (challengeNode == NULL) {
@@ -193,6 +343,32 @@
 						g_queue_push_head(oim->send_queue, msg);
 						msn_oim_send_msg(oim);
 					}
+				} else {
+					/* Report the error */
+					const char *str_reason;
+
+					if (g_str_equal(faultcode_str, "q0:SystemUnavailable")) {
+						str_reason = _("Message was not sent because the system is "
+						               "unavailable. This normally happens when the "
+						               "user is blocked or does not exist.");
+
+					} else if (g_str_equal(faultcode_str, "q0:SenderThrottleLimitExceeded")) {
+						str_reason = _("Message was not sent because messages "
+						               "are being sent too quickly.");
+
+					} else if (g_str_equal(faultcode_str, "q0:InvalidContent")) {
+						str_reason = _("Message was not sent because an unknown "
+						               "encoding error occured.");
+
+					} else {
+						str_reason = _("Message was not sent because an unknown "
+						               "error occured.");
+					}
+					
+					msn_session_report_user(oim->session, msg->to_member, 
+						str_reason, PURPLE_MESSAGE_ERROR);
+					msn_session_report_user(oim->session, msg->to_member,
+						msg->oim_msg, PURPLE_MESSAGE_RAW);
 				}
 
 				g_free(faultcode_str);
@@ -217,7 +393,7 @@
 msn_oim_send_msg(MsnOim *oim)
 {
 	MsnOimSendReq *oim_request;
-	char *soap_body,*mspauth;
+	char *soap_body;
 	char *msg_body;
 
 	g_return_if_fail(oim != NULL);
@@ -225,16 +401,12 @@
 	g_return_if_fail(oim_request != NULL);
 
 	purple_debug_info("MSNP14","sending OIM: %s\n", oim_request->oim_msg);
-	mspauth = g_strdup_printf("t=%s&amp;p=%s",
-		oim->session->passport_info.t,
-		oim->session->passport_info.p
-		);
 
 	/* if we got the challenge lock key, we compute it
 	 * else we go for the SOAP fault and resend it.
 	 */
 	if(oim->challenge == NULL){
-		purple_debug_info("MSNP14","no lock key challenge,wait for SOAP Fault and Resend\n");
+		purple_debug_info("MSNP14","no lock key challenge, wait for SOAP Fault and Resend\n");
 	}
 
 	msg_body = msn_oim_msg_to_str(oim, oim_request->oim_msg);
@@ -242,23 +414,20 @@
 					oim_request->from_member,
 					oim_request->friendname,
 					oim_request->to_member,
-					mspauth,
-					MSNP13_WLM_PRODUCT_ID,
+					MSNP15_WLM_PRODUCT_ID,
 					oim->challenge ? oim->challenge : "",
 					oim->send_seq,
 					msg_body);
 
-	msn_soap_message_send(oim->session,
-		msn_soap_message_new(MSN_OIM_SEND_SOAP_ACTION,
-			xmlnode_from_str(soap_body, -1)),
-		MSN_OIM_SEND_HOST, MSN_OIM_SEND_URL, msn_oim_send_read_cb, oim);
+	msn_oim_make_request(oim, TRUE, MSN_OIM_SEND_SOAP_ACTION, MSN_OIM_SEND_HOST,
+		MSN_OIM_SEND_URL, xmlnode_from_str(soap_body, -1), msn_oim_send_read_cb,
+		oim);
 
 	/*increase the offline Sequence control*/
 	if (oim->challenge != NULL) {
 		oim->send_seq++;
 	}
 
-	g_free(mspauth);
 	g_free(msg_body);
 	g_free(soap_body);
 }
@@ -272,7 +441,7 @@
 {
 	MsnOimRecvData *rdata = data;
 
-	if (response && msn_soap_xml_get(response->xml, "Body/Fault") == NULL) {
+	if (response && xmlnode_get_child(response->xml, "Body/Fault") == NULL) {
 		purple_debug_info("msnoim", "delete OIM success\n");
 		rdata->oim->oim_list = g_list_remove(rdata->oim->oim_list,
 			rdata->msg_id);
@@ -294,14 +463,10 @@
 
 	purple_debug_info("MSNP14","Delete single OIM Message {%s}\n",msgid);
 
-	soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE,
-		oim->session->passport_info.t, oim->session->passport_info.p, msgid);
+	soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE, msgid);
 
-	msn_soap_message_send(oim->session,
-		msn_soap_message_new(MSN_OIM_DEL_SOAP_ACTION,
-			xmlnode_from_str(soap_body, -1)),
-		MSN_OIM_RETRIEVE_HOST, MSN_OIM_RETRIEVE_URL,
-		msn_oim_delete_read_cb, rdata);
+	msn_oim_make_request(oim, FALSE, MSN_OIM_DEL_SOAP_ACTION, MSN_OIM_RETRIEVE_HOST,
+		MSN_OIM_RETRIEVE_URL, xmlnode_from_str(soap_body, -1), msn_oim_delete_read_cb, rdata);
 
 	g_free(soap_body);
 }
@@ -351,7 +516,7 @@
 				long sys_tzoff;
 #endif
 
-				if (!offset_positive)
+				if (offset_positive)
 					tzoff *= -1;
 
 				t.tm_year -= 1900;
@@ -361,7 +526,7 @@
 					tzoff += sys_tzoff;
 #else
 #ifdef HAVE_TM_GMTOFF
-				tzoff -= t.tm_gmtoff;
+				tzoff += t.tm_gmtoff;
 #else
 #	ifdef HAVE_TIMEZONE
 				tzset();    /* making sure */
@@ -445,7 +610,7 @@
 	MsnOimRecvData *rdata = data;
 
 	if (response != NULL) {
-		xmlnode *msg_node = msn_soap_xml_get(response->xml,
+		xmlnode *msg_node = xmlnode_get_child(response->xml,
 			"Body/GetMessageResponse/GetMessageResult");
 
 		if (msg_node) {
@@ -468,20 +633,37 @@
 void
 msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg)
 {
-	xmlnode *node, *mNode;
-	xmlnode *iu_node;
-	MsnSession *session = oim->session;
+	xmlnode *node;
 
 	purple_debug_info("MSNP14:OIM", "%s\n", xmlmsg);
 
-	node = xmlnode_from_str(xmlmsg, -1);
+	if (!strcmp(xmlmsg, "too-large")) {
+		/* Too many OIM's to send via NS, so we need to request them via SOAP. */
+		msn_oim_get_metadata(oim);
+	} else {
+		node = xmlnode_from_str(xmlmsg, -1);
+		msn_parse_oim_xml(oim, node);
+		xmlnode_free(node);
+	}
+}
+
+static void
+msn_parse_oim_xml(MsnOim *oim, xmlnode *node)
+{
+	xmlnode *mNode;
+	xmlnode *iu_node;
+	MsnSession *session = oim->session;
+
+	g_return_if_fail(node != NULL);
+
 	if (strcmp(node->name, "MD") != 0) {
+		char *xmlmsg = xmlnode_to_str(node, NULL);
 		purple_debug_info("msnoim", "WTF is this? %s\n", xmlmsg);
-		xmlnode_free(node);
+		g_free(xmlmsg);
 		return;
 	}
 
-	iu_node = msn_soap_xml_get(node, "E/IU");
+	iu_node = xmlnode_get_child(node, "E/IU");
 
 	if (iu_node != NULL && purple_account_get_check_mail(session->account))
 	{
@@ -528,8 +710,6 @@
 		g_free(rtime);
 		g_free(nickname);
 	}
-
-	xmlnode_free(node);
 }
 
 /*Post to get the Offline Instant Message*/
@@ -544,14 +724,11 @@
 	data->oim = oim;
 	data->msg_id = msgid;
 
-	soap_body = g_strdup_printf(MSN_OIM_GET_TEMPLATE,
-		oim->session->passport_info.t, oim->session->passport_info.p, msgid);
+	soap_body = g_strdup_printf(MSN_OIM_GET_TEMPLATE, msgid);
 
-	msn_soap_message_send(oim->session,
-		msn_soap_message_new(MSN_OIM_GET_SOAP_ACTION,
-			xmlnode_from_str(soap_body, -1)),
-		MSN_OIM_RETRIEVE_HOST, MSN_OIM_RETRIEVE_URL,
-		msn_oim_get_read_cb, data);
+	msn_oim_make_request(oim, FALSE, MSN_OIM_GET_SOAP_ACTION, MSN_OIM_RETRIEVE_HOST,
+		MSN_OIM_RETRIEVE_URL, xmlnode_from_str(soap_body, -1), msn_oim_get_read_cb,
+		data);
 
 	g_free(soap_body);
 }
--- a/libpurple/protocols/msn/oim.h	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/oim.h	Sat Jun 21 07:36:16 2008 +0000
@@ -25,17 +25,41 @@
 #ifndef _MSN_OIM_H_
 #define _MSN_OIM_H_
 
-/*OIM Retrieve SOAP Template*/
+/* OIM Retrieval Info */
 #define MSN_OIM_RETRIEVE_HOST	"rsi.hotmail.com"
 #define MSN_OIM_RETRIEVE_URL	"/rsi/rsi.asmx"
+
+/* OIM GetMetadata SOAP Template */
+#define MSN_OIM_GET_METADATA_ACTION "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata"
+
+#define MSN_OIM_GET_METADATA_TEMPLATE "<?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>"\
+		"<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
+			"<t>EMPTY</t>"\
+			"<p>EMPTY</p>"\
+		"</PassportCookie>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<GetMetadata xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\" />"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+/*OIM GetMessage SOAP Template*/
 #define MSN_OIM_GET_SOAP_ACTION	"http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage"
 
 #define MSN_OIM_GET_TEMPLATE "<?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: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>"\
 		"<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
-			"<t>%s</t>"\
-			"<p>%s</p>"\
+			"<t>EMPTY</t>"\
+			"<p>EMPTY</p>"\
 		"</PassportCookie>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -46,15 +70,18 @@
 	"</soap:Body>"\
 "</soap:Envelope>"
 
-/*OIM Delete SOAP Template*/
+/*OIM DeleteMessages SOAP Template*/
 #define MSN_OIM_DEL_SOAP_ACTION	"http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages"
 
 #define MSN_OIM_DEL_TEMPLATE "<?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: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>"\
 		"<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
-			"<t>%s</t>"\
-			" <p>%s</p>"\
+			"<t>EMPTY</t>"\
+			"<p>EMPTY</p>"\
 		"</PassportCookie>"\
 	"</soap:Header>"\
 	"<soap:Body>"\
@@ -77,13 +104,23 @@
 
 #define MSN_OIM_SEND_HOST	"ows.messenger.msn.com"
 #define MSN_OIM_SEND_URL	"/OimWS/oim.asmx"
-#define MSN_OIM_SEND_SOAP_ACTION	"http://messenger.msn.com/ws/2004/09/oim/Store"
+#define MSN_OIM_SEND_SOAP_ACTION	"http://messenger.live.com/ws/2006/09/oim/Store2"
 #define MSN_OIM_SEND_TEMPLATE "<?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: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=\"%s\" xml:lang=\"en-US\" proxy=\"MSNMSGR\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\" msnpVer=\"MSNP14\" buildVer=\"8.0.0792\"/>"\
+		"<From"\
+			" memberName=\"%s\""\
+			" friendlyName=\"%s\""\
+			" xml:lang=\"en-US\""\
+			" proxy=\"MSNMSGR\""\
+			" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\""\
+			" msnpVer=\"MSNP15\""\
+			" buildVer=\"8.5.1288\"/>"\
 		"<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/\"/>"\
+		"<Ticket passport=\"EMPTY\" 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>"\
--- a/libpurple/protocols/msn/session.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/session.c	Sat Jun 21 07:36:16 2008 +0000
@@ -45,8 +45,6 @@
 								 purple_account_get_username(account), NULL);
 	session->oim = msn_oim_new(session);
 
-	/*if you want to chat with Yahoo Messenger*/
-	//session->protocol_ver = WLM_YAHOO_PROT_VER;
 	session->protocol_ver = WLM_PROT_VER;
 
 	return session;
@@ -74,8 +72,9 @@
 	msn_userlist_destroy(session->userlist);
 
 	g_free(session->psm);
-	g_free(session->passport_info.t);
-	g_free(session->passport_info.p);
+
+	g_free(session->blocked_text);
+
 	g_free(session->passport_info.kv);
 	g_free(session->passport_info.sid);
 	g_free(session->passport_info.mspauth);
@@ -93,15 +92,13 @@
 	if (session->nexus != NULL)
 		msn_nexus_destroy(session->nexus);
 
-	if (session->contact != NULL)
-		msn_contact_destroy(session->contact);
 	if (session->oim != NULL)
 		msn_oim_destroy(session->oim);
 
 	if (session->user != NULL)
 		msn_user_destroy(session->user);
 
-	if (session->soap_table)
+	if (session->soap_table != NULL)
 		g_hash_table_destroy(session->soap_table);
 
 	if (session->soap_cleanup_handle)
@@ -195,7 +192,7 @@
  * 	passport - the one want to talk to you
  */
 void
-msn_session_report_user(MsnSession *session,const char *passport,char *msg,PurpleMessageFlags flags)
+msn_session_report_user(MsnSession *session,const char *passport,const char *msg,PurpleMessageFlags flags)
 {
 	PurpleConversation * conv;
 
--- a/libpurple/protocols/msn/session.h	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/session.h	Sat Jun 21 07:36:16 2008 +0000
@@ -38,7 +38,6 @@
 #include "cmdproc.h"
 #include "nexus.h"
 #include "httpconn.h"
-#include "contact.h"
 #include "oim.h"
 
 #include "userlist.h"
@@ -96,7 +95,6 @@
 
 	MsnNotification *notification;
 	MsnNexus *nexus;
-	MsnContact *contact;
 	MsnOim		*oim;
 	MsnSync *sync;
 
@@ -109,12 +107,10 @@
 	/*psm info*/
 	char *psm;
 
+	char *blocked_text;
+
 	struct
 	{
-		/*t and p, get via USR TWN*/
-		char *t;
-		char *p;
-
 		char *kv;
 		char *sid;
 		char *mspauth;
@@ -236,6 +232,6 @@
 
 /*post message to User*/
 void msn_session_report_user(MsnSession *session,const char *passport,
-							char *msg,PurpleMessageFlags flags);
+							const char *msg,PurpleMessageFlags flags);
 
 #endif /* _MSN_SESSION_H_ */
--- a/libpurple/protocols/msn/soap.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/soap.c	Sat Jun 21 07:36:16 2008 +0000
@@ -841,7 +841,6 @@
 					"POST %s HTTP/1.1\r\n"
 					"SOAPAction: %s\r\n"
 					"Content-Type:text/xml; charset=utf-8\r\n"
-					"Cookie: MSPAuth=%s\r\n"
 					"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
 					"Accept: */*\r\n"
 					"Host: %s\r\n"
@@ -851,7 +850,6 @@
 					"%s",
 					request->login_path,
 					request->soap_action,
-					soapconn->session->passport_info.mspauth,
 					request->login_host,
 					strlen(request->body),
 					request->body
--- a/libpurple/protocols/msn/soap2.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/soap2.c	Sat Jun 21 07:36:16 2008 +0000
@@ -80,8 +80,9 @@
 	MsnSoapMessage *message, const char *host, const char *path,
 	MsnSoapCallback cb, gpointer cb_data, gboolean first);
 
-static void msn_soap_request_destroy(MsnSoapRequest *req);
+static void msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message);
 static void msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect);
+static gboolean msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, gboolean initial);
 static void msn_soap_process(MsnSoapConnection *conn);
 
 static gboolean
@@ -182,23 +183,15 @@
 static gboolean
 msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url)
 {
-	char *c;
-
-	/* Skip the http:// */
-	if ((c = strchr(url, '/')) != NULL)
-		url += 2;
+	char *host;
+	char *path;
 
-	if ((c = strchr(url, '/')) != NULL) {
-		char *host, *path;
-
-		host = g_strndup(url, c - url);
-		path = g_strdup(c);
-
+	if (purple_url_parse(url, &host, NULL, &path, NULL, NULL)) {
 		msn_soap_message_send_internal(conn->session,
 			conn->current_request->message,	host, path,
 			conn->current_request->cb, conn->current_request->cb_data, TRUE);
 
-		msn_soap_request_destroy(conn->current_request);
+		msn_soap_request_destroy(conn->current_request, TRUE);
 		conn->current_request = NULL;
 
 		g_free(host);
@@ -223,7 +216,7 @@
 			char *faultdata = xmlnode_get_data(faultcode);
 
 			if (g_str_equal(faultdata, "psf:Redirect")) {
-				xmlnode *url = xmlnode_get_child(body, "redirectUrl");
+				xmlnode *url = xmlnode_get_child(fault, "redirectUrl");
 
 				if (url) {
 					char *urldata = xmlnode_get_data(url);
@@ -232,9 +225,10 @@
 				}
 
 				g_free(faultdata);
+				msn_soap_message_destroy(response);
 				return TRUE;
 			} else if (g_str_equal(faultdata, "wsse:FailedAuthentication")) {
-				xmlnode *reason = xmlnode_get_child(body, "faultstring");
+				xmlnode *reason = xmlnode_get_child(fault, "faultstring");
 				char *reasondata = xmlnode_get_data(reason);
 
 				msn_soap_connection_sanitize(conn, TRUE);
@@ -243,6 +237,7 @@
 
 				g_free(reasondata);
 				g_free(faultdata);
+				msn_soap_message_destroy(response);
 				return FALSE;
 			}
 
@@ -255,7 +250,8 @@
 		conn->current_request = NULL;
 		request->cb(request->message, response,
 			request->cb_data);
-		msn_soap_request_destroy(request);
+		msn_soap_message_destroy(response);
+		msn_soap_request_destroy(request, FALSE);
 	}
 
 	return TRUE;
@@ -406,10 +402,8 @@
 				message->xml = node;
 
 				if (!msn_soap_handle_body(conn, message)) {
-					msn_soap_message_destroy(message);
 					return;
 				}
-				msn_soap_message_destroy(message);
 			}
 
 			msn_soap_connection_handle_next(conn);
@@ -426,27 +420,34 @@
 static void
 msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond)
 {
+	msn_soap_write_cb_internal(data, fd, cond, FALSE);
+}
+
+static gboolean
+msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond,
+		gboolean initial)
+{
 	MsnSoapConnection *conn = data;
 	int written;
 
-	g_return_if_fail(cond == PURPLE_INPUT_WRITE);
+	if (cond != PURPLE_INPUT_WRITE) return TRUE;
 
 	written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len,
 		conn->buf->len - conn->handled_len);
 
 	if (written < 0 && errno == EAGAIN)
-		return;
+		return TRUE;
 	else if (written <= 0) {
 		purple_ssl_close(conn->ssl);
 		conn->ssl = NULL;
-		msn_soap_connection_handle_next(conn);
-		return;
+		if (!initial) msn_soap_connection_handle_next(conn);
+		return FALSE;
 	}
 
 	conn->handled_len += written;
 
 	if (conn->handled_len < conn->buf->len)
-		return;
+		return TRUE;
 
 	/* we are done! */
 	g_string_free(conn->buf, TRUE);
@@ -460,6 +461,7 @@
 	purple_input_remove(conn->event_handle);
 	conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ,
 		msn_soap_read_cb, conn);
+	return TRUE;
 }
 
 static gboolean
@@ -478,22 +480,15 @@
 			int len = -1;
 			char *body = xmlnode_to_str(req->message->xml, &len);
 			GSList *iter;
-			char *authstr = NULL;
 
 			g_queue_pop_head(conn->queue);
 
 			conn->buf = g_string_new("");
 
-			if (conn->session->passport_info.mspauth)
-				authstr = g_strdup_printf("Cookie: MSPAuth=%s\r\n",
-					conn->session->passport_info.mspauth);
-
-
 			g_string_append_printf(conn->buf,
-				"POST %s HTTP/1.1\r\n"
+				"POST /%s HTTP/1.1\r\n"
 				"SOAPAction: %s\r\n"
 				"Content-Type:text/xml; charset=utf-8\r\n"
-				"%s"
 				"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
 				"Accept: */*\r\n"
 				"Host: %s\r\n"
@@ -501,7 +496,7 @@
 				"Connection: Keep-Alive\r\n"
 				"Cache-Control: no-cache\r\n",
 				req->path, req->message->action ? req->message->action : "",
-				authstr ? authstr : "",	conn->host, len);
+				conn->host, len);
 
 			for (iter = req->message->headers; iter; iter = iter->next) {
 				g_string_append(conn->buf, (char *)iter->data);
@@ -518,9 +513,18 @@
 
 			conn->event_handle = purple_input_add(conn->ssl->fd,
 				PURPLE_INPUT_WRITE, msn_soap_write_cb, conn);
-			msn_soap_write_cb(conn, conn->ssl->fd, PURPLE_INPUT_WRITE);
+			if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) {
+				/* Not connected => reconnect and retry */
+				purple_debug_info("soap", "not connected, reconnecting");
+				
+				conn->connected = FALSE;
+				conn->current_request = NULL;
+				msn_soap_connection_sanitize(conn, FALSE);
+				
+				g_queue_push_head(conn->queue, req);
+				conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn);
+			}
 
-			g_free(authstr);
 			g_free(body);
 		}
 	}
@@ -585,7 +589,7 @@
 	}
 
 	if (conn->current_request) {
-		msn_soap_request_destroy(conn->current_request);
+		msn_soap_request_destroy(conn->current_request, FALSE);
 		conn->current_request = NULL;
 	}
 }
@@ -612,7 +616,7 @@
 	if (req->cb)
 		req->cb(req->message, NULL, req->cb_data);
 
-	msn_soap_request_destroy(req);
+	msn_soap_request_destroy(req, FALSE);
 }
 
 static void
@@ -666,28 +670,11 @@
 }
 
 static void
-msn_soap_request_destroy(MsnSoapRequest *req)
+msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message)
 {
 	g_free(req->path);
-	msn_soap_message_destroy(req->message);
+	if (!keep_message)
+		msn_soap_message_destroy(req->message);
 	g_free(req);
 }
 
-xmlnode *
-msn_soap_xml_get(xmlnode *parent, const char *node)
-{
-	xmlnode *ret = NULL;
-	char **tokens = g_strsplit(node, "/", -1);
-	int i;
-
-	for (i = 0; tokens[i]; i++) {
-		if ((ret = xmlnode_get_child(parent, tokens[i])) != NULL)
-			parent = ret;
-		else
-			break;
-	}
-
-	g_strfreev(tokens);
-	return ret;
-}
-
--- a/libpurple/protocols/msn/soap2.h	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/soap2.h	Sat Jun 21 07:36:16 2008 +0000
@@ -53,6 +53,4 @@
 
 void msn_soap_message_destroy(MsnSoapMessage *message);
 
-xmlnode *msn_soap_xml_get(xmlnode *parent, const char *node);
-
 #endif
--- a/libpurple/protocols/msn/user.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/user.c	Sat Jun 21 07:36:16 2008 +0000
@@ -28,7 +28,7 @@
 /*new a user object*/
 MsnUser *
 msn_user_new(MsnUserList *userlist, const char *passport,
-			 const char *store_name)
+			 const char *friendly_name)
 {
 	MsnUser *user;
 
@@ -37,16 +37,7 @@
 	user->userlist = userlist;
 
 	msn_user_set_passport(user, passport);
-	msn_user_set_store_name(user, store_name);
-
-	/*
-	 * XXX This seems to reset the friendly name from what it should be
-	 *     to the passport when moving users. So, screw it :)
-	 */
-#if 0
-	if (name != NULL)
-		msn_user_set_name(user, name);
-#endif
+	msn_user_set_friendly_name(user, friendly_name);
 
 	return user;
 }
@@ -75,7 +66,6 @@
 
 	g_free(user->passport);
 	g_free(user->friendly_name);
-	g_free(user->store_name);
 	g_free(user->uid);
 	g_free(user->phone.home);
 	g_free(user->phone.work);
@@ -199,18 +189,6 @@
 }
 
 void
-msn_user_set_store_name(MsnUser *user, const char *name)
-{
-	g_return_if_fail(user != NULL);
-
-	if (name != NULL)
-	{
-		g_free(user->store_name);
-		user->store_name = g_strdup(name);
-	}
-}
-
-void
 msn_user_set_uid(MsnUser *user, const char *uid)
 {
 	g_return_if_fail(user != NULL);
@@ -220,14 +198,6 @@
 }
 
 void
-msn_user_set_type(MsnUser *user, MsnUserType type)
-{
-	g_return_if_fail(user != NULL);
-
-	user->type = type;
-}
-
-void
 msn_user_set_op(MsnUser *user, int list_op)
 {
 	g_return_if_fail(user != NULL);
@@ -325,12 +295,9 @@
 	if (gc != NULL)
 		session = gc->proto_data;
 
-	if ((session != NULL) && (session->protocol_ver == WLM_PROT_VER))
-		return FALSE;
-
 	if ((session != NULL) && (user = msn_userlist_find_user(session->userlist, name)) != NULL)
 	{
-		return (user->type == MSN_USER_TYPE_YAHOO);
+		return (user->networkid == MSN_NETWORK_YAHOO);
 	}
 	return (strstr(name,"@yahoo.") != NULL);
 }
@@ -380,6 +347,22 @@
 }
 
 void
+msn_user_set_clientid(MsnUser *user, guint clientid)
+{
+	g_return_if_fail(user != NULL);
+
+	user->clientid = clientid;
+}
+
+void
+msn_user_set_network(MsnUser *user, MsnNetwork network)
+{
+	g_return_if_fail(user != NULL);
+
+	user->networkid = network;
+}
+
+void
 msn_user_set_object(MsnUser *user, MsnObject *obj)
 {
 	g_return_if_fail(user != NULL);
@@ -422,14 +405,6 @@
 }
 
 const char *
-msn_user_get_store_name(const MsnUser *user)
-{
-	g_return_val_if_fail(user != NULL, NULL);
-
-	return user->store_name;
-}
-
-const char *
 msn_user_get_home_phone(const MsnUser *user)
 {
 	g_return_val_if_fail(user != NULL, NULL);
@@ -453,6 +428,14 @@
 	return user->phone.mobile;
 }
 
+guint
+msn_user_get_clientid(const MsnUser *user)
+{
+	g_return_val_if_fail(user != NULL, 0);
+
+	return user->clientid;
+}
+
 MsnObject *
 msn_user_get_object(const MsnUser *user)
 {
--- a/libpurple/protocols/msn/user.h	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/user.h	Sat Jun 21 07:36:16 2008 +0000
@@ -33,14 +33,14 @@
 
 typedef enum
 {
-	MSN_USER_TYPE_UNKNOWN  = 0x00,
-	MSN_USER_TYPE_PASSPORT = 0x01,
-	MSN_USER_TYPE_UNKNOWN1 = 0x02,
-	MSN_USER_TYPE_MOBILE   = 0x04,
-	MSN_USER_TYPE_UNKNOWN2 = 0x08,
-	MSN_USER_TYPE_UNKNOWN3 = 0x10,
-	MSN_USER_TYPE_YAHOO    = 0x20
-} MsnUserType;
+	MSN_NETWORK_UNKNOWN      = 0x00,
+	MSN_NETWORK_PASSPORT     = 0x01,
+	MSN_NETWORK_COMMUNICATOR = 0x02,
+	MSN_NETWORK_MOBILE       = 0x04,
+	MSN_NETWORK_MNI          = 0x08,
+	MSN_NETWORK_SMTP         = 0x10,
+	MSN_NETWORK_YAHOO        = 0x20
+} MsnNetwork;
 
 /**
  * Current media.
@@ -60,7 +60,6 @@
 	MsnUserList *userlist;
 
 	char *passport;         /**< The passport account.          */
-	char *store_name;       /**< The name stored in the server. */
 	char *friendly_name;    /**< The friendly name.             */
 
 	char * uid;				/*< User Id							*/
@@ -88,7 +87,9 @@
 
 	GHashTable *clientcaps; /**< The client's capabilities.     */
 
-	MsnUserType type;       /**< The user type                  */
+	guint clientid;         /**< The client's ID                */
+
+	MsnNetwork networkid;   /**< The user's network             */
 
 	int list_op;            /**< Which lists the user is in     */
 
@@ -111,7 +112,7 @@
  * @return A new user structure.
  */
 MsnUser *msn_user_new(MsnUserList *userlist, const char *passport,
-					  const char *store_name);
+					  const char *friendly_name);
 
 /**
  * Destroys a user structure.
@@ -171,14 +172,6 @@
 void msn_user_set_friendly_name(MsnUser *user, const char *name);
 
 /**
- * Sets the store name for a user.
- *
- * @param user The user.
- * @param name The store name.
- */
-void msn_user_set_store_name(MsnUser *user, const char *name);
-
-/**
  * Sets the buddy icon for a local user.
  *
  * @param user     The user.
@@ -227,7 +220,22 @@
 void msn_user_set_work_phone(MsnUser *user, const char *number);
 
 void msn_user_set_uid(MsnUser *user, const char *uid);
-void msn_user_set_type(MsnUser *user, MsnUserType type);
+
+/**
+ * Sets the client id for a user.
+ *
+ * @param user     The user.
+ * @param clientid The client id.
+ */
+void msn_user_set_clientid(MsnUser *user, guint clientid);
+
+/**
+ * Sets the network id for a user.
+ *
+ * @param user    The user.
+ * @param network The network id.
+ */
+void msn_user_set_network(MsnUser *user, MsnNetwork network);
 
 /**
  * Sets the mobile phone number for a user.
@@ -273,15 +281,6 @@
 const char *msn_user_get_friendly_name(const MsnUser *user);
 
 /**
- * Returns the store name for a user.
- *
- * @param user The user.
- *
- * @return The store name.
- */
-const char *msn_user_get_store_name(const MsnUser *user);
-
-/**
  * Returns the home phone number for a user.
  *
  * @param user The user.
@@ -309,6 +308,24 @@
 const char *msn_user_get_mobile_phone(const MsnUser *user);
 
 /**
+ * Returns the client id for a user.
+ *
+ * @param user    The user.
+ *
+ * @return The user's client id.
+ */
+guint msn_user_get_clientid(const MsnUser *user);
+
+/**
+ * Returns the network id for a user.
+ *
+ * @param user    The user.
+ *
+ * @return The user's network id.
+ */
+MsnNetwork msn_user_get_network(const MsnUser *user);
+
+/**
  * Returns the MSNObject for a user.
  *
  * @param user The user.
--- a/libpurple/protocols/msn/userlist.c	Sat Jun 21 06:56:35 2008 +0000
+++ b/libpurple/protocols/msn/userlist.c	Sat Jun 21 07:36:16 2008 +0000
@@ -24,6 +24,8 @@
 #include "msn.h"
 #include "userlist.h"
 
+#include "contact.h"
+
 const char *lists[] = { "FL", "AL", "BL", "RL" };
 
 typedef struct
@@ -51,7 +53,7 @@
 
 		msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL);
 
-		msn_del_contact_from_list(session->contact, NULL, pa->who, MSN_LIST_PL);
+		msn_del_contact_from_list(session, NULL, pa->who, MSN_LIST_PL);
 	}
 
 	g_free(pa->who);
@@ -75,7 +77,7 @@
 		msn_callback_state_set_action(state, MSN_DENIED_BUDDY);
 
 		msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_BL);
-		msn_del_contact_from_list(session->contact, state, pa->who, MSN_LIST_PL);
+		msn_del_contact_from_list(session, state, pa->who, MSN_LIST_PL);
 	}
 
 	g_free(pa->who);
@@ -138,18 +140,18 @@
 
 #if 0
 static const char*
-get_store_name(MsnUser *user)
+get_friendly_name(MsnUser *user)
 {
-	const char *store_name;
+	const char *friendly_name;
 
 	g_return_val_if_fail(user != NULL, NULL);
 
-	store_name = msn_user_get_store_name(user);
+	friendly_name = msn_user_get_friendly_name(user);
 
-	if (store_name != NULL)
-		store_name = purple_url_encode(store_name);
+	if (friendly_name != NULL)
+		friendly_name = purple_url_encode(friendly_name);
 	else
-		store_name = msn_user_get_passport(user);
+		friendly_name = msn_user_get_passport(user);
 
 	/* this might be a bit of a hack, but it should prevent notification server
 	 * disconnections for people who have buddies with insane friendly names
@@ -159,10 +161,10 @@
 	/* Stu: yeah, that's why it's a bit of a hack, as you pointed out, we're
 	 * probably decoding the incoming store_name wrong, or something. bleh. */
 
-	if (strlen(store_name) > BUDDY_ALIAS_MAXLEN)
-		store_name = msn_user_get_passport(user);
+	if (strlen(friendly_name) > BUDDY_ALIAS_MAXLEN)
+		friendly_name = msn_user_get_passport(user);
 
-	return store_name;
+	return friendly_name;
 }
 #endif
 
@@ -340,7 +342,7 @@
 	gc = purple_account_get_connection(account);
 
 	passport = msn_user_get_passport(user);
-	store = msn_user_get_store_name(user);
+	store = msn_user_get_friendly_name(user);
 
 	msn_user_set_op(user, list_op);
 
@@ -454,7 +456,7 @@
 		user = msn_user_new(userlist, passport, userName);
 		msn_userlist_add_user(userlist, user);
 	} else {
-		msn_user_set_store_name(user, userName);
+		msn_user_set_friendly_name(user, userName);
 	}
 	return user;
 }
@@ -647,7 +649,6 @@
 
 	g_return_if_fail(userlist != NULL);
 	g_return_if_fail(userlist->session != NULL);
-	g_return_if_fail(userlist->session->contact != NULL);
 	g_return_if_fail(who != NULL);
 
 	user = msn_userlist_find_user(userlist, who);
@@ -656,7 +657,7 @@
 
 	/* delete the contact from address book via soap action */
 	if (user != NULL) {
-		msn_delete_contact(userlist->session->contact, user->uid);
+		msn_delete_contact(userlist->session, user->uid);
 	}
 }
 
@@ -693,17 +694,11 @@
 
 	new_group_name = group_name == NULL ? MSN_INDIVIDUALS_GROUP_NAME : group_name;
 
-
 	g_return_if_fail(userlist != NULL);
 	g_return_if_fail(userlist->session != NULL);
 
-
 	purple_debug_info("MSN Userlist", "Add user: %s to group: %s\n", who, new_group_name);
 
-	state = msn_callback_state_new(userlist->session);
-	msn_callback_state_set_who(state, who);
-	msn_callback_state_set_new_group_name(state, new_group_name);
-
 	if (!purple_email_is_valid(who))
 	{
 		/* only notify the user about problems adding to the friends list
@@ -719,6 +714,10 @@
 		return;
 	}
 
+	state = msn_callback_state_new(userlist->session);
+	msn_callback_state_set_who(state, who);
+	msn_callback_state_set_new_group_name(state, new_group_name);
+
 	group_id = msn_userlist_find_group_id(userlist, new_group_name);
 
 	if (group_id == NULL)
@@ -748,6 +747,7 @@
 
 		if (msn_userlist_user_is_in_group(user, group_id)) {
 			purple_debug_info("MSN Userlist", "User %s is already in group %s, returning\n", who, new_group_name);
+			msn_callback_state_free(state);
 			return;
 		}
 	}
@@ -758,7 +758,7 @@
 
 	/* Add contact in the Contact server with a SOAP request and if
 	   successful, send ADL with MSN_LIST_AL and MSN_LIST_FL and a FQY */
-	msn_add_contact_to_group(userlist->session->contact, state, who, group_id);
+	msn_add_contact_to_group(userlist->session, state, who, group_id);
 }
 
 void
@@ -781,7 +781,7 @@
 		return;
 	}
 
-	//store_name = (user != NULL) ? get_store_name(user) : who;
+	//friendly_name = (user != NULL) ? get_friendly_name(user) : who;
 
 	//purple_debug_info("MSN Userlist", "store_name = %s\n", store_name);
 
@@ -859,7 +859,6 @@
 
 	g_return_if_fail(userlist != NULL);
 	g_return_if_fail(userlist->session != NULL);
-	g_return_if_fail(userlist->session->contact != NULL);
 
 	state = msn_callback_state_new(userlist->session);
 	msn_callback_state_set_who(state, who);
@@ -878,7 +877,7 @@
 	/* add the contact to the new group, and remove it from the old one in
 	 * the callback
 	*/
-	msn_add_contact_to_group(userlist->session->contact, state, who, new_group_id);
+	msn_add_contact_to_group(userlist->session, state, who, new_group_id);
 }
 
 /*load userlist from the Blist file cache*/