changeset 21113:0d6e582f2679

propagate from branch 'im.pidgin.cpw.khc.msnp14.soap' (head c3fe9bf88d02997989374e7ecc80a3f2462ee462) to branch 'im.pidgin.pidgin' (head 18ccd2ba2c1c9b7fa3dfedf72b48b3bd01c3a7c4)
author Ka-Hing Cheung <khc@hxbc.us>
date Sat, 03 Nov 2007 05:12:43 +0000
parents 9838af97586b (current diff) 13279824fe64 (diff)
children 598fb854c1f5
files libpurple/protocols/jabber/buddy.c libpurple/protocols/msn/msn.c pidgin/gtkaccount.c pidgin/gtkdialogs.c pidgin/gtksavedstatuses.c pidgin/gtkutils.c
diffstat 16 files changed, 1528 insertions(+), 1675 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/msn/Makefile.am	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/Makefile.am	Sat Nov 03 05:12:43 2007 +0000
@@ -52,6 +52,8 @@
 	slpsession.h \
 	soap.c\
 	soap.h\
+	soap2.c \
+	soap2.h \
 	state.c \
 	state.h \
 	switchboard.c \
--- a/libpurple/protocols/msn/contact.c	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/contact.c	Sat Nov 03 05:12:43 2007 +0000
@@ -28,6 +28,7 @@
 #include "contact.h"
 #include "xmlnode.h"
 #include "group.h"
+#include "soap2.h"
 
 const char *MsnSoapPartnerScenarioText[] =
 {
@@ -47,6 +48,11 @@
 	"Pending"
 };
 
+typedef struct {
+	MsnContact *contact;
+	MsnSoapPartnerScenario which;
+} GetContactListCbData;
+
 /* new a contact */
 MsnContact *
 msn_contact_new(MsnSession *session)
@@ -55,7 +61,6 @@
 
 	contact = g_new0(MsnContact, 1);
 	contact->session = session;
-	contact->soapconn = msn_soap_new(session,contact,1);
 
 	return contact;
 }
@@ -64,15 +69,18 @@
 void
 msn_contact_destroy(MsnContact *contact)
 {
-	msn_soap_destroy(contact->soapconn);
 	g_free(contact);
 }
 
 MsnCallbackState *
-msn_callback_state_new(void)
+msn_callback_state_new(MsnSession *session)
 {
-	return g_new0(MsnCallbackState, 1);
-}
+	MsnCallbackState *state = g_new0(MsnCallbackState, 1);
+
+	state->session = session;
+
+	return state;
+}	
 
 void
 msn_callback_state_free(MsnCallbackState *state)
@@ -151,36 +159,6 @@
 	state->action |= action;
 }
 
-/*contact SOAP server login error*/
-static void
-msn_contact_login_error_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc, PurpleSslErrorType error)
-{
-	MsnSession *session;
-
-	session = soapconn->session;
-	g_return_if_fail(session != NULL);
-
-	msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to contact server"));
-}
-
-/*msn contact SOAP server connect process*/
-static gboolean
-msn_contact_login_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc)
-{
-	MsnSession * session;
-	MsnContact *contact;
-
-	contact = soapconn->parent;
-	g_return_val_if_fail(contact != NULL, TRUE);
-
-	session = contact->session;
-	g_return_val_if_fail(session != NULL, FALSE);
-
-	/*login ok!We can retrieve the contact list*/
-//	msn_get_contact_list(contact, MSN_PS_INITIAL, NULL);
-	return TRUE;
-}
-
 /*get MSN member role utility*/
 static MsnListId
 msn_get_memberrole(const char *role)
@@ -219,37 +197,20 @@
 }
 
 /* Create the AddressBook in the server, if we don't have one */
-static gboolean
-msn_create_address_cb(MsnSoapConn *soapconn)
+static void
+msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	MsnContact *contact;
-
-	if (soapconn->body == NULL)
-		return TRUE;
-
-	contact = soapconn->parent;
-	g_return_val_if_fail(contact != NULL, TRUE);
-
-	purple_debug_info("MSN AddressBook", "Address Book successfully created!\n");
-	msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL);
-
-//	msn_soap_free_read_buf(soapconn);
-	return TRUE;
-}
-
-static void
-msn_create_address_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSN AddressBook","AddressBookAdd written\n");
-	soapconn->read_cb = msn_create_address_cb;
-
-	return;
+	if (resp && msn_soap_xml_get(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);
+	} else {
+		purple_debug_info("msnab", "Address Book creation failed!\n");
+	}
 }
 
 static void
 msn_create_address_book(MsnContact * contact)
 {
-	MsnSoapReq *soap_request;
 	gchar *body;
 
 	g_return_if_fail(contact != NULL);
@@ -257,323 +218,185 @@
 	g_return_if_fail(contact->session->user != NULL);
 	g_return_if_fail(contact->session->user->passport != NULL);
 	
-	purple_debug_info("MSN AddressBook","Creating an Address Book.\n");
+	purple_debug_info("msnab","Creating an Address Book.\n");
 
 	body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport);
 
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,MSN_ADD_ADDRESSBOOK_SOAP_ACTION,
-					body,
-					NULL,
-					msn_create_address_cb,
-					msn_create_address_written_cb,
-					msn_contact_connect_init);
-	msn_soap_post(contact->soapconn, soap_request);
+	msn_soap_message_send(contact->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);
 
 	g_free(body);
-	
-	return;
+}
+
+static void
+msn_parse_each_member(MsnSession *session, xmlnode *member, const char *node,
+	MsnListId list)
+{
+	char *passport = xmlnode_get_data(xmlnode_get_child(member, node));
+	char *type = xmlnode_get_data(xmlnode_get_child(member, "type"));
+	char *member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId"));
+	MsnUser *user = msn_userlist_find_add_user(session->userlist, passport, NULL);
+
+	purple_debug_info("msncl","%s name: %s, Type: %s\n", node, passport, type);
+
+	if (member_id) {
+		user->membership_id[list] = atoi(member_id);
+	}
+
+	msn_got_lst_user(session, user, 1 << list, NULL);         
+
+	g_free(passport);
+	g_free(type);
+	g_free(member_id);
+}
+
+static void
+msn_parse_each_service(MsnSession *session, xmlnode *service)
+{
+	xmlnode *type;
+
+	if ((type = msn_soap_xml_get(service, "Info/Handle/Type"))) {
+		char *type_str = xmlnode_get_data(type);
+
+		if (g_str_equal(type_str, "Profile")) {
+			/* Process Windows Live 'Messenger Roaming Identity' */
+		} else if (g_str_equal(type_str, "Messenger")) {
+			xmlnode *lastchange = xmlnode_get_child(service, "LastChange");
+			char *lastchange_str = xmlnode_get_data(lastchange);
+			xmlnode *membership;
+
+			purple_debug_info("msncl","last change: %s\n", lastchange_str);	
+			purple_account_set_string(session->account,	"CLLastChange",
+				lastchange_str);
+
+			for (membership = msn_soap_xml_get(service,
+					"Memberships/Membership");
+				 membership; membership = xmlnode_get_next_twin(membership)) {
+
+				xmlnode *role = xmlnode_get_child(membership, "MemberRole");
+				char *role_str = xmlnode_get_data(role);
+				MsnListId list = msn_get_memberrole(role_str);
+				xmlnode *member;
+
+				purple_debug_info("msncl", "MemberRole role: %s, list: %d\n",
+					role, list);
+
+				for (member = msn_soap_xml_get(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")) {
+						msn_parse_each_member(session, member, "PassportName",
+							list);
+					} else if (g_str_equal(member_type, "PhoneMember")) {
+
+					} else if (g_str_equal(member_type, "EmailMember")) {
+						msn_parse_each_member(session, member, "Email",	list);
+					}
+				}
+
+				g_free(role_str);
+			}
+
+			g_free(lastchange_str);
+		}
+
+		g_free(type_str);
+	}
 }
 
 /*parse contact list*/
 static void
-msn_parse_contact_list(MsnContact * contact)
+msn_parse_contact_list(MsnContact *contact, xmlnode *node)
 {
-	MsnSession * session;
-	MsnListOp list_op = 0;
-	MsnListId list;
-	char * passport, *typedata;
-	xmlnode *fault, *faultstringnode, *faultdetail, *errorcode;
-	xmlnode *node, *body, *response, *result, *services;
-	xmlnode *service, *memberships, *info, *handle, *handletype;
-	xmlnode *membershipnode, *members, *member, *passportNode;
-
-	session = contact->session;
-	node = xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len);
-
-	if (node == NULL) {
-		purple_debug_error("MSNCL","Unable to parse SOAP data!\n");
-		return;
-	}
-
-	purple_debug_misc("MSNCL","Parsing contact list with size %d\n", contact->soapconn->body_len);
+	xmlnode *fault, *faultnode;
 
-	purple_debug_misc("MSNCL","Root node @ %p: Name: '%s', child: '%s', lastchild: '%s'\n", node,
-		node->name ? node->name : "(null)",
-		(node->child && node->child->name) ? node->child->name : "(null)",
-		(node->lastchild && node->lastchild->name) ? node->lastchild->name : "(null)");
-
-	body = xmlnode_get_child(node, "Body");
-
-	if (body == NULL) {
-		purple_debug_warning("MSNCL", "Failed to parse contact list Body node\n");
-		xmlnode_free(node);
-		return;
-	}
-	purple_debug_info("MSNCL","Body @ %p:  Name: '%s'\n",body,body->name);
-
-	/* Did we receive a <Fault> ? */
-	if ( (fault = xmlnode_get_child(body, "Fault")) != NULL) {
-	        purple_debug_info("MSNCL","Fault received from SOAP server!\n");
-
-		if ( (faultstringnode = xmlnode_get_child(fault, "faultstring")) != NULL ) {
-			gchar * faultstring = xmlnode_get_data(faultstringnode);
-			purple_debug_info("MSNCL", "Faultstring: %s\n", faultstring ? faultstring : "(null)");
+	/* we may get a response if our cache data is too old:
+	 *
+	 * <faultstring>Need to do full sync. Can't sync deltas Client
+	 * has too old a copy for us to do a delta sync</faultstring>
+	 *
+	 * this is not handled yet
+	 */
+	if ((fault = msn_soap_xml_get(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 ( (faultdetail = xmlnode_get_child(fault, "detail")) != NULL ) {
-			purple_debug_info("MSNCL","detail @ %p, name: %s\n",faultdetail, faultdetail->name);
-
-			if ( (errorcode = xmlnode_get_child(faultdetail, "errorcode")) != NULL ) {
-				purple_debug_info("MSNCL","errorcode @ %p, name: %s\n", errorcode, errorcode->name);
-
-				if (errorcode->child != NULL) {
-					gchar *errorcodestring = xmlnode_get_data(errorcode);
-					purple_debug_info("MSNCL", "Error Code: %s\n", errorcodestring ? errorcodestring : "(null)");
-
-					if (errorcodestring && !strncmp(errorcodestring, "ABDoesNotExist", 14) ) {
-						xmlnode_free(node);
-						g_free(errorcodestring);
-						msn_create_address_book(contact);
-						return;
-					}
-					g_free(errorcodestring);
-				}
-			}
-		}
-		xmlnode_free(node);
-		msn_get_contact_list(contact, MSN_PS_INITIAL, NULL);
-		return;
-	}
-
-	response = xmlnode_get_child(body,"FindMembershipResponse");
-
-	if (response == NULL) {
-		/* we may get a response if our cache data is too old:
-		 *
-		 * <faultstring>Need to do full sync. Can't sync deltas Client
-		 * has too old a copy for us to do a delta sync</faultstring>
-		 */
-		xmlnode_free(node);
-		msn_get_contact_list(contact, MSN_PS_INITIAL, NULL);
-		return;
-	}
-	purple_debug_info("MSNCL","FindMembershipResponse @ %p: Name: '%s'\n",response,response->name);
+		if ((faultnode = msn_soap_xml_get(fault, "detail/errorcode"))) {
+			char *errorcode = xmlnode_get_data(faultnode);
 
-	result = xmlnode_get_child(response,"FindMembershipResult");
-	if (result == NULL) {
-		purple_debug_warning("MSNCL","Received No Update!\n");
-		xmlnode_free(node);
-		return;
-	}
-	purple_debug_info("MSNCL","Result @ %p: Name: '%s'\n", result, result->name);
-
-	if ( (services = xmlnode_get_child(result,"Services")) == NULL) {
-		purple_debug_misc("MSNCL","No <Services> received.\n");
-		xmlnode_free(node);
-		return;
-	}
-
-	purple_debug_info("MSNCL","Services @ %p\n",services);
-	
-	for (service = xmlnode_get_child(services, "Service"); service;
-	                                service = xmlnode_get_next_twin(service)) {
-		purple_debug_info("MSNCL","Service @ %p\n",service);
+			if (g_str_equal(errorcode, "ABDoesNotExist")) {
+				msn_create_address_book(contact);
+				g_free(errorcode);
+				return;
+			}
 
-		if ( (info = xmlnode_get_child(service,"Info")) == NULL ) {
-			purple_debug_error("MSNCL","Error getting 'Info' child node\n");
-			continue;
-		}
-		if ( (handle = xmlnode_get_child(info,"Handle")) == NULL ) {
-			purple_debug_error("MSNCL","Error getting 'Handle' child node\n");
-			continue;
-		}
-		if ( (handletype = xmlnode_get_child(handle,"Type")) == NULL ) {
-			purple_debug_error("MSNCL","Error getting 'Type' child node\n");
-			continue;
-		}
-
-		if ( (typedata = xmlnode_get_data(handletype)) == NULL) {
-			purple_debug_error("MSNCL","Error retrieving data from 'Type' child node\n");
-			continue;
-		}
-
-		purple_debug_info("MSNCL","processing '%s' Service\n", typedata);
-
-		if ( !g_strcasecmp(typedata, "Profile") ) {
-			/* Process Windows Live 'Messenger Roaming Identity' */
-			g_free(typedata);
-			continue;
+			g_free(errorcode);
 		}
 
-		if ( !g_strcasecmp(typedata, "Messenger") ) {
-			char *LastChangeStr = NULL;
-			xmlnode *LastChangeNode;
-
-			/*Last Change Node*/
-			if ((LastChangeNode = xmlnode_get_child(service, "LastChange")))
-				LastChangeStr = xmlnode_get_data(LastChangeNode);
-			purple_debug_info("MSNCL","LastChangeNode: '%s'\n",LastChangeStr ? LastChangeStr : "(null)");
-			purple_account_set_string(session->account, "CLLastChange", LastChangeStr);
-			g_free(LastChangeStr);
-
-			memberships = xmlnode_get_child(service,"Memberships");
-			if (memberships == NULL) {
-				purple_debug_warning("MSNCL","Memberships = NULL, cleaning up and returning.\n");
-				g_free(typedata);
-				xmlnode_free(node);
-				return;
-			}
-			purple_debug_info("MSNCL","Memberships @ %p: Name: '%s'\n",memberships,memberships->name);
-			for (membershipnode = xmlnode_get_child(memberships, "Membership"); membershipnode;
-							membershipnode = xmlnode_get_next_twin(membershipnode)){
-				xmlnode *roleNode;
-				char *role = NULL;
-				list = 0;
-
-				if ((roleNode = xmlnode_get_child(membershipnode,"MemberRole"))) {
-					role = xmlnode_get_data(roleNode);
-					list = msn_get_memberrole(role);
-				}
-				list_op = 1 << list;
-
-				purple_debug_info("MSNCL","MemberRole role: %s, list_op: %d\n", role ? role : "(null)", list_op);
-
-				g_free(role);
-
-				members = xmlnode_get_child(membershipnode, "Members");
-				for (member = xmlnode_get_child(members, "Member"); member;
-						member = xmlnode_get_next_twin(member)){
-					MsnUser *user = NULL;
-					xmlnode *typeNode, *membershipIdNode = NULL;
-					gchar *type, *membershipId = NULL;
-					const char *member_type = xmlnode_get_attrib(member, "type");
-
-					if (!member_type) {
-						purple_debug_error("msn", "No Member Type specified for Member.\n");
-						continue;
-					}
+		msn_get_contact_list(contact, MSN_PS_INITIAL, NULL);
+	} else {
+		xmlnode *service;
 
-					if(!g_strcasecmp(member_type, "PassportMember") ) {
-						passport = type = NULL;
-						if ((passportNode = xmlnode_get_child(member, "PassportName")))
-							passport = xmlnode_get_data(passportNode);
-						if ((typeNode = xmlnode_get_child(member, "Type")))
-							type = xmlnode_get_data(typeNode);
-						purple_debug_info("MSNCL","Passport name: '%s', Type: %s\n", passport ? passport : "(null)", type ? type : "(null)");
-						/* Why do we even bother parsing it just to free it??? */
-						g_free(type);
-
-						user = msn_userlist_find_add_user(session->userlist,passport,NULL);
-						g_free(passport);
-
-						membershipIdNode = xmlnode_get_child(member,"MembershipId");
-						if (membershipIdNode != NULL) {
-							membershipId = xmlnode_get_data(membershipIdNode);
-							if (membershipId != NULL) {
-								user->membership_id[list] = atoi(membershipId);
-								g_free(membershipId);
-							}
-						}
-
-						msn_got_lst_user(session, user, list_op, NULL);
-					}
-					else if (!g_strcasecmp(member_type, "PhoneMember")) {
-						purple_debug_info("msn", "Recieved Phone Member; ignoring.\n");
-					}
-					else if (!g_strcasecmp(member_type, "EmailMember")) {
-						xmlnode *emailNode;
-						passport = NULL;
-
-						if ((emailNode = xmlnode_get_child(member, "Email")))
-							passport = xmlnode_get_data(emailNode);
-						purple_debug_info("MSNCL","Email Member: Name: '%s', list_op: %d\n", passport ? passport : "(null)", list_op);
-
-						user = msn_userlist_find_add_user(session->userlist, passport, NULL);
-						g_free(passport);
-
-						membershipIdNode = xmlnode_get_child(member,"MembershipId");
-						if (membershipIdNode != NULL) {
-							membershipId = xmlnode_get_data(membershipIdNode);
-							if (membershipId != NULL) {
-								user->membership_id[list] = atoi(membershipId);
-								g_free(membershipId);
-							}
-						}
-
-						msn_got_lst_user(session, user, list_op, NULL);
-					} else {
-						purple_debug_info("msn", "Unknown Member type: %s\n", member_type);
-					}
-				}
-			}
+		for (service = msn_soap_xml_get(node, "Body/FindMembershipResponse/"
+				"FindMembershipResult/Services/Service");
+			 service; service = xmlnode_get_next_twin(service)) {
+			msn_parse_each_service(contact->session, service);
 		}
 		g_free(typedata);
 	}
-
-	xmlnode_free(node);	/* Free the whole XML tree */
-}
-
-static gboolean
-msn_get_contact_list_cb(MsnSoapConn *soapconn)
-{
-	MsnContact *contact;
-	MsnSession *session;
-	const char *abLastChange;
-	const char *dynamicItemLastChange;
-	gchar *partner_scenario;
-
-	if (soapconn->body == NULL)
-		return TRUE;
-
-	purple_debug_misc("MSNCL","Got the contact list!\n");
-
-	contact = soapconn->parent;
-	g_return_val_if_fail(contact != NULL, TRUE);
-	session = soapconn->session;
-	g_return_val_if_fail(session != NULL, FALSE);
-	g_return_val_if_fail(soapconn->data_cb != NULL, TRUE);
-
-	partner_scenario = soapconn->data_cb;
-
-	msn_parse_contact_list(contact);
-	/*free the read buffer*/
-	msn_soap_free_read_buf(soapconn);
-
-	abLastChange = purple_account_get_string(session->account, "ablastChange", NULL);
-	dynamicItemLastChange = purple_account_get_string(session->account, "dynamicItemLastChange", NULL);
-
-	if (!strcmp(partner_scenario, MsnSoapPartnerScenarioText[MSN_PS_INITIAL])) {
-
-#ifdef MSN_PARTIAL_LISTS
-		/* 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);
-#else
-		msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL);
-#endif
-	} else {
-		msn_soap_free_read_buf(soapconn);
-	}
-
-	return TRUE;
 }
 
 static void
-msn_get_contact_written_cb(MsnSoapConn *soapconn)
+msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
 {
-	purple_debug_misc("MSNCL","Sent SOAP request for the contact list.\n");
-	soapconn->read_cb = msn_get_contact_list_cb;
+	GetContactListCbData *cb_data = data;
+	MsnContact *contact = cb_data->contact;
+	MsnSession *session = contact->session;
+
+	g_return_if_fail(session != NULL);
+
+	if (resp != NULL) {
+		const char *abLastChange;
+		const char *dynamicItemLastChange;
+
+		purple_debug_misc("msncl","Got the contact list!\n");
+
+		msn_parse_contact_list(cb_data->contact, resp->xml);
+		abLastChange = purple_account_get_string(session->account,
+			"ablastChange", NULL);
+		dynamicItemLastChange = purple_account_get_string(session->account,
+			"dynamicItemLastChange", NULL);
+
+		if (cb_data->which == MSN_PS_INITIAL) {
+#ifdef MSN_PARTIAL_LISTS
+			/* 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);
+#else
+			msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL);
+#endif
+		}
+	}
+
+	g_free(cb_data);
 }
 
-/* SOAP  get contact list*/
+/*SOAP  get contact list*/
 void
-msn_get_contact_list(MsnContact * contact, const MsnSoapPartnerScenario partner_scenario, const char *update_time)
+msn_get_contact_list(MsnContact * contact,
+	const MsnSoapPartnerScenario partner_scenario, const char *update_time)
 {
-	MsnSoapReq *soap_request;
-	gchar *body;
+	gchar *body = NULL;
 	gchar *update_str = NULL;
+	GetContactListCbData cb_data = { contact, partner_scenario };
 	const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario];
 
 	purple_debug_misc("MSNCL","Getting Contact List.\n");
@@ -584,17 +407,14 @@
 	}
 
 	body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, update_str ? update_str : "");
-	g_free(update_str);
 
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_GET_CONTACT_POST_URL,
-					MSN_GET_CONTACT_SOAP_ACTION,
-					body,
-					(gpointer) partner_scenario_str,
-					msn_get_contact_list_cb,
-					msn_get_contact_written_cb,
-					msn_contact_connect_init);
-	msn_soap_post(contact->soapconn,soap_request);
+	msn_soap_message_send(contact->session,
+		msn_soap_message_new(MSN_GET_CONTACT_SOAP_ACTION,
+			xmlnode_from_str(body, -1)),
+		MSN_CONTACT_SERVER, MSN_GET_CONTACT_POST_URL,
+		msn_get_contact_list_cb, g_memdup(&cb_data, sizeof(cb_data)));
+
+	g_free(update_str);
 	g_free(body);
 }
 
@@ -604,7 +424,7 @@
 	MsnSession *session = contact->session;
 	xmlnode *group;
 
-	purple_debug_info("MsnAb","msn_parse_addressbook_groups()\n");
+	purple_debug_info("MSNAB","msn_parse_addressbook_groups()\n");
 
 	for(group = xmlnode_get_child(node, "Group"); group;
 					group = xmlnode_get_next_twin(group)){
@@ -756,81 +576,48 @@
 }
 
 static gboolean
-msn_parse_addressbook(MsnContact * contact)
+msn_parse_addressbook(MsnContact * contact, xmlnode *node)
 {
-	MsnSession *session;
-	xmlnode * node,*body,*response,*result;
+	MsnSession * session;
+	xmlnode *result;
 	xmlnode *groups;
 	xmlnode *contacts;
 	xmlnode *abNode;
-	xmlnode *fault, *faultstringnode, *faultdetail, *errorcode;
+	xmlnode *fault;
 
 	session = contact->session;
 
-	node = xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len);
-	if ( node == NULL ) {
-		purple_debug_error("MSN AddressBook","Error parsing Address Book with size %d\n", contact->soapconn->body_len);
+	if ((fault = msn_soap_xml_get(node, "Body/Fault"))) {
+		xmlnode *faultnode;
+
+		if ((faultnode = xmlnode_get_child(fault, "faultstring"))) {
+			gchar *faultstring = xmlnode_get_data(faultnode);
+			purple_debug_info("MSNAB","Faultstring: %s\n", faultstring);
+			g_free(faultstring);
+		}
+
+		if ((faultnode = msn_soap_xml_get(fault, "detail/errorcode"))) {
+			gchar *errorcode = xmlnode_get_data(faultnode);
+
+			purple_debug_info("MSNAB", "Error Code: %s\n", errorcode);
+						
+			if (g_str_equal(errorcode, "ABDoesNotExist")) {
+				g_free(errorcode);
+				return TRUE;
+			}
+		}
+
 		return FALSE;
 	}
 
-	purple_debug_misc("MSN AddressBook", "Parsing Address Book with size %d\n", contact->soapconn->body_len);
-
-	purple_debug_misc("MSN AddressBook","node{%p},name:%s,child:%s,last:%s\n", node,
-		node->name ? node->name : "(null)",
-		(node->child && node->child->name) ? node->child->name : "(null)",
-		(node->lastchild && node->lastchild->name) ? node->lastchild->name : "(null)");
-
-	body = xmlnode_get_child(node,"Body");
-	purple_debug_misc("MSN AddressBook","body{%p},name:%s\n",body,body->name);
-
-	/* TODO: This appears to be used in a number of places and should be de-duplicated */
-	if ( (fault = xmlnode_get_child(body, "Fault")) != NULL) {
-		purple_debug_info("MSN AddressBook","Fault received from SOAP server!\n");
-		
-		if ( (faultstringnode = xmlnode_get_child(fault, "faultstring")) != NULL ) {
-			gchar *faultstring = xmlnode_get_data(faultstringnode);
-			purple_debug_info("MSN AddressBook","Faultstring: %s\n", faultstring ? faultstring : "(null)");
-			g_free(faultstring);
-		}
-		if ( (faultdetail = xmlnode_get_child(fault, "detail")) != NULL ) {
-			purple_debug_info("MSN AddressBook","detail @ %p, name: %s\n",faultdetail, faultdetail->name);
-
-			if ( (errorcode = xmlnode_get_child(faultdetail, "errorcode")) != NULL ) {
-				gchar *errorcodestring;
-				purple_debug_info("MSN AddressBook","errorcode @ %p, name: %s\n",errorcode, errorcode->name);
-
-				errorcodestring = xmlnode_get_data(errorcode);
-				purple_debug_info("MSN AddressBook", "Error Code: %s\n", errorcodestring ? errorcodestring : "(null)");
-						
-				if (errorcodestring && !strncmp(errorcodestring, "ABDoesNotExist", 14) ) {
-					g_free(errorcodestring);
-					xmlnode_free(node);
-					return TRUE;
-				}
-				g_free(errorcodestring);
-			}
-		}
-		xmlnode_free(node);
-		return FALSE;
+	result = msn_soap_xml_get(node, "Body/ABFindAllResponse/ABFindAllResult");
+	if(result == NULL){
+		purple_debug_misc("MSNAB","receive no address book update\n");
+		return TRUE;
 	}
 
-
-	response = xmlnode_get_child(body,"ABFindAllResponse");
-
-	if (response == NULL) {
-		xmlnode_free(node);
-		return FALSE;
-	}
-
-	purple_debug_misc("MSN SOAP","response{%p},name:%s\n",response,response->name);
-	result = xmlnode_get_child(response,"ABFindAllResult");
-	if(result == NULL){
-		purple_debug_misc("MSNAB","receive no address book update\n");
-		xmlnode_free(node);
-		return TRUE;
-	}
-	purple_debug_info("MSN SOAP","result{%p},name:%s\n",result,result->name);
-
+	/* I don't see this "groups" tag documented on msnpiki, need to find out
+	   if they are really there, and update msnpiki */
 	/*Process Group List*/
 	groups = xmlnode_get_child(result,"groups");
 	if (groups != NULL) {
@@ -840,7 +627,7 @@
 	/*add a default No group to set up the no group Membership*/
 	msn_group_new(session->userlist, MSN_INDIVIDUALS_GROUP_ID,
 				  MSN_INDIVIDUALS_GROUP_NAME);
-	purple_debug_misc("MsnAB","group_id:%s name:%s\n",
+	purple_debug_misc("MSNAB","group_id:%s name:%s\n",
 					  MSN_INDIVIDUALS_GROUP_ID, MSN_INDIVIDUALS_GROUP_NAME);
 	if ((purple_find_group(MSN_INDIVIDUALS_GROUP_NAME)) == NULL){
 		PurpleGroup *g = purple_group_new(MSN_INDIVIDUALS_GROUP_NAME);
@@ -849,7 +636,7 @@
 
 	/*add a default No group to set up the no group Membership*/
 	msn_group_new(session->userlist, MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME);
-	purple_debug_misc("MsnAB","group_id:%s name:%s\n", MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME);
+	purple_debug_misc("MSNAB","group_id:%s name:%s\n", MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME);
 	if ((purple_find_group(MSN_NON_IM_GROUP_NAME)) == NULL){
 		PurpleGroup *g = purple_group_new(MSN_NON_IM_GROUP_NAME);
 		purple_blist_add_group(g, NULL);
@@ -869,7 +656,7 @@
 
 		if ((node2 = xmlnode_get_child(abNode, "lastChange")))
 			tmp = xmlnode_get_data(node2);
-		purple_debug_info("MsnAB"," lastchanged Time:{%s}\n", tmp ? tmp : "(null)");
+		purple_debug_info("MSNAB"," lastchanged Time:{%s}\n", tmp ? tmp : "(null)");
 		purple_account_set_string(session->account, "ablastChange", tmp);
 
 		g_free(tmp); tmp = NULL;
@@ -880,68 +667,51 @@
 		g_free(tmp);
 	}
 
-	xmlnode_free(node);
-	msn_soap_free_read_buf(contact->soapconn);
 	return TRUE;
 }
 
-static gboolean
-msn_get_address_cb(MsnSoapConn *soapconn)
+static void
+msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	MsnContact *contact;
+	MsnContact *contact = data;
 	MsnSession *session;
 
-	if (soapconn->body == NULL)
-		return TRUE;
+	if (resp == NULL)
+		return;
 
-	contact = soapconn->parent;
-	g_return_val_if_fail(contact != NULL, TRUE);
-	session = soapconn->session;
-	g_return_val_if_fail(session != NULL, FALSE);
+	g_return_if_fail(contact != NULL);
+	session = contact->session;
+	g_return_if_fail(session != NULL);
 
-	purple_debug_misc("MSN AddressBook", "Got the Address Book!\n");
+	purple_debug_misc("MSNAB", "Got the Address Book!\n");
 
-	if ( msn_parse_addressbook(contact) ) {
-		//msn_soap_free_read_buf(soapconn);
-
+	if (msn_parse_addressbook(contact, resp->xml)) {
 		if (!session->logged_in) {
 			msn_send_privacy(session->account->gc);
 			msn_notification_dump_contact(session);
 		}
-
-		/*free the read buffer*/
-		msn_soap_free_read_buf(soapconn);
-		return TRUE;
 	} else {
-		/* This is making us loop infinitely when we fail to parse the address book,
-		  disable for now (we should re-enable when we send timestamps)
+		/* This is making us loop infinitely when we fail to parse the
+		  address book, disable for now (we should re-enable when we
+		  send timestamps)
 		*/
 		/*
 		msn_get_address_book(contact, NULL, NULL);
 		*/
 		msn_session_disconnect(session);
 		purple_connection_error(session->account->gc, _("Unable to retrieve MSN Address Book"));
-		return FALSE;
 	}
 }
 
-/**/
-static void
-msn_address_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_misc("MSN AddressBook","Sent SOAP request for the Address Book.\n");
-	soapconn->read_cb = msn_get_address_cb;
-}
-
 /*get the address book*/
 void
-msn_get_address_book(MsnContact *contact, const MsnSoapPartnerScenario partner_scenario, const char *LastChanged, const char *dynamicItemLastChange)
+msn_get_address_book(MsnContact *contact,
+	MsnSoapPartnerScenario partner_scenario, const char *LastChanged,
+	const char *dynamicItemLastChange)
 {
-	MsnSoapReq *soap_request;
-	char *body;
-	char *update_str = NULL;
+	char *body, *update_str = NULL;
 
-	purple_debug_misc("MSN AddressBook","Getting Address Book\n");
+	purple_debug_misc("MSNAB","Getting Address Book\n");
 
 	/*build SOAP and POST it*/
 	if (dynamicItemLastChange != NULL)
@@ -949,79 +719,58 @@
 	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 : "");
-	g_free(update_str);
 
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,MSN_GET_ADDRESS_SOAP_ACTION,
-					body,
-					NULL,
-					msn_get_address_cb,
-					msn_address_written_cb,
-					msn_contact_connect_init);
-	msn_soap_post(contact->soapconn,soap_request);
+	msn_soap_message_send(contact->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);
+
+	g_free(update_str);
 	g_free(body);
 }
 
-static gboolean
-msn_add_contact_read_cb(MsnSoapConn *soapconn)
+static void
+msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
 {
-	MsnCallbackState *state = NULL;
-	MsnUserList *userlist;
-	MsnUser *user;
-	
-	g_return_val_if_fail(soapconn->data_cb != NULL, TRUE);
-	g_return_val_if_fail(soapconn->session != NULL, FALSE);
-	g_return_val_if_fail(soapconn->session->userlist != NULL, TRUE);
+	MsnCallbackState *state = data;
+	MsnSession *session = state->session;
 
-	state = (MsnCallbackState *) soapconn->data_cb;
+	g_return_if_fail(session != NULL);
 
-	if (soapconn->body == NULL) {
-		msn_callback_state_free(state);
-		return TRUE;
-	}
+	if (resp != NULL) {
+		MsnUserList *userlist = session->userlist;
+		MsnUser *user;
 	
-	userlist = soapconn->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(soapconn->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(soapconn->session, state->who);
+		// 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);
+		}
 
-	user = msn_userlist_find_add_user(userlist, state->who, state->who);
-	msn_user_add_group_id(user, state->guid);
-
-	msn_soap_free_read_buf(soapconn);
-	msn_callback_state_free(state);
+		msn_notification_send_fqy(session, state->who);
 
-	return TRUE;
-}
+		user = msn_userlist_find_add_user(userlist, state->who, state->who);
+		msn_user_add_group_id(user, state->guid);
+	}
 
-static void
-msn_add_contact_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSNCL","Add contact request written\n");
-	soapconn->read_cb = msn_add_contact_read_cb;
+	msn_callback_state_free(state);
 }
 
 /* add a Contact in MSN_INDIVIDUALS_GROUP */
 void
 msn_add_contact(MsnContact *contact, MsnCallbackState *state, const char *passport)
 {
-	MsnSoapReq *soap_request;
 	gchar *body = NULL;
 	gchar *contact_xml = NULL;
 
-	g_return_if_fail(passport != NULL);
-/*	gchar *escaped_displayname;
+#if 0
+	gchar *escaped_displayname;
 
 
 	 if (displayname != NULL) {
@@ -1030,87 +779,71 @@
 		escaped_displayname = passport;
 	 }
 	contact_xml = g_strdup_printf(MSN_XML_ADD_CONTACT, escaped_displayname, passport);
-*/
+#endif
+
 	purple_debug_info("MSNCL","Adding contact %s to contact list\n", passport);
 
-//	if ( !strcmp(state->guid, MSN_INDIVIDUALS_GROUP_ID) ) {
-		contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
-//	}
+	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);
+
 	g_free(contact_xml);
-
-	/*build SOAP and POST it*/
-
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,
-					MSN_CONTACT_ADD_SOAP_ACTION,
-					body,
-					state,
-					msn_add_contact_read_cb,
-					msn_add_contact_written_cb,
-					msn_contact_connect_init);
-	msn_soap_post(contact->soapconn,soap_request);
-
 	g_free(body);
 }
 
-static gboolean
-msn_add_contact_to_group_read_cb(MsnSoapConn *soapconn)
+static void
+msn_add_contact_to_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
 {
-	MsnCallbackState *state; 
+	MsnCallbackState *state = data;
 	MsnUserList *userlist;
 
-	g_return_val_if_fail(soapconn->data_cb != NULL, TRUE);
-	g_return_val_if_fail(soapconn->session != NULL, FALSE);
-	g_return_val_if_fail(soapconn->session->userlist != NULL, TRUE);
+	g_return_if_fail(data != NULL);
+
+	userlist = state->session->userlist;
 
-	userlist = soapconn->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);
+		}
 
-	state = (MsnCallbackState *) soapconn->data_cb;
+		if (state->action & MSN_ADD_BUDDY) {
+			MsnUser *user = msn_userlist_find_user(userlist, state->who);
 
-	if (soapconn->body == NULL) {
-		msn_callback_state_free(state);
-		return TRUE;
-	}
-	
-	if (msn_userlist_add_buddy_to_group(userlist, state->who, state->new_group_name) == TRUE) {
-		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_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 (state->action & MSN_ADD_BUDDY) {
-
-        	if ( !msn_user_is_yahoo(soapconn->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(soapconn->session, state->who);
-	}
-
-	if (state->action & MSN_MOVE_BUDDY) {
-		msn_del_contact_from_group(soapconn->session->contact, state->who, state->old_group_name);
-	} else {
-		msn_callback_state_free(state);
-		msn_soap_free_read_buf(soapconn);
-	}
-	return TRUE;
-}
-
-static void
-msn_add_contact_to_group_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSNCL","Add contact to group request sent!\n");
-	soapconn->read_cb = msn_add_contact_to_group_read_cb;
+	msn_callback_state_free(state);
 }
 
 void
 msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, 
 			 const char *passport, const char *groupId)
 {
-	MsnSoapReq *soap_request;
 	MsnUserList *userlist;
 	MsnUser *user;
 	gchar *body = NULL, *contact_xml;
@@ -1143,13 +876,14 @@
 		return;
 	}
 
-
 	purple_debug_info("MSNCL", "Adding user %s to group %s\n", passport, 
 			  msn_userlist_find_group_name(userlist, groupId));
 
 	user = msn_userlist_find_user(userlist, passport);
 	if (user == NULL) {
-		purple_debug_warning("MSN CL", "Unable to retrieve user %s from the userlist!\n", passport);
+		purple_debug_warning("MSNCL", "Unable to retrieve user %s from the userlist!\n", passport);
+		msn_callback_state_free(state);                                     
+		return; /* guess this never happened! */
 	}
 
 	if (user != NULL && user->uid != NULL) {
@@ -1159,60 +893,35 @@
 	}
 
 	body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE, groupId, contact_xml);
-	g_free(contact_xml);
-
-	/*build SOAP and POST it*/
 
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,
-					MSN_ADD_CONTACT_GROUP_SOAP_ACTION,
-					body,
-					state,
-					msn_add_contact_to_group_read_cb,
-					msn_add_contact_to_group_written_cb,
-					msn_contact_connect_init);
-	msn_soap_post(contact->soapconn,soap_request);
+	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);
 
+	g_free(contact_xml);
 	g_free(body);
 }
 
-
-
-static gboolean
-msn_delete_contact_read_cb(MsnSoapConn *soapconn)
+static void
+msn_delete_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
 {
-	MsnUser *user;
-	MsnCallbackState *state = (MsnCallbackState *) soapconn->data_cb;
-	MsnUserList *userlist; 
-
-	g_return_val_if_fail(soapconn->session != NULL, FALSE);
-	g_return_val_if_fail(soapconn->session->userlist != NULL, TRUE);
+	MsnCallbackState *state = data;
 
-	userlist = soapconn->session->userlist;
+	if (resp != NULL) {
+		MsnUserList *userlist = state->session->userlist;
+		MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid);
 
-        if (soapconn->body == NULL) {
-                msn_callback_state_free(state);
-                return TRUE;
-        }
+		purple_debug_info("MSNCL","Delete contact successful\n");
 
-	purple_debug_info("MSNCL","Delete contact successful\n");
-
-	user = msn_userlist_find_user_with_id(userlist, state->uid);
-	if (user != NULL) {
-		msn_userlist_remove_user(userlist, user);
+		if (user != NULL) {
+			msn_userlist_remove_user(userlist, user);
+		}
 	}
 
 	msn_callback_state_free(state);
-	msn_soap_free_read_buf(soapconn);
-
-	return TRUE;
-}
-
-static void
-msn_delete_contact_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSNCL","Delete contact request written\n");
-	soapconn->read_cb = msn_delete_contact_read_cb;
 }
 
 /*delete a Contact*/
@@ -1221,72 +930,52 @@
 {	
 	gchar *body = NULL;
 	gchar *contact_id_xml = NULL ;
-	MsnSoapReq *soap_request;
 	MsnCallbackState *state;
 
 	g_return_if_fail(contactId != NULL);
 	contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, contactId);
 
-	state = msn_callback_state_new();
+	state = msn_callback_state_new(contact->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);
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,
-					MSN_CONTACT_DEL_SOAP_ACTION,
-					body,
-					state,
-					msn_delete_contact_read_cb,
-					msn_delete_contact_written_cb,
-					msn_contact_connect_init);
+	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);
 
 	g_free(contact_id_xml);
-
-	/* POST the SOAP request */
-	msn_soap_post(contact->soapconn, soap_request);
-
 	g_free(body);
 }
 
-static gboolean
-msn_del_contact_from_group_read_cb(MsnSoapConn *soapconn)
+static void
+msn_del_contact_from_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
 {
-	MsnCallbackState *state = (MsnCallbackState *) soapconn->data_cb;
+	MsnCallbackState *state = data;
 
-	if (soapconn->body == NULL) {
-		msn_callback_state_free(state);
-		return TRUE;
-	}
-	
-	if (msn_userlist_rem_buddy_from_group(soapconn->session->userlist, state->who, state->old_group_name)) {
-		purple_debug_info("MSN CL", "Contact %s deleted successfully from group %s\n", state->who, state->old_group_name);
-	} else {
-		purple_debug_info("MSN CL", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name);
+	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);
+		}
 	}
 	
 	msn_callback_state_free(state);
-	msn_soap_free_read_buf(soapconn);
-
-	return TRUE;
-}
-
-static void
-msn_del_contact_from_group_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSN CL","Del contact from group request sent!\n");
-	soapconn->read_cb = msn_del_contact_from_group_read_cb;
 }
 
 void
 msn_del_contact_from_group(MsnContact *contact, const char *passport, const char *group_name)
 {
-	MsnSoapReq *soap_request;
 	MsnUserList * userlist;
 	MsnUser *user;
 	MsnCallbackState *state;
-	gchar *body = NULL, *contact_id_xml;
+	gchar *body, *contact_id_xml;
 	const gchar *groupId;
 	
 	g_return_if_fail(passport != NULL);
@@ -1299,16 +988,16 @@
 	
 	groupId = msn_userlist_find_group_id(userlist, group_name);
 	if (groupId != NULL) {
-		purple_debug_info("MSN CL", "Deleting user %s from group %s\n", passport, group_name);
+		purple_debug_info("MSNCL", "Deleting user %s from group %s\n", passport, group_name);
 	} else {
-		purple_debug_warning("MSN CL", "Unable to retrieve group id from group %s !\n", group_name);
+		purple_debug_warning("MSNCL", "Unable to retrieve group id from group %s !\n", group_name);
 		return;
 	}
 	
 	user = msn_userlist_find_user(userlist, passport);
 	
 	if (user == NULL) {
-		purple_debug_warning("MSN CL", "Unable to retrieve user from passport %s!\n", passport);
+		purple_debug_warning("MSNCL", "Unable to retrieve user from passport %s!\n", passport);
 		return;
 	}
 
@@ -1317,58 +1006,40 @@
 		return;
 	}
 
-	state = msn_callback_state_new();
+	state = msn_callback_state_new(contact->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);
 
 	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);
+	
 	g_free(contact_id_xml);
-
-	/*build SOAP and POST it*/
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					    MSN_ADDRESS_BOOK_POST_URL,
-					    MSN_CONTACT_DEL_GROUP_SOAP_ACTION,
-					    body,
-					    state,
-					    msn_del_contact_from_group_read_cb,
-					    msn_del_contact_from_group_written_cb,
-					    msn_contact_connect_init);
-	msn_soap_post(contact->soapconn,soap_request);
-
 	g_free(body);
 }
 
 
-static gboolean
-msn_update_contact_read_cb(MsnSoapConn *soapconn)
+static void
+msn_update_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
 {
-	if (soapconn->body == NULL)
-		return TRUE;
-
-	purple_debug_info("MSN CL","Contact updated successfully\n");
-
-	return TRUE;
-}
-
-static void
-msn_update_contact_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSN CL","Update contact information request sent\n");
-	soapconn->read_cb = msn_update_contact_read_cb;
+	if (resp)
+		purple_debug_info("MSN CL","Contact updated successfully\n");
+	else
+		purple_debug_info("MSN CL","Contact updated successfully\n");
 }
 
 /* Update a contact's nickname */
-
 void
 msn_update_contact(MsnContact *contact, const char* nickname)
 {
-	MsnSoapReq *soap_request;
-	gchar *body, *escaped_nickname;
-
-	/* I'm not sure this is right, but if it isn't, the rest of this function will need to be fixed */
-	g_return_if_fail(nickname != NULL);
+	gchar *body = NULL, *escaped_nickname;
 
 	purple_debug_info("MSN CL","Update contact information with new friendly name: %s\n", nickname);
 
@@ -1376,83 +1047,49 @@
 
 	body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, escaped_nickname);
 
+	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);
+
 	g_free(escaped_nickname);
-	/*build SOAP and POST it*/
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,
-					MSN_CONTACT_UPDATE_SOAP_ACTION,
-					body,
-					NULL,
-					msn_update_contact_read_cb,
-					msn_update_contact_written_cb,
-					msn_contact_connect_init);
-	msn_soap_post(contact->soapconn, soap_request);
-
 	g_free(body);
 }
 
-
-static gboolean
-msn_del_contact_from_list_read_cb(MsnSoapConn *soapconn)
+static void
+msn_del_contact_from_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
 {
-	MsnCallbackState *state = NULL;
+	MsnCallbackState *state = data;
+	MsnSession *session = state->session;
 
-	g_return_val_if_fail(soapconn->data_cb != NULL, TRUE);
-	g_return_val_if_fail(soapconn->session != NULL, FALSE);
-	g_return_val_if_fail(soapconn->session->contact != NULL, FALSE);
-	g_return_val_if_fail(soapconn->session->userlist != NULL, FALSE);
-
-	state = (MsnCallbackState *) soapconn->data_cb;
-
-	if (soapconn->body == NULL) {
-		msn_callback_state_free(state);
-		return TRUE;
-	}
-	
-	purple_debug_info("MSN CL", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
+	if (resp != NULL) {
+		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(soapconn->session->userlist, state->who);
-		
-		if (user != NULL)
-			msn_user_unset_op(user, MSN_LIST_PL_OP);
+		if (state->list_id == MSN_LIST_PL) {
+			MsnUser *user = msn_userlist_find_user(session->userlist, state->who);
 
-		msn_add_contact_to_list(soapconn->session->contact, state, state->who, MSN_LIST_RL);
-		return TRUE;
-	}
+			if (user != NULL)
+				msn_user_unset_op(user, MSN_LIST_PL_OP);
 
-	if (state->list_id == MSN_LIST_AL) {
-		purple_privacy_permit_remove(soapconn->session->account, state->who, TRUE);
-		msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_BL);
-		msn_callback_state_free(state);
-		return TRUE;
-	}
-
-	if (state->list_id == MSN_LIST_BL) {
-		purple_privacy_deny_remove(soapconn->session->account, state->who, TRUE);
-		msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_AL);
-		msn_callback_state_free(state);
-		return TRUE;
+			msn_add_contact_to_list(session->contact, state, state->who, MSN_LIST_RL);
+		} 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_callback_state_free(state);
-	msn_soap_free_read_buf(soapconn);
-
-	return TRUE;
-}
-
-static void
-msn_del_contact_from_list_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSN CL","Delete contact from list SOAP request sent!\n");
-	soapconn->read_cb = msn_del_contact_from_list_read_cb;
 }
 
 void
 msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state,
 			  const gchar *passport, const MsnListId list)
 {
-	MsnSoapReq *soap_request;
 	gchar *body = NULL, *member = NULL;
 	MsnSoapPartnerScenario partner_scenario;
 	MsnUser *user;
@@ -1464,7 +1101,7 @@
 	purple_debug_info("MSN CL", "Deleting contact %s from %s list\n", passport, MsnMemberRole[list]);
 
 	if (state == NULL) {
-		state = msn_callback_state_new();
+		state = msn_callback_state_new(contact->session);
 	}
 	msn_callback_state_set_list_id(state, list);
 	msn_callback_state_set_who(state, passport);
@@ -1488,79 +1125,55 @@
 			        MsnSoapPartnerScenarioText[partner_scenario],
 			        MsnMemberRole[list],
 			        member);
-	g_free(member);
 
-	soap_request = msn_soap_request_new( MSN_CONTACT_SERVER,
-					     MSN_SHARE_POST_URL,
-					     MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION,
-					     body,
-					     state,
-					     msn_del_contact_from_list_read_cb,
-					     msn_del_contact_from_list_written_cb,
-					     msn_contact_connect_init);
+	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);
 
-	msn_soap_post(contact->soapconn,soap_request);
-	
+	g_free(member);
 	g_free(body);
 }
 
-static gboolean
-msn_add_contact_to_list_read_cb(MsnSoapConn *soapconn)
+static void
+msn_add_contact_to_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
+	gpointer data)
 {
-	MsnCallbackState *state = NULL;
+	MsnCallbackState *state = data;
 
-	g_return_val_if_fail(soapconn->data_cb != NULL, TRUE);
-	g_return_val_if_fail(soapconn->session != NULL, FALSE);
-	g_return_val_if_fail(soapconn->session->userlist != NULL, FALSE);
-
-	state = (MsnCallbackState *) soapconn->data_cb;
+	g_return_if_fail(state != NULL);
+	g_return_if_fail(state->session != NULL);
+	g_return_if_fail(state->session->contact != NULL);
 	
-	if (soapconn->body == NULL) {
-		msn_callback_state_free(state);
-		return TRUE;
-	}
-	
-	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(soapconn->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 (user != NULL) {
+				msn_user_set_op(user, MSN_LIST_RL_OP);
+			}
+
+			if (state->action & MSN_DENIED_BUDDY) {
 
-		if (state->action & MSN_DENIED_BUDDY) {
-			g_return_val_if_fail(soapconn->session->contact != NULL, FALSE);
-
-			msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_BL);
-			return TRUE;
+				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->list_id == MSN_LIST_AL) {
-		purple_privacy_permit_add(soapconn->session->account, state->who, TRUE);
-	} else if (state->list_id == MSN_LIST_BL) {
-		purple_privacy_deny_add(soapconn->session->account, state->who, TRUE);
-	}
-
 	msn_callback_state_free(state);
-	msn_soap_free_read_buf(soapconn);
-	return TRUE;
-}
-
-
-static void
-msn_add_contact_to_list_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSN CL","Add contact to list SOAP request sent!\n");
-	soapconn->read_cb = msn_add_contact_to_list_read_cb;
 }
 
 void
 msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
 			const gchar *passport, const MsnListId list)
 {
-	MsnSoapReq *soap_request;
 	gchar *body = NULL, *member = NULL;
 	MsnSoapPartnerScenario partner_scenario;
 
@@ -1571,7 +1184,7 @@
 	purple_debug_info("MSN CL", "Adding contact %s to %s list\n", passport, MsnMemberRole[list]);
 
 	if (state == NULL) {
-		state = msn_callback_state_new();
+		state = msn_callback_state_new(contact->session);
 	}
 	msn_callback_state_set_list_id(state, list);
 	msn_callback_state_set_who(state, passport);
@@ -1585,37 +1198,24 @@
 			       MsnMemberRole[list], 
 			       member);
 
-	g_free(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);
 
-	soap_request = msn_soap_request_new( MSN_CONTACT_SERVER,
-					     MSN_SHARE_POST_URL,
-					     MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION,
-					     body,
-					     state,
-					     msn_add_contact_to_list_read_cb,
-					     msn_add_contact_to_list_written_cb,
-					     msn_contact_connect_init);
-
-	msn_soap_post(contact->soapconn, soap_request);
-
+	g_free(member);
 	g_free(body);
 }
 
-
 #if 0
-static gboolean
-msn_gleams_read_cb(MsnSoapConn * soapconn)
+static void
+msn_gleams_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	purple_debug_info("MSN CL","Gleams read done\n");
-	return TRUE;
-}
-
-static void
-msn_gleams_written_cb(MsnSoapConn * soapconn)
-{
-	purple_debug_info("MSNP14","finish Group written\n");
-	soapconn->read_cb = msn_gleams_read_cb;
-//	msn_soap_read_cb(data,source,cond);
+	if (resp != NULL)
+		purple_debug_info("MSNP14","Gleams read done\n");
+	else
+		purple_debug_info("MSNP14","Gleams read failed\n");
 }
 
 /*get the gleams info*/
@@ -1625,16 +1225,11 @@
 	MsnSoapReq *soap_request;
 
 	purple_debug_info("MSNP14","msn get gleams info...\n");
-	/*build SOAP and POST it*/
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,
-					MSN_GET_GLEAMS_SOAP_ACTION,
-					MSN_GLEAMS_TEMPLATE,
-					NULL,
-					msn_gleams_read_cb,
-					msn_gleams_written_cb,
-					msn_contact_connect_init);
-	msn_soap_post(contact->soapconn,soap_request);
+	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);
 }
 #endif
 
@@ -1643,100 +1238,88 @@
  * Group Operations
  ***************************************************************/
 
-static gboolean
-msn_group_read_cb(MsnSoapConn *soapconn)
+static void
+msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	MsnUserList *userlist;
-	MsnCallbackState *state = NULL;
+	MsnCallbackState *state = data;
 	
-	purple_debug_info("MSN CL", "Group request successful.\n");
+	purple_debug_info("MSNCL", "Group request successful.\n");
 	
-	g_return_val_if_fail(soapconn->session != NULL, FALSE);
-	g_return_val_if_fail(soapconn->session->userlist != NULL, TRUE);
-	g_return_val_if_fail(soapconn->session->contact != NULL, FALSE);
+	g_return_if_fail(state->session != NULL);
+	g_return_if_fail(state->session->userlist != NULL);
+	g_return_if_fail(state->session->contact != NULL);
 
-	state = (MsnCallbackState *) soapconn->data_cb;
-	
-	if (soapconn->body == NULL) {
+	if (resp == NULL) {
 		msn_callback_state_free(state);
-		return TRUE;
+		return;
 	}
-	
+
 	if (state) {
-		userlist = soapconn->session->userlist;
+		MsnSession *session = state->session;
+		MsnUserList *userlist = session->userlist;
 		
 		if (state->action & MSN_RENAME_GROUP) {
-			msn_userlist_rename_group_id(soapconn->session->userlist,
+			msn_userlist_rename_group_id(session->userlist,
 						     state->guid,
 						     state->new_group_name);
 		}
 		
 		if (state->action & MSN_ADD_GROUP) {
-			gchar *guid, *endguid;
-			
-			guid = g_strstr_len(soapconn->read_buf, soapconn->read_len, "<guid>");
-			guid += 6;
-			endguid = g_strstr_len(soapconn->read_buf, soapconn->read_len, "</guid>");
-			*endguid = '\0';
-			/* create and add the new group to the userlist */
-			purple_debug_info("MSN CL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid);
-			msn_group_new(soapconn->session->userlist, guid, state->new_group_name);
+			/* 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);
+
+				/* 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(soapconn->session->userlist,
-						       state->who,
-						       state->new_group_name);
-				msn_callback_state_free(state);
-				return TRUE;
-			}
-			
-			if (state->action & MSN_MOVE_BUDDY) {
-				msn_add_contact_to_group(soapconn->session->contact, state, state->who, guid); 
-				return TRUE;
+				if (state->action & MSN_ADD_BUDDY) {
+					msn_userlist_add_buddy(session->userlist,
+						state->who,
+						state->new_group_name);
+					msn_callback_state_free(state);
+				} else if (state->action & MSN_MOVE_BUDDY) {
+					msn_add_contact_to_group(session->contact, state, state->who, guid); 
+				}
+
+				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(soapconn->session->userlist, state->guid);
+			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);
 	}
-	
-	msn_soap_free_read_buf(soapconn);
-	return TRUE;
-}
-
-static void
-msn_group_written_cb(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSN CL","Sent group request.\n");
-	soapconn->read_cb = msn_group_read_cb;
 }
 
 /* add group */
 void
 msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_name)
 {
-	MsnSoapReq *soap_request;
-	MsnContact *contact;
 	char *body = NULL;
-	gchar *escaped_group_name;
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(group_name != NULL);
 	
-	contact = session->contact;
-	purple_debug_info("MSN CL","Adding group %s to contact list.\n", group_name);
+	purple_debug_info("MSNCL","Adding group %s to contact list.\n", group_name);
 
 	if (state == NULL) {
-		state = msn_callback_state_new();
+		state = msn_callback_state_new(session);
 	}
 
 	msn_callback_state_set_action(state, MSN_ADD_GROUP);
@@ -1745,21 +1328,14 @@
 	/* escape group name's html special chars so it can safely be sent
 	* in a XML SOAP request
 	*/
-	escaped_group_name = g_markup_escape_text(group_name, -1);
-	body = g_strdup_printf(MSN_GROUP_ADD_TEMPLATE, escaped_group_name);
-	g_free(escaped_group_name);
+	body = g_markup_printf_escaped(MSN_GROUP_ADD_TEMPLATE, group_name);
 
-	/*build SOAP and POST it*/
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,
-					MSN_GROUP_ADD_SOAP_ACTION,
-					body,
-					state,
-					msn_group_read_cb,
-					msn_group_written_cb,
-					msn_contact_connect_init);
-	msn_soap_post(contact->soapconn,soap_request);
-	
+	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);
+
 	g_free(body);
 }
 
@@ -1767,8 +1343,6 @@
 void
 msn_del_group(MsnSession *session, const gchar *group_name)
 {
-	MsnSoapReq *soap_request;
-	MsnContact *contact;
 	MsnCallbackState *state;
 	char *body = NULL;
 	const gchar *guid;
@@ -1776,8 +1350,7 @@
 	g_return_if_fail(session != NULL);
 	
 	g_return_if_fail(group_name != NULL);
-	contact = session->contact;
-	purple_debug_info("MSN CL","Deleting group %s from contact list\n", group_name);
+	purple_debug_info("MSNCL","Deleting group %s from contact list\n", group_name);
 	
 	guid = msn_userlist_find_group_id(session->userlist, group_name);
 	
@@ -1785,7 +1358,7 @@
 	*  we need to delete nothing
 	*/
 	if (guid == NULL) {
-		purple_debug_info("MSN CL", "Group %s guid not found, returning.\n", group_name);
+		purple_debug_info("MSNCL", "Group %s guid not found, returning.\n", group_name);
 		return;
 	}
 
@@ -1794,21 +1367,17 @@
 		return;
 	}
 
-	state = msn_callback_state_new();
+	state = msn_callback_state_new(session);
 	msn_callback_state_set_action(state, MSN_DEL_GROUP);
 	msn_callback_state_set_guid(state, guid);
 	
 	body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE, guid);
-	/*build SOAP and POST it*/
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					    MSN_ADDRESS_BOOK_POST_URL,
-					    MSN_GROUP_DEL_SOAP_ACTION,
-					    body,
-					    state,
-					    msn_group_read_cb,
-					    msn_group_written_cb,
-					    msn_contact_connect_init);
-	msn_soap_post(contact->soapconn, soap_request);
+
+	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);
 
 	g_free(body);
 }
@@ -1817,24 +1386,22 @@
 void
 msn_contact_rename_group(MsnSession *session, const char *old_group_name, const char *new_group_name)
 {
-	MsnSoapReq *soap_request;
-	MsnContact *contact;
-	gchar * escaped_group_name, *body = NULL;
+	gchar *body = NULL;
 	const gchar * guid;
-	MsnCallbackState *state = msn_callback_state_new();
+	MsnCallbackState *state;
 	
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(session->userlist != NULL);
 	g_return_if_fail(old_group_name != NULL);
 	g_return_if_fail(new_group_name != NULL);
 	
-	contact = session->contact;
 	purple_debug_info("MSN CL", "Renaming group %s to %s.\n", old_group_name, new_group_name);
 	
 	guid = msn_userlist_find_group_id(session->userlist, old_group_name);
 	if (guid == NULL)
 		return;
 
+	state = msn_callback_state_new(session);
 	msn_callback_state_set_guid(state, guid);
 	msn_callback_state_set_new_group_name(state, new_group_name);
 
@@ -1845,31 +1412,14 @@
 
 	msn_callback_state_set_action(state, MSN_RENAME_GROUP);
 	
-	/* escape group name's html special chars so it can safely be sent
-	 * in a XML SOAP request
-	*/
-	escaped_group_name = g_markup_escape_text(new_group_name, -1);
-	
-	body = g_strdup_printf(MSN_GROUP_RENAME_TEMPLATE, guid, escaped_group_name);
+	body = g_markup_printf_escaped(MSN_GROUP_RENAME_TEMPLATE,
+		guid, new_group_name);
 	
-	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					    MSN_ADDRESS_BOOK_POST_URL,
-					    MSN_GROUP_RENAME_SOAP_ACTION,
-					    body,
-					    state,
-					    msn_group_read_cb,
-					    msn_group_written_cb,
-					    msn_contact_connect_init);
-	msn_soap_post(contact->soapconn, soap_request);
-	
-	g_free(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);
+
 	g_free(body);
 }
-
-void
-msn_contact_connect_init(MsnSoapConn *soapconn)
-{
-	msn_soap_init(soapconn, MSN_CONTACT_SERVER, TRUE,
-		      msn_contact_login_connect_cb,
-		      msn_contact_login_error_cb);
-}
--- a/libpurple/protocols/msn/contact.h	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/contact.h	Sat Nov 03 05:12:43 2007 +0000
@@ -380,6 +380,7 @@
 	gchar * guid;
 	MsnListId list_id;
 	MsnCallbackAction action;
+	MsnSession *session;
 };
 
 typedef enum 
@@ -397,7 +398,7 @@
 MsnContact * msn_contact_new(MsnSession *session);
 void msn_contact_destroy(MsnContact *contact);
 
-MsnCallbackState * msn_callback_state_new(void);
+MsnCallbackState * msn_callback_state_new(MsnSession *session);
 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);
--- a/libpurple/protocols/msn/msn.c	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Sat Nov 03 05:12:43 2007 +0000
@@ -956,21 +956,17 @@
 	}else	{
 		/*send Offline Instant Message,only to MSN Passport User*/
 		MsnSession *session;
-		MsnOim *oim;
 		char *friendname;
 
 		purple_debug_info("MSNP14","prepare to send offline Message\n");
 		session = gc->proto_data;
-		/* XXX/khc: hack */
-		if (!session->oim)
-			session->oim = msn_oim_new(session);
 
-		oim = session->oim;
 		friendname = msn_encode_mime(account->username);
-		msn_oim_prep_send_msg_info(oim, purple_account_get_username(account),
-								   friendname, who,	message);
+		msn_oim_prep_send_msg_info(session->oim,
+			purple_account_get_username(account),
+			friendname, who,	message);
+		msn_oim_send_msg(session->oim);
 		g_free(friendname);
-		msn_oim_send_msg(oim);
 	}
 
 	return 1;
--- a/libpurple/protocols/msn/nexus.c	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/nexus.c	Sat Nov 03 05:12:43 2007 +0000
@@ -22,15 +22,12 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #include "msn.h"
-#include "soap.h"
+#include "soap2.h"
 #include "nexus.h"
 #include "notification.h"
 
 #undef NEXUS_LOGIN_TWN
 
-/*Local Function Prototype*/
-static gboolean nexus_login_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc);
-
 /**************************************************************************
  * Main
  **************************************************************************/
@@ -42,8 +39,6 @@
 
 	nexus = g_new0(MsnNexus, 1);
 	nexus->session = session;
-	/*we must use SSL connection to do Windows Live ID authentication*/
-	nexus->soapconn = msn_soap_new(session,nexus,1);
 
 	nexus->challenge_data = g_hash_table_new_full(g_str_hash,
 		g_str_equal, g_free, g_free);
@@ -57,193 +52,98 @@
 	if (nexus->challenge_data != NULL)
 		g_hash_table_destroy(nexus->challenge_data);
 
-	msn_soap_destroy(nexus->soapconn);
 	g_free(nexus);
 }
 
-#if 0 /* khc */
-/**************************************************************************
- * Util
- **************************************************************************/
-
-static gssize
-msn_ssl_read(MsnNexus *nexus)
-{
-	gssize len;
-	char temp_buf[4096];
-
-	if ((len = purple_ssl_read(nexus->gsc, temp_buf,
-			sizeof(temp_buf))) > 0)
-	{
-		nexus->read_buf = g_realloc(nexus->read_buf,
-			nexus->read_len + len + 1);
-		strncpy(nexus->read_buf + nexus->read_len, temp_buf, len);
-		nexus->read_len += len;
-		nexus->read_buf[nexus->read_len] = '\0';
-	}
-
-	return len;
-}
-
-static void
-nexus_write_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnNexus *nexus = data;
-	int len, total_len;
-
-	total_len = strlen(nexus->write_buf);
-
-	len = purple_ssl_write(nexus->gsc,
-		nexus->write_buf + nexus->written_len,
-		total_len - nexus->written_len);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len <= 0) {
-		purple_input_remove(nexus->input_handler);
-		nexus->input_handler = 0;
-		/* TODO: notify of the error */
-		return;
-	}
-	nexus->written_len += len;
-
-	if (nexus->written_len < total_len)
-		return;
-
-	purple_input_remove(nexus->input_handler);
-	nexus->input_handler = 0;
-
-	g_free(nexus->write_buf);
-	nexus->write_buf = NULL;
-	nexus->written_len = 0;
-
-	nexus->written_cb(nexus, source, 0);
-}
-
-#endif
 /**************************************************************************
  * Login
  **************************************************************************/
+
 static void
-nexus_login_error_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc, PurpleSslErrorType error)
+nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
 {
-	MsnSession *session;
+	MsnNexus *nexus = data;
+	MsnSession *session = nexus->session;
+	xmlnode *node;
+
+	if (resp == NULL) {
+		msn_session_set_error(session, MSN_ERROR_AUTH, _("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);
 
-	session = soapconn->session;
-	g_return_if_fail(session != NULL);
+			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*/
+			if (session->passport_info.t != NULL){
+				g_free(session->passport_info.t);
+			}
+			session->passport_info.t = g_strdup(msn_twn_t);
 
-	soapconn->gsc = NULL;
+			if (session->passport_info.p != NULL)
+				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);
 
-	msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication:Unable to connect"));
-	/* the above line will result in nexus being destroyed, so we don't want
-	 * to destroy it here, or we'd crash */
+			purple_debug_info("MSN Nexus","Close nexus connection!\n");
+			g_free(cert_str);
+			msn_nexus_destroy(nexus);
+			session->nexus = NULL;
+
+			return;
+		}
+	}
+
+	/* we must have failed! */
+	msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication: cannot find authenticate token in server response"));
 }
 
-/*process the SOAP reply, get the Authentication Info*/
-static gboolean
-nexus_login_read_cb(MsnSoapConn *soapconn)
+/*when connect, do the SOAP Style windows Live ID authentication */
+void
+msn_nexus_connect(MsnNexus *nexus)
 {
-	MsnNexus *nexus;
-	MsnSession *session;
-
-	char *base, *c;
-	char *msn_twn_t,*msn_twn_p;
-	char *login_params;
-	char **elems, **cur, **tokens;
-	char * cert_str;
-
-	nexus = soapconn->parent;
-	g_return_val_if_fail(nexus != NULL, TRUE);
-	session = nexus->session;
-	g_return_val_if_fail(session != NULL, FALSE);
-
-	/*reply OK, we should process the SOAP body*/
-	purple_debug_info("MSN Nexus","TWN Server Windows Live ID Reply OK!\n");
-
-	//TODO: we should parse it using XML
-#ifdef NEXUS_LOGIN_TWN
-	base  = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_START_TOKEN);
-	base += strlen(TWN_START_TOKEN);
-	c     = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_END_TOKEN);
-#else
-	base  = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_LIVE_START_TOKEN);
-	base += strlen(TWN_LIVE_START_TOKEN);
-	c     = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_LIVE_END_TOKEN);
-#endif
-	login_params = g_strndup(base, c - base);
-
-	//		purple_debug_info("msn", "TWN Cert: {%s}\n", login_params);
-
-	/* Parse the challenge data. */
-	elems = g_strsplit(login_params, "&amp;", 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_strfreev(elems);
-
-	msn_twn_t = (char *)g_hash_table_lookup(nexus->challenge_data, "t");
-	msn_twn_p = (char *)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);
-	g_free(login_params);
-	msn_nexus_destroy(nexus);
-	session->nexus = NULL;
-
-	return FALSE;
-}
-
-static void
-nexus_login_written_cb(MsnSoapConn *soapconn)
-{
-	soapconn->read_cb = nexus_login_read_cb;
-//	msn_soap_read_cb(data,source,cond);
-}
-
-
-/*when connect, do the SOAP Style windows Live ID authentication */
-gboolean
-nexus_login_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc)
-{
-	MsnNexus * nexus;
-	MsnSession *session;
+	MsnSession *session = nexus->session;
 	char *ru,*lc,*id,*tw,*ct,*kpp,*kv,*ver,*rn,*tpf;
 	char *fs0,*fs;
 	char *username, *password;
-	char *request_str, *tail;
+	char *tail;
 #ifdef NEXUS_LOGIN_TWN
 	char *challenge_str;
 #else
 	char *rst1_str,*rst2_str,*rst3_str;
 #endif
-	
-	purple_debug_info("MSN Nexus","Starting Windows Live ID authentication\n");
-
-	g_return_val_if_fail(soapconn != NULL, FALSE);
 
-	nexus = soapconn->parent;
-	g_return_val_if_fail(nexus != NULL, TRUE);
+	MsnSoapMessage *soap;
 
-	session = soapconn->session;
-	g_return_val_if_fail(session != NULL, FALSE);
-
-	msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING);
-
+	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*/
@@ -272,10 +172,9 @@
 		msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication Failed"));
 		g_free(username);
 		g_free(password);
-		purple_ssl_close(gsc);
 		msn_nexus_destroy(nexus);
 		session->nexus = NULL;
-		return FALSE;
+		return;
 	}
 
 	/*
@@ -314,151 +213,8 @@
 #endif
 	g_free(fs);
 
-	soapconn->login_path = g_strdup(TWN_POST_URL);
-	request_str = g_strdup_printf(
-					"POST %s HTTP/1.1\r\n"
-					"Accept: text/*\r\n"
-					"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
-					"Host: %s\r\n"
-					"Content-Length: %" G_GSIZE_FORMAT "\r\n"
-					"Connection: Keep-Alive\r\n"
-					"Cache-Control: no-cache\r\n\r\n"
-					"%s",
-					soapconn->login_path, soapconn->login_host, strlen(tail),
-					tail);
-
-#ifdef MSN_SOAP_DEBUG	
-	purple_debug_misc("MSN Nexus", "TWN Sending:\n%s\n", request_str);
-#endif
-	g_free(tail);
-	g_free(username);
-	g_free(password);
-
-	/*prepare to send the SOAP request*/
-	msn_soap_write(soapconn, request_str, nexus_login_written_cb);
-
-	return TRUE;
+	soap = msn_soap_message_new(NULL, xmlnode_from_str(tail, -1));
+	msn_soap_message_send(nexus->session, soap, MSN_TWN_SERVER, TWN_POST_URL,
+		nexus_got_response_cb, nexus);
 }
 
-#if 0 /* khc */
-static void
-nexus_connect_written_cb(gpointer data, gint source, PurpleInputCondition cond)
-{
-	MsnNexus *nexus = data;
-	int len;
-
-	char *da_login;
-	char *base, *c;
-
-	if (nexus->input_handler == 0)
-		/* TODO: Use purple_ssl_input_add()? */
-		nexus->input_handler = purple_input_add(nexus->gsc->fd,
-			PURPLE_INPUT_READ, nexus_connect_written_cb, nexus);
-
-
-	/* Get the PassportURLs line. */
-	len = msn_ssl_read(nexus);
-
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len < 0) {
-		purple_input_remove(nexus->input_handler);
-		nexus->input_handler = 0;
-		g_free(nexus->read_buf);
-		nexus->read_buf = NULL;
-		nexus->read_len = 0;
-		/* TODO: error handling */
-		return;
-	}
-
-	if (g_strstr_len(nexus->read_buf, nexus->read_len,
-			"\r\n\r\n") == NULL)
-		return;
-
-	purple_input_remove(nexus->input_handler);
-	nexus->input_handler = 0;
-
-	base = strstr(nexus->read_buf, "PassportURLs");
-
-	if (base == NULL)
-	{
-		g_free(nexus->read_buf);
-		nexus->read_buf = NULL;
-		nexus->read_len = 0;
-		return;
-	}
-
-	if ((da_login = strstr(base, "DALogin=")) != NULL)
-	{
-		/* skip over "DALogin=" */
-		da_login += 8;
-
-		if ((c = strchr(da_login, ',')) != NULL)
-			*c = '\0';
-
-		if ((c = strchr(da_login, '/')) != NULL)
-		{
-			nexus->login_path = g_strdup(c);
-			*c = '\0';
-		}
-
-		nexus->login_host = g_strdup(da_login);
-	}
-
-	g_free(nexus->read_buf);
-	nexus->read_buf = NULL;
-	nexus->read_len = 0;
-
-	purple_ssl_close(nexus->gsc);
-
-	/* Now begin the connection to the login server. */
-	nexus->gsc = purple_ssl_connect(nexus->session->account,
-			nexus->login_host, PURPLE_SSL_DEFAULT_PORT,
-			login_connect_cb, login_error_cb, nexus);
-}
-
-
-#endif
-
-/**************************************************************************
- * Connect
- **************************************************************************/
-
-#if 0 /* khc */
-static void
-nexus_connect_cb(gpointer data, PurpleSslConnection *gsc,
-				 PurpleInputCondition cond)
-{
-	MsnNexus *nexus;
-	MsnSession *session;
-
-	nexus = data;
-	g_return_if_fail(nexus != NULL);
-
-	session = nexus->session;
-	g_return_if_fail(session != NULL);
-
-	msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH);
-
-	nexus->write_buf = g_strdup("GET /rdr/pprdr.asp\r\n\r\n");
-	nexus->written_len = 0;
-
-	nexus->read_len = 0;
-
-	nexus->written_cb = nexus_connect_written_cb;
-
-	nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE,
-		nexus_write_cb, nexus);
-
-	nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
-}
-
-#endif
-
-void
-msn_nexus_connect(MsnNexus *nexus)
-{
-	/*  Authenticate via Windows Live ID. */
-	msn_soap_init(nexus->soapconn, MSN_TWN_SERVER, TRUE, nexus_login_connect_cb, nexus_login_error_cb);
-	msn_soap_connect(nexus->soapconn);
-}
--- a/libpurple/protocols/msn/nexus.h	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/nexus.h	Sat Nov 03 05:12:43 2007 +0000
@@ -139,7 +139,6 @@
 struct _MsnNexus
 {
 	MsnSession *session;
-	MsnSoapConn *soapconn;
 	char * challenge_data_str;
 	GHashTable *challenge_data;
 };
--- a/libpurple/protocols/msn/notification.c	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Sat Nov 03 05:12:43 2007 +0000
@@ -1790,9 +1790,7 @@
 		return;
 
 	/*new a oim session*/
-	/* There are several things that call this */
-	if (session->oim == NULL)
-		session->oim = msn_oim_new(session);
+//	session->oim = msn_oim_new(session);
 //	msn_oim_connect(session->oim);
 
 	table = msn_message_get_hashtable_from_body(msg);
--- a/libpurple/protocols/msn/oim.c	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/oim.c	Sat Nov 03 05:12:43 2007 +0000
@@ -24,24 +24,31 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include "msn.h"
-#include "soap.h"
+#include "soap2.h"
 #include "oim.h"
 #include "msnutils.h"
 
+typedef struct _MsnOimSendReq {
+	char *from_member;
+	char *friendname;
+	char *to_member;
+	char *oim_msg;
+} MsnOimSendReq;
+
+typedef struct {
+	MsnOim *oim;
+	char *msg_id;
+} MsnOimRecvData;
+
 /*Local Function Prototype*/
-static void msn_oim_post_single_get_msg(MsnOim *oim,const char *msgid);
+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,
-					   const char* to_member,
-					   gint send_seq,
-					   const char *msg);
-static void msn_oim_retrieve_connect_init(MsnSoapConn *soapconn);
-static void msn_oim_send_connect_init(MsnSoapConn *soapconn);
+										   const char *friendname,
+										   const char* to_member,
+										   const char *msg);
 static void msn_oim_free_send_req(MsnOimSendReq *req);
-static void msn_oim_report_to_user(MsnOim *oim, const char *msg_str);
-static void msn_oim_get_process(MsnOim *oim, const char *oim_msg);
-static gchar *msn_oim_msg_to_str(MsnOim *oim, const char *body);
-static void msn_oim_send_process(MsnOim *oim, const char *body, int len);
+static void msn_oim_report_to_user(MsnOimRecvData *rdata, const char *msg_str);
+static char *msn_oim_msg_to_str(MsnOim *oim, const char *body);
 
 /*new a OIM object*/
 MsnOim *
@@ -51,10 +58,7 @@
 
 	oim = g_new0(MsnOim, 1);
 	oim->session = session;
-	oim->retrieveconn = msn_soap_new(session, oim, TRUE);
-	
-	oim->oim_list = NULL;
-	oim->sendconn = msn_soap_new(session, oim, TRUE);
+	oim->oim_list	= NULL;
 	oim->run_id = rand_guid();
 	oim->challenge = NULL;
 	oim->send_queue = g_queue_new();
@@ -69,8 +73,6 @@
 	MsnOimSendReq *request;
 	
 	purple_debug_info("OIM","destroy the OIM \n");
-	msn_soap_destroy(oim->retrieveconn);
-	msn_soap_destroy(oim->sendconn);
 	g_free(oim->run_id);
 	g_free(oim->challenge);
 	
@@ -84,8 +86,7 @@
 
 static MsnOimSendReq *
 msn_oim_new_send_req(const char *from_member, const char*friendname,
-					 const char* to_member, gint send_seq,
-					 const char *msg)
+	const char* to_member, const char *msg)
 {
 	MsnOimSendReq *request;
 	
@@ -93,7 +94,6 @@
 	request->from_member	=g_strdup(from_member);
 	request->friendname		= g_strdup(friendname);
 	request->to_member		= g_strdup(to_member);
-	request->send_seq		= send_seq;
 	request->oim_msg		= g_strdup(msg);
 	return request;
 }
@@ -130,135 +130,75 @@
 	return oim_body;
 }
 
-/*oim SOAP server login error*/
-static void
-msn_oim_send_error_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc, PurpleSslErrorType error)
-{
-	MsnSession *session;
-
-	session = soapconn->session;
-	g_return_if_fail(session != NULL);
-
-	msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to OIM server"));
-}
-
-/*msn oim SOAP server connect process*/
-static gboolean
-msn_oim_send_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc)
-{
-	MsnSession * session;
-	MsnOim *oim;
-
-	oim = soapconn->parent;
-	g_return_val_if_fail(oim != NULL, TRUE);
-
-	session = oim->session;
-	g_return_val_if_fail(session != NULL, FALSE);
-
-	return TRUE;
-}
-
 /*
  * Process the send return SOAP string
  * If got SOAP Fault,get the lock key,and resend it.
  */
 static void
-msn_oim_send_process(MsnOim *oim, const char *body, int len)
+msn_oim_send_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
+	gpointer data)
 {
-	xmlnode *responseNode, *bodyNode;
-	xmlnode *faultNode = NULL, *faultCodeNode, *faultstringNode;
-	xmlnode *detailNode, *challengeNode;
-	char *fault_code, *fault_text;
+	MsnOim *oim = data;
+	MsnOimSendReq *msg = g_queue_pop_head(oim->send_queue);
+
+	g_return_if_fail(msg != NULL);
 
-	responseNode = xmlnode_from_str(body,len);
+	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");
 
-	g_return_if_fail(responseNode != NULL);
+		if (faultNode == NULL) {
+			/*Send OK! return*/
+			purple_debug_info("MSNP14", "sent OIM: %s\n", msg->oim_msg);
+		} else {
+			xmlnode *faultcode = xmlnode_get_child(faultNode, "faultcode");
+
+			if (faultcode) {
+				char *faultcode_str = xmlnode_get_data(faultcode);
 
-	if ((bodyNode = xmlnode_get_child(responseNode, "Body")))
-		faultNode = xmlnode_get_child(bodyNode, "Fault");
+				if (g_str_equal(faultcode_str, "q0:AuthenticationFailed")) {
+					xmlnode *challengeNode = msn_soap_xml_get(faultNode,
+						"detail/LockKeyChallenge");
 
-	if (faultNode == NULL) {
-		/*Send OK! return*/
-		MsnOimSendReq *request;
+					if (challengeNode == NULL) {
+						if (oim->challenge) {
+							g_free(oim->challenge);
+							oim->challenge = NULL;
 
-		xmlnode_free(responseNode);
-		request = g_queue_pop_head(oim->send_queue);
-		msn_oim_free_send_req(request);
-		/*send next buffered Offline Message*/
-		msn_soap_post(oim->sendconn, NULL);
-
-		return;
-	}
+							purple_debug_info("msnoim","resending OIM: %s\n",
+								msg->oim_msg);
+							g_queue_push_head(oim->send_queue, msg);
+							msn_oim_send_msg(oim);
+							return;
+						} else {
+							purple_debug_info("msnoim",
+								"can't find lock key for OIM: %s\n",
+								msg->oim_msg);
+						}
+					} else {
+						char buf[33];
 
-	/*get the challenge,and repost it*/
-	if (faultNode)
-		faultCodeNode = xmlnode_get_child(faultNode, "faultcode");
+						char *challenge = xmlnode_get_data(challengeNode);
+						msn_handle_chl(challenge, buf);
+
+						g_free(oim->challenge);
+						oim->challenge = g_strndup(buf, sizeof(buf));
+						g_free(challenge);
+						purple_debug_info("MSNP14","lockkey:{%s}\n",oim->challenge);
 
-	if(faultCodeNode == NULL){
-		purple_debug_info("MSN OIM", "No faultcode for failed Offline Message.\n");
-		xmlnode_free(responseNode);
-		return;
+						/*repost the send*/
+						purple_debug_info("MSNP14","resending OIM: %s\n", msg->oim_msg);
+						g_queue_push_head(oim->send_queue, msg);
+						msn_oim_send_msg(oim);
+						return;
+					}
+				}
+			}
+		}
 	}
 
-	fault_code = xmlnode_get_data(faultCodeNode);
-#if 0
-	if(!strcmp(fault_code,"q0:AuthenticationFailed")){
-		/*other Fault Reason?*/
-		goto oim_send_process_fail;
-	}
-#endif
-
-	faultstringNode = xmlnode_get_child(faultNode, "faultstring");
-	fault_text = xmlnode_get_data(faultstringNode);
-	purple_debug_info("MSN OIM", "Error sending Offline Message: %s (%s)\n",
-		fault_text ? fault_text : "(null)", fault_code ? fault_code : "(null)");
-
-	/* lock key fault reason,
-	 * compute the challenge and resend it
-	 */
-	if ((detailNode = xmlnode_get_child(faultNode, "detail"))
-			&& (challengeNode = xmlnode_get_child(detailNode, "LockKeyChallenge"))) {
-		g_free(oim->challenge);
-		oim->challenge = xmlnode_get_data(challengeNode);
-
-		purple_debug_info("MSN OIM", "Retrying Offline IM with lockkey:{%s}\n",
-			oim->challenge ? oim->challenge : "(null)");
-
-		/*repost the send*/
-		msn_oim_send_msg(oim);
-
-		/* XXX: This needs to give up eventually (1 retry, maybe?) */
-	}
-
-	g_free(fault_text);
-	g_free(fault_code);
-	xmlnode_free(responseNode);
-}
-
-static gboolean
-msn_oim_send_read_cb(MsnSoapConn *soapconn)
-{
-	MsnSession *session = soapconn->session;
-	MsnOim * oim;
-
-	if (soapconn->body == NULL)
-		return TRUE;
-
-	g_return_val_if_fail(session != NULL, FALSE);
-	oim = soapconn->session->oim;
-	g_return_val_if_fail(oim != NULL, TRUE);
-
-	purple_debug_info("MSN OIM","read buffer:{%s}\n", soapconn->body);
-	msn_oim_send_process(oim,soapconn->body,soapconn->body_len);
-
-	return TRUE;
-}
-
-static void
-msn_oim_send_written_cb(MsnSoapConn *soapconn)
-{
-	soapconn->read_cb = msn_oim_send_read_cb;
-//	msn_soap_read_cb(data,source,cond);
+	msn_oim_free_send_req(msg);
 }
 
 void
@@ -266,45 +206,36 @@
 						   const char* friendname, const char *tomember,
 						   const char * msg)
 {
-	MsnOimSendReq *request;
-
 	g_return_if_fail(oim != NULL);
 
-	request = msn_oim_new_send_req(membername,friendname,tomember,oim->send_seq,msg);
-	g_queue_push_tail(oim->send_queue,request);
+	g_queue_push_tail(oim->send_queue,
+		msn_oim_new_send_req(membername, friendname, tomember, msg));
 }
 
 /*post send single message request to oim server*/
 void 
 msn_oim_send_msg(MsnOim *oim)
 {
-	MsnSoapReq *soap_request;
 	MsnOimSendReq *oim_request;
 	char *soap_body,*mspauth;
-	gchar *msg_body;
-	char buf[33];
+	char *msg_body;
 
 	g_return_if_fail(oim != NULL);
-	oim_request = g_queue_pop_head(oim->send_queue);
+	oim_request = g_queue_peek_head(oim->send_queue);
 	g_return_if_fail(oim_request != NULL);
 
-	purple_debug_info("MSN OIM","send single OIM Message\n");
+	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
 		);
-	g_queue_push_head(oim->send_queue,oim_request);
 
 	/* if we got the challenge lock key, we compute it
 	 * else we go for the SOAP fault and resend it.
 	 */
-	if(oim->challenge != NULL){
-		msn_handle_chl(oim->challenge, buf);
-	}else{
-		purple_debug_info("MSN OIM","no lock key challenge,wait for SOAP Fault and Resend\n");
-		buf[0]='\0';
+	if(oim->challenge == NULL){
+		purple_debug_info("MSNP14","no lock key challenge,wait for SOAP Fault and Resend\n");
 	}
-	purple_debug_info("MSN OIM","get the lock key challenge {%s}\n",buf);
 
 	msg_body = msn_oim_msg_to_str(oim, oim_request->oim_msg);
 	soap_body = g_strdup_printf(MSN_OIM_SEND_TEMPLATE,
@@ -313,117 +244,71 @@
 					oim_request->to_member,
 					mspauth,
 					MSNP13_WLM_PRODUCT_ID,
-					buf,
-					oim_request->send_seq,
+					oim->challenge ? oim->challenge : "",
+					oim->send_seq,
 					msg_body);
 
-	soap_request = msn_soap_request_new(MSN_OIM_SEND_HOST,
-					MSN_OIM_SEND_URL,
-					MSN_OIM_SEND_SOAP_ACTION,
-					soap_body,
-					NULL,
-					msn_oim_send_read_cb,
-					msn_oim_send_written_cb,
-					msn_oim_send_connect_init);
+	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);
+
+	/*increase the offline Sequence control*/
+	if (oim->challenge != NULL) {
+		oim->send_seq++;
+	}
+
 	g_free(mspauth);
 	g_free(msg_body);
 	g_free(soap_body);
-
-	/*increase the offline Sequence control*/
-	if(oim->challenge != NULL){
-		oim->send_seq++;
-	}
-	msn_soap_post(oim->sendconn,soap_request);
 }
 
 /****************************************
  * OIM delete SOAP request
  * **************************************/
-static gboolean
-msn_oim_delete_read_cb(MsnSoapConn *soapconn)
+static void
+msn_oim_delete_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
+	gpointer data)
 {
-	if (soapconn->body == NULL)
-		return TRUE;
-	purple_debug_info("MSN OIM","OIM delete read buffer:{%s}\n",soapconn->body);
+	MsnOimRecvData *rdata = data;
 
-	msn_soap_free_read_buf(soapconn);
-	/*get next single Offline Message*/
-//	msn_soap_post(soapconn,NULL);	/* we already do this in soap.c */
-	return TRUE;
-}
+	if (response && msn_soap_xml_get(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);
+		g_free(rdata->msg_id);
+	} else {
+		purple_debug_info("msnoim", "delete OIM failed\n");
+	}
 
-static void
-msn_oim_delete_written_cb(MsnSoapConn *soapconn)
-{
-	soapconn->read_cb = msn_oim_delete_read_cb;
+	g_free(rdata);
 }
 
 /*Post to get the Offline Instant Message*/
 static void
-msn_oim_post_delete_msg(MsnOim *oim,const char *msgid)
+msn_oim_post_delete_msg(MsnOimRecvData *rdata)
 {
-	MsnSoapReq *soap_request;
-	gchar *soap_body;
-	const char *t,*p;
+	MsnOim *oim = rdata->oim;
+	char *msgid = rdata->msg_id;
+	char *soap_body;
 
-	g_return_if_fail(oim != NULL);
-	g_return_if_fail(msgid != NULL);
-
-	purple_debug_info("MSN OIM","Delete single OIM Message {%s}\n",msgid);
-	t = oim->session->passport_info.t;
-	p = oim->session->passport_info.p;
+	purple_debug_info("MSNP14","Delete single OIM Message {%s}\n",msgid);
 
 	soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE,
-					t,
-					p,
-					msgid
-					);
-	soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST,
-					MSN_OIM_RETRIEVE_URL,
-					MSN_OIM_DEL_SOAP_ACTION,
-					soap_body,
-					NULL,
-					msn_oim_delete_read_cb,
-					msn_oim_delete_written_cb,
-					msn_oim_retrieve_connect_init);
+		oim->session->passport_info.t, oim->session->passport_info.p, 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);
+
 	g_free(soap_body);
-	msn_soap_post(oim->retrieveconn,soap_request);
 }
 
 /****************************************
  * OIM get SOAP request
  * **************************************/
-/*oim SOAP server login error*/
-static void
-msn_oim_get_error_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc, PurpleSslErrorType error)
-{
-	MsnSession *session;
-
-	session = soapconn->session;
-	g_return_if_fail(session != NULL);
-	msn_soap_clean_unhandled_requests(soapconn);
-
-//	msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to OIM server"));
-}
-
-/*msn oim SOAP server connect process*/
-static gboolean
-msn_oim_get_connect_cb(MsnSoapConn *soapconn, PurpleSslConnection *gsc)
-{
-	MsnSession * session;
-	MsnOim *oim;
-
-	oim = soapconn->parent;
-	g_return_val_if_fail(oim != NULL, TRUE);
-
-	session = oim->session;
-	g_return_val_if_fail(session != NULL, FALSE);
-
-	purple_debug_info("MSN OIM","Connected and ready to get OIM!\n");
-
-	return TRUE;
-}
-
 /* like purple_str_to_time, but different. The format of the timestamp
  * is like this: 5 Sep 2007 21:42:12 -0700 */
 static time_t
@@ -435,9 +320,13 @@
 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
 	};
+	time_t tval = 0;
 	struct tm t;
 	memset(&t, 0, sizeof(t));
 
+	time(&tval);
+	localtime_r(&tval, &t);
+
 	if (sscanf(timestamp, "%02d %03s %04d %02d:%02d:%02d %05s",
 					&t.tm_mday, month_str, &t.tm_year,
 					&t.tm_hour, &t.tm_min, &t.tm_sec, tz_str) == 7) {
@@ -466,14 +355,13 @@
 					tzoff *= -1;
 
 				t.tm_year -= 1900;
-				t.tm_isdst = 0;
 
 #ifdef _WIN32
 				if ((sys_tzoff = wpurple_get_tz_offset()) != -1)
 					tzoff += sys_tzoff;
 #else
 #ifdef HAVE_TM_GMTOFF
-				tzoff += t.tm_gmtoff;
+				tzoff -= t.tm_gmtoff;
 #else
 #	ifdef HAVE_TIMEZONE
 				tzset();    /* making sure */
@@ -487,13 +375,13 @@
 		}
 	}
 
-	purple_debug_info("MSN OIM:OIM", "Can't parse timestamp %s\n", timestamp);
-	return time(NULL);
+	purple_debug_info("MSNP14:OIM", "Can't parse timestamp %s\n", timestamp);
+	return tval;
 }
 
 /*Post the Offline Instant Message to User Conversation*/
 static void
-msn_oim_report_to_user(MsnOim *oim, const char *msg_str)
+msn_oim_report_to_user(MsnOimRecvData *rdata, const char *msg_str)
 {
 	MsnMessage *message;
 	char *date,*from,*decode_msg;
@@ -502,14 +390,13 @@
 	char *start,*end;
 	int has_nick = 0;
 	char *passport_str, *passport;
-	char *msg_id;
 	time_t stamp;
 
 	message = msn_message_new(MSN_MSG_UNKNOWN);
 
 	msn_message_parse_payload(message, msg_str, strlen(msg_str),
 							  MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM);
-	purple_debug_info("MSN OIM","oim body:{%s}\n",message->body);
+	purple_debug_info("MSNP14","oim body:{%s}\n",message->body);
 	decode_msg = (char *)purple_base64_decode(message->body,&body_len);
 	date =	(char *)g_hash_table_lookup(message->attr_table, "Date");
 	from =	(char *)g_hash_table_lookup(message->attr_table, "From");
@@ -519,12 +406,12 @@
 	if(has_nick){
 		tokens = g_strsplit(from , " " , 2);
 		passport_str = g_strdup(tokens[1]);
-		purple_debug_info("MSN OIM","oim Date:{%s},nickname:{%s},tokens[1]:{%s} passport{%s}\n",
+		purple_debug_info("MSNP14","oim Date:{%s},nickname:{%s},tokens[1]:{%s} passport{%s}\n",
 							date,tokens[0],tokens[1],passport_str);
 		g_strfreev(tokens);
 	}else{
 		passport_str = g_strdup(from);
-		purple_debug_info("MSN OIM","oim Date:{%s},passport{%s}\n",
+		purple_debug_info("MSNP14","oim Date:{%s},passport{%s}\n",
 					date,passport_str);
 	}
 	start = strstr(passport_str,"<");
@@ -536,66 +423,43 @@
 
 	stamp = msn_oim_parse_timestamp(date);
 
-	serv_got_im(oim->session->account->gc, passport, decode_msg, 0, stamp);
+	serv_got_im(rdata->oim->session->account->gc, passport, decode_msg, 0,
+		stamp);
 
 	/*Now get the oim message ID from the oim_list.
 	 * and append to read list to prepare for deleting the Offline Message when sign out
 	 */
-	if(oim->oim_list != NULL){
-		msg_id = oim->oim_list->data;
-		msn_oim_post_delete_msg(oim,msg_id);
-		oim->oim_list = g_list_remove(oim->oim_list, oim->oim_list->data);
-		g_free(msg_id);
-	}
+	msn_oim_post_delete_msg(rdata);
 
 	g_free(passport);
+	g_free(decode_msg);
 }
 
 /* Parse the XML data,
  * prepare to report the OIM to user
  */
 static void
-msn_oim_get_process(MsnOim *oim, const char *oim_msg)
-{
-	xmlnode *oim_node,*bodyNode,*responseNode,*msgNode;
-	char *msg_str;
-
-	oim_node = xmlnode_from_str(oim_msg, strlen(oim_msg));
-	bodyNode = xmlnode_get_child(oim_node,"Body");
-	responseNode = xmlnode_get_child(bodyNode,"GetMessageResponse");
-	msgNode = xmlnode_get_child(responseNode,"GetMessageResult");
-	msg_str = xmlnode_get_data(msgNode);
-	purple_debug_info("OIM","msg:{%s}\n",msg_str);
-	msn_oim_report_to_user(oim,msg_str);
-
-	g_free(msg_str);
-	xmlnode_free(oim_node);
-}
-
-static gboolean
-msn_oim_get_read_cb(MsnSoapConn *soapconn)
+msn_oim_get_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
+	gpointer data)
 {
-	MsnOim * oim = soapconn->session->oim;
-
-	if (soapconn->body == NULL)
-		return TRUE;
+	MsnOimRecvData *rdata = data;
 
-	purple_debug_info("MSN OIM","OIM get read buffer:{%s}\n",soapconn->body);
-
-	/*we need to process the read message!*/
-	msn_oim_get_process(oim,soapconn->body);
-	msn_soap_free_read_buf(soapconn);
+	if (response != NULL) {
+		xmlnode *msg_node = msn_soap_xml_get(response->xml,
+			"Body/GetMessageResponse/GetMessageResult");
 
-	/*get next single Offline Message*/
-//	msn_soap_post(soapconn,NULL); /* we already do this in soap.c */
-	return TRUE;
-}
-
-static void
-msn_oim_get_written_cb(MsnSoapConn *soapconn)
-{
-	soapconn->read_cb = msn_oim_get_read_cb;
-//	msn_soap_read_cb(data,source,cond);
+		if (msg_node) {
+			char *msg_str = xmlnode_get_data(msg_node);
+			msn_oim_report_to_user(rdata, msg_str);
+			g_free(msg_str);
+		} else {
+			char *str = xmlnode_to_str(response->xml, NULL);
+			purple_debug_info("msnoim", "Unknown response: %s\n", str);
+			g_free(str);
+		}
+	} else {
+		purple_debug_info("msnoim", "Failed to get OIM\n");
+	}
 }
 
 /* parse the oim XML data 
@@ -604,116 +468,88 @@
 void
 msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg)
 {
-	xmlnode *node, *mNode,*ENode,*INode,*rtNode,*nNode;
-	char *passport,*msgid,*nickname, *unread, *rTime = NULL;
+	xmlnode *node, *mNode;
+	xmlnode *iu_node;
 	MsnSession *session = oim->session;
 
-	purple_debug_info("MSN OIM:OIM", "%s", xmlmsg);
+	purple_debug_info("MSNP14:OIM", "%s", xmlmsg);
 
-	node = xmlnode_from_str(xmlmsg, strlen(xmlmsg));
-	if (!node || !node->name || strcmp(node->name, "MD") != 0) {
-		if (node)
-			xmlnode_free(node);
+	node = xmlnode_from_str(xmlmsg, -1);
+	if (strcmp(node->name, "MD") != 0) {
+		purple_debug_info("msnoim", "WTF is this? %s\n", xmlmsg);
+		xmlnode_free(node);
 		return;
 	}
 
-	ENode = xmlnode_get_child(node, "E");
-	INode = xmlnode_get_child(ENode, "IU");
-	unread = xmlnode_get_data(INode);
-
-	if (unread != NULL && purple_account_get_check_mail(session->account))
-	{
-		int count = atoi(unread);
+	iu_node = msn_soap_xml_get(node, "E/IU");
 
-		if (count > 0)
-		{
-			const char *passport;
-			const char *url;
+	if (iu_node != NULL && purple_account_get_check_mail(session->account))
+	{
+		char *unread = xmlnode_get_data(iu_node);
+		const char *passport = msn_user_get_passport(session->user);
+		const char *url = session->passport_info.file;
 
-			passport = msn_user_get_passport(session->user);
-			url = session->passport_info.file;
-
-			purple_notify_emails(session->account->gc, atoi(unread), FALSE, NULL, NULL,
-					&passport, &url, NULL, NULL);
-		}
+		/* XXX/khc: pretty sure this is wrong */
+		purple_notify_emails(session->account->gc, atoi(unread), FALSE, NULL,
+			NULL, &passport, &url, NULL, NULL);
+		g_free(unread);
 	}
 
 	for(mNode = xmlnode_get_child(node, "M"); mNode;
 					mNode = xmlnode_get_next_twin(mNode)){
-		/*email Node*/
-		ENode = xmlnode_get_child(mNode,"E");
-		passport = xmlnode_get_data(ENode);
-		/*Index */
-		INode = xmlnode_get_child(mNode,"I");
-		msgid = xmlnode_get_data(INode);
-		/*Nickname*/
-		nNode = xmlnode_get_child(mNode,"N");
-		nickname = xmlnode_get_data(nNode);
-		/*receive time*/
-		rtNode = xmlnode_get_child(mNode,"RT");
-		if(rtNode != NULL)
-			rTime = xmlnode_get_data(rtNode);
-/*		purple_debug_info("MSN OIM","E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime); */
+		char *passport, *msgid, *nickname, *rtime = NULL;
+		xmlnode *e_node, *i_node, *n_node, *rt_node;
+
+		e_node = xmlnode_get_child(mNode, "E");
+		passport = xmlnode_get_data(e_node);
+
+		i_node = xmlnode_get_child(mNode, "I");
+		msgid = xmlnode_get_data(i_node);
+
+		n_node = xmlnode_get_child(mNode, "N");
+		nickname = xmlnode_get_data(n_node);
 
-		oim->oim_list = g_list_append(oim->oim_list,strdup(msgid));
-		msn_oim_post_single_get_msg(oim,msgid);
+		rt_node = xmlnode_get_child(mNode, "RT");
+		if (rt_node != NULL) {
+			rtime = xmlnode_get_data(rt_node);
+		}
+/*		purple_debug_info("msnoim","E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime); */
+
+		if (!g_list_find_custom(oim->oim_list, msgid, (GCompareFunc)strcmp)) {
+			oim->oim_list = g_list_append(oim->oim_list, msgid);
+			msn_oim_post_single_get_msg(oim, msgid);
+			msgid = NULL;
+		}
+
 		g_free(passport);
 		g_free(msgid);
-		g_free(rTime);
-		rTime = NULL;
+		g_free(rtime);
 		g_free(nickname);
 	}
-	g_free(unread);
+
 	xmlnode_free(node);
 }
 
 /*Post to get the Offline Instant Message*/
 static void
-msn_oim_post_single_get_msg(MsnOim *oim,const char *msgid)
+msn_oim_post_single_get_msg(MsnOim *oim, char *msgid)
 {
-	MsnSoapReq *soap_request;
-	gchar *soap_body;
-	const char *t,*p;
+	char *soap_body;
+	MsnOimRecvData *data = g_new0(MsnOimRecvData, 1);
 
-	purple_debug_info("MSN OIM","Get single OIM Message\n");
-	t = oim->session->passport_info.t;
-	p = oim->session->passport_info.p;
+	purple_debug_info("MSNP14","Get single OIM Message\n");
+
+	data->oim = oim;
+	data->msg_id = msgid;
 
 	soap_body = g_strdup_printf(MSN_OIM_GET_TEMPLATE,
-					t,
-					p,
-					msgid
-					);
-	soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST,
-					MSN_OIM_RETRIEVE_URL,
-					MSN_OIM_GET_SOAP_ACTION,
-					soap_body,
-					NULL,
-					msn_oim_get_read_cb,
-					msn_oim_get_written_cb,
-					msn_oim_retrieve_connect_init);
-	g_free(soap_body);
-	msn_soap_post(oim->retrieveconn,soap_request);
-}
+		oim->session->passport_info.t, oim->session->passport_info.p, msgid);
 
-/*msn oim retrieve server connect init */
-static void
-msn_oim_retrieve_connect_init(MsnSoapConn *soapconn)
-{
-	purple_debug_info("MSN OIM","Initializing OIM retrieve connection\n");
-	msn_soap_init(soapconn, MSN_OIM_RETRIEVE_HOST, TRUE,
-		      msn_oim_get_connect_cb,
-		      msn_oim_get_error_cb);
-}
+	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 Send Server Connect Init Function*/
-static void
-msn_oim_send_connect_init(MsnSoapConn *sendconn)
-{
-	purple_debug_info("MSN OIM","Initializing OIM send connection\n");
-	msn_soap_init(sendconn, MSN_OIM_SEND_HOST, TRUE,
-		      msn_oim_send_connect_cb,
-		      msn_oim_send_error_cb);
+	g_free(soap_body);
 }
-
-/* EOF oim.c*/
--- a/libpurple/protocols/msn/oim.h	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/oim.h	Sat Nov 03 05:12:43 2007 +0000
@@ -95,17 +95,6 @@
 	"</soap:Body>"\
 "</soap:Envelope>"
 
-typedef struct _MsnOimSendReq MsnOimSendReq;
-
-struct _MsnOimSendReq
-{
-	char *from_member;
-	char *friendname;
-	char *to_member;
-	char *oim_msg;
-	gint send_seq;
-};
-
 typedef struct _MsnOim MsnOim;
 
 struct _MsnOim
@@ -127,7 +116,6 @@
  * **************************************************/
 MsnOim * msn_oim_new(MsnSession *session);
 void msn_oim_destroy(MsnOim *oim);
-void msn_oim_connect(MsnOim *oim);
 
 void msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg);
 
@@ -138,11 +126,5 @@
 
 void msn_oim_send_msg(MsnOim *oim);
 
-/*get the OIM message*/
-void msn_oim_get_msg(MsnOim *oim);
-
-/*report the oim message to the conversation*/
-void msn_oim_report_user(MsnOim *oim,const char *passport,char *msg);
-
 #endif/* _MSN_OIM_H_*/
 /*endof oim.h*/
--- a/libpurple/protocols/msn/session.c	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/session.c	Sat Nov 03 05:12:43 2007 +0000
@@ -24,6 +24,7 @@
 #include "msn.h"
 #include "session.h"
 #include "notification.h"
+#include "oim.h"
 
 #include "dialog.h"
 
@@ -42,6 +43,7 @@
 
 	session->user = msn_user_new(session->userlist,
 								 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;
@@ -99,6 +101,12 @@
 	if (session->user != NULL)
 		msn_user_destroy(session->user);
 
+	if (session->soap_table)
+		g_hash_table_destroy(session->soap_table);
+
+	if (session->soap_cleanup_handle)
+		purple_timeout_remove(session->soap_cleanup_handle);
+
 	g_free(session);
 }
 
--- a/libpurple/protocols/msn/session.h	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/session.h	Sat Nov 03 05:12:43 2007 +0000
@@ -126,6 +126,9 @@
 		char *client_ip;
 		int client_port;
 	} passport_info;
+
+	GHashTable *soap_table;
+	int soap_cleanup_handle;
 };
 
 /**
--- a/libpurple/protocols/msn/soap.c	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/soap.c	Sat Nov 03 05:12:43 2007 +0000
@@ -308,7 +308,7 @@
 }
 
 /*read the whole SOAP server response*/
-void 
+static void 
 msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	MsnSoapConn *soapconn = data;
--- a/libpurple/protocols/msn/soap.h	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/soap.h	Sat Nov 03 05:12:43 2007 +0000
@@ -154,7 +154,6 @@
 void msn_soap_free_read_buf(MsnSoapConn *soapconn);
 void msn_soap_free_write_buf(MsnSoapConn *soapconn);
 void msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
-void msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond);
 
 /*clean the unhandled requests*/
 void msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/soap2.c	Sat Nov 03 05:12:43 2007 +0000
@@ -0,0 +1,665 @@
+/**
+ * @file soap2.c
+ * 	C file for SOAP connection related process
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "soap2.h"
+
+#include "session.h"
+
+#include "debug.h"
+#include "xmlnode.h"
+
+#include <glib.h>
+#include <error.h>
+
+#define SOAP_TIMEOUT (5 * 60)
+
+typedef struct _MsnSoapRequest {
+	char *path;
+	MsnSoapMessage *message;
+	MsnSoapCallback cb;
+	gpointer cb_data;
+} MsnSoapRequest;
+
+typedef struct _MsnSoapConnection {
+	MsnSession *session;
+	char *host;
+
+	time_t last_used;
+	PurpleSslConnection *ssl;
+	gboolean connected;
+
+	guint event_handle;
+	GString *buf;
+	gsize handled_len;
+	gsize body_len;
+	int response_code;
+	gboolean headers_done;
+	gboolean close_when_done;
+
+	MsnSoapMessage *message;
+
+	GQueue *queue;
+	MsnSoapRequest *current_request;
+} MsnSoapConnection;
+
+static void msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data);
+static gboolean msn_soap_connection_run(gpointer data);
+
+static MsnSoapConnection *msn_soap_connection_new(MsnSession *session,
+	const char *host);
+static void msn_soap_connection_handle_next(MsnSoapConnection *conn);
+static void msn_soap_connection_destroy(MsnSoapConnection *conn);
+
+static void msn_soap_message_send_internal(MsnSession *session,
+	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_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect);
+
+static gboolean
+msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data)
+{
+	MsnSoapConnection *conn = value;
+	time_t *t = data;
+
+	if ((*t - conn->last_used) > SOAP_TIMEOUT * 2) {
+		purple_debug_info("soap", "cleaning up soap conn %p\n", conn);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+msn_soap_cleanup_for_session(gpointer data)
+{
+	MsnSession *sess = data;
+	time_t t = time(NULL);
+
+	purple_debug_info("soap", "session cleanup timeout\n");
+
+	if (sess->soap_table) {
+		g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each,
+			&t);
+
+		if (g_hash_table_size(sess->soap_table) == 0) {
+			purple_timeout_remove(sess->soap_cleanup_handle);
+			sess->soap_cleanup_handle = 0;
+		}
+	}
+
+	return TRUE;
+}
+
+static MsnSoapConnection *
+msn_soap_get_connection(MsnSession *session, const char *host)
+{
+	MsnSoapConnection *conn = NULL;
+
+	if (session->soap_table) {
+		conn = g_hash_table_lookup(session->soap_table, host);
+	} else {
+		session->soap_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+			NULL, (GDestroyNotify)msn_soap_connection_destroy);
+	}
+
+	if (session->soap_cleanup_handle == 0)
+		session->soap_cleanup_handle = purple_timeout_add(SOAP_TIMEOUT * 1000,
+			msn_soap_cleanup_for_session, session);
+
+	if (conn == NULL) {
+		conn = msn_soap_connection_new(session, host);
+		g_hash_table_insert(session->soap_table, conn->host, conn);
+	}
+
+	conn->last_used = time(NULL);
+
+	return conn;
+}
+
+static MsnSoapConnection *
+msn_soap_connection_new(MsnSession *session, const char *host)
+{
+	MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1);
+	conn->session = session;
+	conn->host = g_strdup(host);
+	conn->queue = g_queue_new();
+	return conn;
+}
+
+static void
+msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl,
+		PurpleInputCondition cond)
+{
+	MsnSoapConnection *conn = data;
+
+	conn->connected = TRUE;
+
+	if (conn->event_handle == 0)
+		conn->event_handle = g_idle_add(msn_soap_connection_run, conn);
+}
+
+static void
+msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error,
+		gpointer data)
+{
+	MsnSoapConnection *conn = data;
+
+	g_hash_table_remove(conn->session->soap_table, conn->host);
+}
+
+static gboolean
+msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url)
+{
+	char *c;
+
+	/* Skip the http:// */
+	if ((c = strchr(url, '/')) != NULL)
+		url += 2;
+
+	if ((c = strchr(url, '/')) != NULL) {
+		char *host, *path;
+
+		host = g_strndup(url, c - url);
+		path = g_strdup(c);
+
+		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);
+		conn->current_request = NULL;
+
+		g_free(host);
+		g_free(path);
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
+msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response)
+{
+	xmlnode *body = xmlnode_get_child(response->xml, "Body");
+
+	if (body) {
+		MsnSoapRequest *request;
+
+		if (strcmp(body->name, "Fault") == 0) {
+			xmlnode *fault = xmlnode_get_child(body, "faultcode");
+
+			if (fault != NULL) {
+				char *faultdata = xmlnode_get_data(fault);
+
+				if (strcmp(faultdata, "psf:Redirect") == 0) {
+					xmlnode *url = xmlnode_get_child(body, "redirectUrl");
+
+					if (url) {
+						char *urldata = xmlnode_get_data(url);
+						msn_soap_handle_redirect(conn, urldata);
+						g_free(urldata);
+					}
+
+					g_free(faultdata);
+					return TRUE;
+				} else if (strcmp(faultdata, "wsse:FailedAuthentication") == 0) {
+					xmlnode *reason = xmlnode_get_child(body, "faultstring");
+					char *reasondata = xmlnode_get_data(reason);
+
+					msn_soap_connection_sanitize(conn, TRUE);
+					msn_session_set_error(conn->session, MSN_ERROR_AUTH,
+						reasondata);
+
+					g_free(reasondata);
+					g_free(faultdata);
+					return FALSE;
+				}
+
+				g_free(faultdata);
+			}
+		}
+
+		request = conn->current_request;
+		conn->current_request = NULL;
+		request->cb(request->message, response,
+			request->cb_data);
+		msn_soap_request_destroy(request);
+	}
+
+	return TRUE;
+}
+
+static void
+msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
+{
+    MsnSoapConnection *conn = data;
+	int count;
+	char buf[8192];
+	char *linebreak;
+	char *cursor;
+	gboolean handled = FALSE;
+
+	g_return_if_fail(cond == PURPLE_INPUT_READ);
+
+	count = purple_ssl_read(conn->ssl, buf, sizeof(buf));
+	purple_debug_info("soap", "read %d bytes\n", count);
+	if (count < 0 && errno == EAGAIN)
+		return;
+	else if (count <= 0) {
+		purple_debug_info("soap", "read: %s\n", strerror(errno));
+		purple_ssl_close(conn->ssl);
+		conn->ssl = NULL;
+		msn_soap_connection_handle_next(conn);
+		return;
+	}
+
+	if (conn->message == NULL) {
+		conn->message = msn_soap_message_new(NULL, NULL);
+	}
+
+	if (conn->buf == NULL) {
+		conn->buf = g_string_new_len(buf, count);
+	} else {
+		g_string_append_len(conn->buf, buf, count);
+	}
+
+	purple_debug_info("soap", "current %s\n", conn->buf->str);
+
+	cursor = conn->buf->str + conn->handled_len;
+
+	if (!conn->headers_done) {
+		while ((linebreak = strstr(cursor, "\r\n"))	!= NULL) {
+			conn->handled_len = linebreak - conn->buf->str + 2;
+
+			if (conn->response_code == 0) {
+				if (sscanf(cursor, "HTTP/1.1 %d", &conn->response_code) != 1) {
+					/* something horribly wrong */
+					purple_ssl_close(conn->ssl);
+					conn->ssl = NULL;
+					msn_soap_connection_handle_next(conn);
+					handled = TRUE;
+					break;
+				} else if (conn->response_code == 503) {
+					msn_soap_connection_sanitize(conn, TRUE);
+					msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
+					return;
+				}
+			} else if (cursor == linebreak) {
+				/* blank line */
+				conn->headers_done = TRUE;
+				cursor = conn->buf->str + conn->handled_len;
+				break;
+			} else {
+				char *line = g_strndup(cursor, linebreak - cursor);
+				char *sep = strstr(line, ": ");
+				char *key = line;
+				char *value;
+
+				if (sep == NULL) {
+					purple_debug_info("soap", "ignoring malformed line: %s\n", line);
+					g_free(line);
+					goto loop_end;
+				}
+
+				value = sep + 2;
+				*sep = '\0';
+				msn_soap_message_add_header(conn->message, key, value);
+
+				if ((conn->response_code == 301 || conn->response_code == 300)
+					&& strcmp(key, "Location") == 0) {
+
+					msn_soap_handle_redirect(conn, value);
+
+					handled = TRUE;
+					g_free(line);
+					break;
+				} else if (conn->response_code == 401 &&
+					strcmp(key, "WWW-Authenticate") == 0) {
+					char *error = strstr(value, "cbtxt=");
+
+					if (error) {
+						error += strlen("cbtxt=");
+					}
+
+					msn_soap_connection_sanitize(conn, TRUE);
+					msn_session_set_error(conn->session, MSN_ERROR_AUTH,
+						error ? purple_url_decode(error) : NULL);
+
+					g_free(line);
+					return;
+				} else if (strcmp(key, "Content-Length") == 0) {
+					conn->body_len = atoi(value);
+				} else if (strcmp(key, "Connection") == 0) {
+					if (strcmp(value, "close") == 0) {
+						conn->close_when_done = TRUE;
+					}
+				}
+				g_free(line);
+			}
+
+		loop_end:
+			cursor = conn->buf->str + conn->handled_len;
+		}
+	}
+
+	if (!handled && conn->headers_done) {
+		if (conn->buf->len - conn->handled_len >= 
+			conn->body_len) {
+			xmlnode *node = xmlnode_from_str(cursor, conn->body_len);
+
+			if (node == NULL) {
+				purple_debug_info("soap", "Malformed SOAP response: %s\n",
+					cursor);
+			} else {
+				MsnSoapMessage *message = conn->message;
+				conn->message = NULL;
+				message->xml = node;
+
+				if (!msn_soap_handle_body(conn, message))
+					return;
+			}
+
+			msn_soap_connection_handle_next(conn);
+		}
+
+		return;
+	}
+
+	if (handled) {
+		msn_soap_connection_handle_next(conn);
+	}
+}
+
+static void
+msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond)
+{
+	MsnSoapConnection *conn = data;
+	int written;
+
+	g_return_if_fail(cond == PURPLE_INPUT_WRITE);
+
+	written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len,
+		conn->buf->len - conn->handled_len);
+
+	if (written < 0 && errno == EAGAIN)
+		return;
+	else if (written <= 0) {
+		purple_ssl_close(conn->ssl);
+		conn->ssl = NULL;
+		msn_soap_connection_handle_next(conn);
+		return;
+	}
+
+	conn->handled_len += written;
+
+	if (conn->handled_len < conn->buf->len)
+		return;
+
+	/* we are done! */
+	g_string_free(conn->buf, TRUE);
+	conn->buf = NULL;
+	conn->handled_len = 0;
+	conn->body_len = 0;
+	conn->response_code = 0;
+	conn->headers_done = FALSE;
+	conn->close_when_done = FALSE;
+
+	purple_input_remove(conn->event_handle);
+	conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ,
+		msn_soap_read_cb, conn);
+}
+
+static gboolean
+msn_soap_connection_run(gpointer data)
+{
+	MsnSoapConnection *conn = data;
+	MsnSoapRequest *req = g_queue_peek_head(conn->queue);
+
+	conn->event_handle = 0;
+
+	if (req) {
+		if (conn->ssl == NULL) {
+			conn->ssl = purple_ssl_connect(conn->session->account, conn->host,
+				443, msn_soap_connected_cb, msn_soap_error_cb, conn);
+		} else if (conn->connected) {
+			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"
+				"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"
+				"Content-Length: %d\r\n"
+				"Connection: Keep-Alive\r\n"
+				"Cache-Control: no-cache\r\n",
+				req->path, req->message->action ? req->message->action : "",
+				authstr ? authstr : "",	conn->host, len);
+
+			for (iter = req->message->headers; iter; iter = iter->next) {
+				g_string_append(conn->buf, (char *)iter->data);
+				g_string_append(conn->buf, "\r\n");
+			}
+
+			g_string_append(conn->buf, "\r\n");
+			g_string_append(conn->buf, body);
+
+			purple_debug_info("soap", "%s\n", conn->buf->str);
+
+			conn->handled_len = 0;
+			conn->current_request = req;
+
+			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);
+
+			g_free(authstr);
+			g_free(body);
+		}		
+	}
+
+	return FALSE;
+}
+
+void
+msn_soap_message_send(MsnSession *session, MsnSoapMessage *message,
+	const char *host, const char *path,
+	MsnSoapCallback cb, gpointer cb_data)
+{
+	msn_soap_message_send_internal(session, message, host, path, cb, cb_data,
+		FALSE);
+}
+
+static void
+msn_soap_message_send_internal(MsnSession *session,
+	MsnSoapMessage *message, const char *host, const char *path,
+	MsnSoapCallback cb, gpointer cb_data, gboolean first)
+{
+	MsnSoapConnection *conn = msn_soap_get_connection(session, host);
+	MsnSoapRequest *req = g_new0(MsnSoapRequest, 1);
+
+	req->path = g_strdup(path);
+	req->message = message;
+	req->cb = cb;
+	req->cb_data = cb_data;
+
+	if (first) {
+		g_queue_push_head(conn->queue, req);
+	} else {
+		g_queue_push_tail(conn->queue, req);
+	}
+
+	if (conn->event_handle == 0)
+		conn->event_handle = purple_timeout_add(0, msn_soap_connection_run,
+			conn);
+}
+
+static void
+msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect)
+{
+	if (conn->event_handle) {
+		purple_input_remove(conn->event_handle);
+		conn->event_handle = 0;
+	}
+
+	if (conn->message) {
+		msn_soap_message_destroy(conn->message);
+		conn->message = NULL;
+	}
+
+	if (conn->buf) {
+		g_string_free(conn->buf, TRUE);
+		conn->buf = NULL;
+	}
+
+	if (conn->ssl && (disconnect || conn->close_when_done)) {
+		purple_ssl_close(conn->ssl);
+		conn->ssl = NULL;
+	}
+
+	if (conn->current_request) {
+		msn_soap_request_destroy(conn->current_request);
+		conn->current_request = NULL;
+	}
+}
+
+static void
+msn_soap_connection_handle_next(MsnSoapConnection *conn)
+{
+	msn_soap_connection_sanitize(conn, FALSE);
+
+	conn->event_handle = purple_timeout_add(0, msn_soap_connection_run,	conn);
+
+	if (conn->current_request) {
+		MsnSoapRequest *req = conn->current_request;
+		conn->current_request = NULL;
+		msn_soap_connection_destroy_foreach_cb(req, conn);
+	}
+}
+
+static void
+msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data)
+{
+	MsnSoapRequest *req = item;
+
+	if (req->cb)
+		req->cb(req->message, NULL, req->cb_data);
+
+	msn_soap_request_destroy(req);
+}
+
+static void
+msn_soap_connection_destroy(MsnSoapConnection *conn)
+{
+	if (conn->current_request) {
+		MsnSoapRequest *req = conn->current_request;
+		conn->current_request = NULL;
+		msn_soap_connection_destroy_foreach_cb(req, conn);
+	}
+
+	msn_soap_connection_sanitize(conn, TRUE);
+	g_queue_foreach(conn->queue, msn_soap_connection_destroy_foreach_cb, conn);
+	g_queue_free(conn->queue);
+
+	g_free(conn->host);
+	g_free(conn);
+}
+
+MsnSoapMessage *
+msn_soap_message_new(const char *action, xmlnode *xml)
+{
+	MsnSoapMessage *message = g_new0(MsnSoapMessage, 1);
+
+	message->action = g_strdup(action);
+	message->xml = xml;
+
+	return message;
+}
+
+void
+msn_soap_message_destroy(MsnSoapMessage *message)
+{
+	if (message) {
+		g_slist_foreach(message->headers, (GFunc)g_free, NULL);
+		g_slist_free(message->headers);
+		g_free(message->action);
+		if (message->xml)
+			xmlnode_free(message->xml);
+		g_free(message);
+	}
+}
+
+void
+msn_soap_message_add_header(MsnSoapMessage *req,
+		const char *name, const char *value)
+{
+	char *header = g_strdup_printf("%s: %s\r\n", name, value);
+
+	req->headers = g_slist_prepend(req->headers, header);
+}
+
+static void
+msn_soap_request_destroy(MsnSoapRequest *req)
+{
+	g_free(req->path);
+	msn_soap_message_destroy(req->message);
+	g_free(req);
+}
+
+xmlnode *
+msn_soap_xml_get(xmlnode *parent, const char *node)
+{
+	xmlnode *ret;
+	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;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/soap2.h	Sat Nov 03 05:12:43 2007 +0000
@@ -0,0 +1,58 @@
+/**
+ * @file soap2.h
+ * 	header file for SOAP connection related process
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _MSN_SOAP2_H
+#define _MSN_SOAP2_H
+
+#include "session.h"
+#include "sslconn.h"
+#include "xmlnode.h"
+
+#include <glib.h>
+
+typedef struct _MsnSoapMessage MsnSoapMessage;
+typedef void (*MsnSoapCallback)(MsnSoapMessage *request,
+	MsnSoapMessage *response, gpointer cb_data);
+
+struct _MsnSoapMessage {
+	char *action;
+	xmlnode *xml;
+	GSList *headers;
+};
+
+MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *xml);
+
+void msn_soap_message_add_header(MsnSoapMessage *req,
+	const char *name, const char *value);
+
+void msn_soap_message_send(MsnSession *session,
+	MsnSoapMessage *message, const char *host, const char *path,
+	MsnSoapCallback cb, gpointer cb_data);
+
+void msn_soap_message_destroy(MsnSoapMessage *message);
+
+xmlnode *msn_soap_xml_get(xmlnode *parent, const char *node);
+
+#endif
--- a/libpurple/protocols/msn/userlist.c	Fri Nov 02 13:31:01 2007 +0000
+++ b/libpurple/protocols/msn/userlist.c	Sat Nov 03 05:12:43 2007 +0000
@@ -70,7 +70,7 @@
 	{
 		MsnSession *session = pa->gc->proto_data;
 		MsnUserList *userlist = session->userlist;
-		MsnCallbackState *state = msn_callback_state_new();
+		MsnCallbackState *state = msn_callback_state_new(session);
 
 		msn_callback_state_set_action(state, MSN_DENIED_BUDDY);
 
@@ -680,7 +680,7 @@
 	
 	purple_debug_info("MSN Userlist", "Add user: %s to group: %s\n", who, new_group_name);
 
-	state = msn_callback_state_new();
+	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);
 
@@ -841,7 +841,7 @@
 	g_return_if_fail(userlist->session != NULL);
 	g_return_if_fail(userlist->session->contact != NULL);
 
-	state = msn_callback_state_new();
+	state = msn_callback_state_new(userlist->session);
 	msn_callback_state_set_who(state, who);
 	msn_callback_state_set_action(state, MSN_MOVE_BUDDY);
 	msn_callback_state_set_old_group_name(state, old_group_name);