changeset 20517:2ebde8bd0e58

explicit merge of '7ca52e5d60c8f4d7842faad73378b57df9db9007' and 'b1148e355b30c726e5fdcc11dc57ddb0cc8736ea'
author Richard Laager <rlaager@wiktel.com>
date Sun, 16 Sep 2007 18:06:22 +0000
parents 97559afd70e4 (current diff) 5bc492d82ff4 (diff)
children 4b20ec37460f
files libpurple/protocols/msn/msg.c libpurple/protocols/msn/msn-utils.c libpurple/protocols/msn/msn-utils.h
diffstat 49 files changed, 7213 insertions(+), 1294 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Tue Sep 11 13:48:19 2007 +0000
+++ b/COPYRIGHT	Sun Sep 16 18:06:22 2007 +0000
@@ -277,6 +277,7 @@
 Eduardo Pérez
 Matt Perry
 Nathan Peterson
+Sebastián E. Peyrott
 Celso Pinto
 Joao Luís Marques Pinto
 Aleksander Piotrowski
--- a/libpurple/internal.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/internal.h	Sun Sep 16 18:06:22 2007 +0000
@@ -79,6 +79,7 @@
 #ifndef _WIN32
 #include <sys/time.h>
 #include <sys/wait.h>
+#include <sys/time.h>
 #endif
 #include <ctype.h>
 #include <errno.h>
--- a/libpurple/protocols/msn/Makefile.am	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/Makefile.am	Sun Sep 16 18:06:22 2007 +0000
@@ -1,4 +1,6 @@
 EXTRA_DIST = \
+		directconn.c \
+		directconn.h \
 		Makefile.mingw
 
 pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
@@ -8,10 +10,10 @@
 	cmdproc.h \
 	command.c \
 	command.h \
+	contact.c\
+	contact.h\
 	dialog.c \
 	dialog.h \
-	directconn.c \
-	directconn.h \
 	error.c \
 	error.h \
 	group.c \
@@ -30,6 +32,8 @@
 	notification.h \
 	object.c \
 	object.h \
+	oim.c\
+	oim.h\
 	page.c \
 	page.h \
 	servconn.c \
@@ -46,6 +50,8 @@
 	slpmsg.h \
 	slpsession.c \
 	slpsession.h \
+	soap.c\
+	soap.h\
 	state.c \
 	state.h \
 	switchboard.c \
@@ -60,8 +66,8 @@
 	user.h \
 	userlist.c \
 	userlist.h \
-	msn-utils.c \
-	msn-utils.h
+	msnutils.c \
+	msnutils.h
 
 AM_CFLAGS = $(st)
 
--- a/libpurple/protocols/msn/Makefile.mingw	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/Makefile.mingw	Sun Sep 16 18:06:22 2007 +0000
@@ -39,6 +39,7 @@
 ##
 C_SRC =			cmdproc.c \
 			command.c \
+			contact.c\
 			dialog.c \
 			directconn.c \
 			error.c \
@@ -50,6 +51,7 @@
 			nexus.c \
 			notification.c \
 			object.c \
+			oim.c\
 			page.c \
 			servconn.c \
 			session.c \
@@ -58,6 +60,7 @@
 			slplink.c \
 			slpmsg.c \
 			slpsession.c \
+			soap.c\
 			state.c \
 			switchboard.c \
 			sync.c \
@@ -65,7 +68,7 @@
 			transaction.c \
 			user.c \
 			userlist.c \
-			msn-utils.c
+			msnutils.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/README	Sun Sep 16 18:06:22 2007 +0000
@@ -0,0 +1,55 @@
+MSNP14 Implementation
+by Ma Yuan<mayuan2006@gmail.com>
+
+1. Introduction
+-------------
+
+MSNP14 Protocol, proposed by Windows Live Messenger, is new, and there is no available implementation except the official one on Windows Platform.
+
+It has introduced many new features attractable to many users, such as:
+* Offline Instant Message
+	You can send the offline Message to the offline User,
+	The message will be posted to that user the next time when he is online.
+
+* Communicate with Yahoo User
+	U can chat with the Yahoo User in MSN, That's Fantastic! Till now ,
+	you can send text/Nudge to Yahoo User.
+
+* Windows Live ID authentition
+	WLM use the Window Live ID Authentication process,Known as Passport 3.0,
+	The procedure is totally different to the previous Passport 2.0
+
+* Video/Audio Conversation
+	U can communicate with other's via Video/Audio.
+(Though very interesting, not implemented in this version)
+
+2.New Features Added
+-----------------
+
+Till now, This project has implemented the following Feature:
+* Windows Live ID authentication.
+
+* Offline Instant Message
+Now can send and receive the Offline Instant Message to MSN user and Yahoo User.
+
+*contact management
+Can add/delete Contact
+Can add/delete Group
+
+* Communicate with Yahoo User
+Can send/receive Message/Nudge to Yahoo User.
+
+*. Changes to made to fit MSNP14 Protocol
+
+3. Reference
+-------------
+
+The very useful sites of MSN Protocol:
+MSNpiki site:
+reverse engineer of MSN Protocol.up to dated.
+http://msnpiki.msnfanatic.com/index.php/MSN_Protocol_Version_13
+
+hypothetic site:
+old MSN Protocol Introduction,but very useful for basic idea of MSN protocol
+http://www.hypothetic.org/docs/msn/index.php
+
--- a/libpurple/protocols/msn/cmdproc.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/cmdproc.c	Sun Sep 16 18:06:22 2007 +0000
@@ -258,8 +258,10 @@
 		trans = msn_history_find(cmdproc->history, cmd->trId);
 
 	if (trans != NULL)
-		if (trans->timer)
+		if (trans->timer) {
 			purple_timeout_remove(trans->timer);
+			trans->timer = 0;
+		}
 
 	if (g_ascii_isdigit(cmd->command[0]))
 	{
--- a/libpurple/protocols/msn/command.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/command.c	Sun Sep 16 18:06:22 2007 +0000
@@ -36,6 +36,50 @@
 	return TRUE;
 }
 
+/*
+ * check the command is the command with payload content
+ *  if it is	return TRUE
+ *  else 		return FALSE
+ */
+static gboolean
+msn_check_payload_cmd(char *str)
+{
+	if( (!strcmp(str,"ADL")) ||
+		(!strcmp(str,"GCF")) ||
+		(!strcmp(str,"SG")) ||
+		(!strcmp(str,"MSG")) ||
+		(!strcmp(str,"RML")) ||
+		(!strcmp(str,"UBX")) ||
+		(!strcmp(str,"UBN")) ||
+		(!strcmp(str,"UUM")) ||
+		(!strcmp(str,"UBM")) ||
+		(!strcmp(str,"FQY")) ||
+		(!strcmp(str,"UUN")) ||
+		(!strcmp(str,"UUX")) ||
+		(is_num(str))){
+			return TRUE;
+		}
+
+	return FALSE;
+}
+
+/*
+ * set command Payload length
+ */
+static void
+msn_set_payload_len(MsnCommand *cmd)
+{
+	char *param;
+	int len = 0;
+
+	if (msn_check_payload_cmd(cmd->command) && (cmd->param_count > 0)){
+		param = cmd->params[cmd->param_count - 1];
+		len = is_num(param) ? atoi(param) : 0;
+	}
+
+	cmd->payload_len = len;
+}
+
 MsnCommand *
 msn_command_from_string(const char *string)
 {
@@ -70,7 +114,13 @@
 		cmd->trId = is_num(param) ? atoi(param) : 0;
 	}
 	else
+	{
 		cmd->trId = 0;
+	}
+
+	/*add payload Length checking*/
+	msn_set_payload_len(cmd);
+	purple_debug_info("MSNP14","get payload len:%d\n",cmd->payload_len);
 
 	msn_command_ref(cmd);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/contact.c	Sun Sep 16 18:06:22 2007 +0000
@@ -0,0 +1,1883 @@
+/**
+ * @file contact.c 
+ * 	get MSN contacts via SOAP request
+ *	created by MaYuan<mayuan2006@gmail.com>
+ *
+ * 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 "msn.h"
+#include "contact.h"
+#include "xmlnode.h"
+#include "group.h"
+
+const char *MsnSoapPartnerScenarioText[] =
+{
+        "Initial",
+        "ContactSave",
+	"MessengerPendingList",
+	"ContactMsgrAPI",
+	"BlockUnblock"
+};
+
+const char *MsnMemberRole[] =
+{
+	"Forward",
+	"Allow",
+	"Block",
+	"Reverse",
+	"Pending"
+};
+
+/* new a contact */
+MsnContact *
+msn_contact_new(MsnSession *session)
+{
+	MsnContact *contact;
+
+	contact = g_new0(MsnContact, 1);
+	contact->session = session;
+	contact->soapconn = msn_soap_new(session,contact,1);
+
+	return contact;
+}
+
+/* destroy the contact */
+void
+msn_contact_destroy(MsnContact *contact)
+{
+	msn_soap_destroy(contact->soapconn);
+	g_free(contact);
+}
+
+MsnCallbackState *
+msn_callback_state_new(void)
+{
+	return g_new0(MsnCallbackState, 1);
+}	
+
+void
+msn_callback_state_free(MsnCallbackState *state)
+{
+	if (state == NULL)
+		return;
+	
+	if (state->who != NULL)
+		g_free(state->who);
+	
+	if (state->old_group_name != NULL)
+		g_free(state->old_group_name);
+	
+	if (state->new_group_name != NULL)
+		g_free(state->new_group_name);
+	
+	if (state->guid != NULL)
+		g_free(state->guid);
+	
+	g_free(state);
+}
+	
+void
+msn_callback_state_set_who(MsnCallbackState *state, const gchar *who)
+{
+	gchar *new_str = NULL;
+
+	g_return_if_fail(state != NULL);
+
+	if (who != NULL)
+		new_str = g_strdup(who);
+	
+	if (state->who != NULL)
+		g_free(state->who);
+	
+	state->who = new_str;
+}
+
+void
+msn_callback_state_set_old_group_name(MsnCallbackState *state, const gchar *old_group_name)
+{
+	gchar *new_str = NULL;
+
+	g_return_if_fail(state != NULL);
+
+	if (old_group_name != NULL)
+		new_str = g_strdup(old_group_name);
+
+	if (state->old_group_name != NULL)
+		g_free(state->old_group_name);
+	
+	state->old_group_name = new_str;
+}
+
+void
+msn_callback_state_set_new_group_name(MsnCallbackState *state, const gchar *new_group_name)
+{
+	gchar *new_str = NULL;
+
+	g_return_if_fail(state != NULL);
+
+	if (new_group_name != NULL)
+		new_str = g_strdup(new_group_name);
+
+	if (state->new_group_name != NULL)
+		g_free(state->new_group_name);
+	
+	state->new_group_name = new_str;
+}
+
+void
+msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid)
+{
+	gchar *new_str = NULL;
+
+	g_return_if_fail(state != NULL);
+
+	if (guid != NULL)
+		new_str = g_strdup(guid);
+	
+	if (state->guid != NULL)
+		g_free(state->guid);
+	
+	state->guid = new_str;
+}
+
+
+void
+msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id)
+{
+	g_return_if_fail(state != NULL);
+	
+	state->list_id = list_id;
+}
+
+void
+msn_callback_state_set_action(MsnCallbackState *state, MsnCallbackAction action)
+{
+	g_return_if_fail(state != NULL);
+	
+	state->action |= action;
+}
+		       
+/*contact SOAP server login error*/
+static void
+msn_contact_login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+{
+	MsnSoapConn *soapconn = data;
+	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 void
+msn_contact_login_connect_cb(gpointer data, PurpleSslConnection *gsc,
+				 PurpleInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+	MsnSession * session;
+	MsnContact *contact;
+
+	contact = soapconn->parent;
+	g_return_if_fail(contact != NULL);
+
+	session = contact->session;
+	g_return_if_fail(session != NULL);
+
+	/*login ok!We can retrieve the contact list*/
+//	msn_get_contact_list(contact, MSN_PS_INITIAL, NULL);
+}
+
+/*get MSN member role utility*/
+static MsnListId
+msn_get_memberrole(char * role)
+{
+	
+	if (!strcmp(role,"Allow")) {
+		return MSN_LIST_AL;
+	} else if (!strcmp(role,"Block")) {
+		return MSN_LIST_BL;
+	} else if (!strcmp(role,"Reverse")) {
+		return MSN_LIST_RL;
+	} else if (!strcmp(role,"Pending")) {
+		return MSN_LIST_PL;
+	}
+	return 0;
+}
+
+/*get User Type*/
+static int 
+msn_get_user_type(char * type)
+{
+	if (!strcmp(type,"Regular")) {
+		return MSN_USER_TYPE_PASSPORT;
+	}
+	if (!strcmp(type,"Live")) {
+		return MSN_USER_TYPE_PASSPORT;
+	}
+	if (!strcmp(type,"LivePending")) {
+		return MSN_USER_TYPE_PASSPORT;
+	}
+
+	return MSN_USER_TYPE_UNKNOWN;
+}
+
+/* Create the AddressBook in the server, if we don't have one */
+static void
+msn_create_address_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+	MsnContact *contact;
+
+	if (soapconn->body == NULL)
+		return;
+
+	contact = soapconn->parent;
+	g_return_if_fail(contact != NULL);
+
+	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;
+}
+
+static void
+msn_create_address_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+
+	purple_debug_info("MSN AddressBook","AddressBookAdd written\n");
+	soapconn->read_cb = msn_create_address_cb;
+
+	return;
+}
+
+static void
+msn_create_address_book(MsnContact * contact)
+{
+	MsnSoapReq *soap_request;
+	gchar *body;
+
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(contact->session != NULL);
+	g_return_if_fail(contact->session->user != NULL);
+	g_return_if_fail(contact->session->user->passport != NULL);
+	
+	purple_debug_info("MSN AddressBook","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_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+	g_free(body);
+	
+	return;
+}
+
+/*parse contact list*/
+static void
+msn_parse_contact_list(MsnContact * contact)
+{
+	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 *LastChangeNode;
+	xmlnode *membershipnode, *members, *member, *passportNode;
+	char *LastChangeStr;
+
+	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);
+
+	purple_debug_misc("MSNCL","Root node @ %p: Name: '%s', child: '%s', lastchild: '%s'\n",node,node->name,node->child->name,node->lastchild->name);
+	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);
+			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);
+
+					if ( !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);
+
+	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 ( (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");
+			g_free(typedata);
+			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;
+		}
+
+		if ( !g_strcasecmp(typedata, "Messenger") ) {
+
+			/*Last Change Node*/
+			LastChangeNode = xmlnode_get_child(service, "LastChange");
+			LastChangeStr = xmlnode_get_data(LastChangeNode);
+			purple_debug_info("MSNCL","LastChangeNode: '%s'\n",LastChangeStr);	
+			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;
+
+				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,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;
+
+					purple_debug_info("MSNCL","Member type: %s\n", xmlnode_get_attrib(member,"type"));
+					
+					if( !g_strcasecmp(xmlnode_get_attrib(member,"type"), "PassportMember") ) {
+						passportNode = xmlnode_get_child(member,"PassportName");
+						passport = xmlnode_get_data(passportNode);
+						typeNode = xmlnode_get_child(member,"Type");
+						type = xmlnode_get_data(typeNode);
+						purple_debug_info("MSNCL","Passport name: '%s', Type: %s\n",passport,type);
+						g_free(type);
+
+						user = msn_userlist_find_add_user(session->userlist,passport,NULL);
+
+						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);
+
+						g_free(passport);
+					}
+					
+					if (!g_strcasecmp(xmlnode_get_attrib(member,"type"),"PhoneMember")) {
+					}
+					
+					if (!g_strcasecmp(xmlnode_get_attrib(member,"type"),"EmailMember")) {
+						xmlnode *emailNode;
+
+						emailNode = xmlnode_get_child(member,"Email");
+						passport = xmlnode_get_data(emailNode);
+						purple_debug_info("MSNCL","Email Member: Name: '%s', list_op: %d\n", passport, list_op);
+						user = msn_userlist_find_add_user(session->userlist, passport, NULL);
+						
+						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);
+						g_free(passport);
+					}
+				}
+			}
+			g_free(typedata);	/* Free 'Type' node data after processing 'Messenger' Service */
+		}
+	}
+
+	xmlnode_free(node);	/* Free the whole XML tree */
+}
+
+static void
+msn_get_contact_list_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+	MsnContact *contact;
+	MsnSession *session;
+	const char *abLastChange;
+	const char *dynamicItemLastChange;
+	gchar *partner_scenario;
+
+	if (soapconn->body == NULL)
+		return;
+
+	purple_debug_misc("MSNCL","Got the contact list!\n");
+
+	contact = soapconn->parent;
+	g_return_if_fail(contact != NULL);
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+	g_return_if_fail(soapconn->data_cb != NULL);
+
+	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);
+	}
+}
+
+static void
+msn_get_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_misc("MSNCL","Sent SOAP request for the contact list.\n");
+	soapconn->read_cb = msn_get_contact_list_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+/*SOAP  get contact list*/
+void
+msn_get_contact_list(MsnContact * contact, const MsnSoapPartnerScenario partner_scenario, const char *update_time)
+{
+	MsnSoapReq *soap_request;
+	gchar *body = NULL;
+	gchar * update_str;
+	const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario];
+
+	purple_debug_misc("MSNCL","Getting Contact List.\n");
+
+	if ( update_time != NULL ) {
+		purple_debug_info("MSNCL","Last update time: %s\n",update_time);
+		update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML,update_time);
+	} else {
+		update_str = g_strdup("");
+	}
+
+	body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_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_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	g_free(body);
+}
+
+static void
+msn_parse_addressbook_groups(MsnContact *contact, xmlnode *node)
+{
+	MsnSession *session = contact->session;
+	xmlnode *group;
+
+	purple_debug_info("MsnAb","msn_parse_addressbook_groups()\n");
+
+	for(group = xmlnode_get_child(node, "Group"); group;
+					group = xmlnode_get_next_twin(group)){
+		xmlnode *groupId, *groupInfo, *groupname;
+		char *group_id, *group_name;
+
+		groupId = xmlnode_get_child(group,"groupId");
+		group_id = xmlnode_get_data(groupId);
+		groupInfo = xmlnode_get_child(group,"groupInfo");
+		groupname = xmlnode_get_child(groupInfo,"name");
+		group_name = xmlnode_get_data(groupname);
+
+		msn_group_new(session->userlist, group_id, group_name);
+
+		if (group_id == NULL){
+			/* Group of ungroupped buddies */
+			g_free(group_name);
+			continue;
+		}
+
+		purple_debug_info("MsnAB","group_id: %s, name: %s\n",group_id,group_name);
+		if ((purple_find_group(group_name)) == NULL){
+			PurpleGroup *g = purple_group_new(group_name);
+			purple_blist_add_group(g, NULL);
+		}
+		g_free(group_id);
+		g_free(group_name);
+	}
+}
+
+static void
+msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
+{
+	MsnSession *session = contact->session;
+	xmlnode *contactNode;
+
+	for(contactNode = xmlnode_get_child(node, "Contact"); contactNode;
+				contactNode = xmlnode_get_next_twin(contactNode)){
+		xmlnode *contactId,*contactInfo,*contactType,*passportName,*displayName,*guid;
+		xmlnode *groupIds;
+		MsnUser *user;
+		MsnUserType usertype;
+		char *passport = NULL, *Name = NULL, *uid = NULL, *type = NULL;
+
+		contactId= xmlnode_get_child(contactNode,"contactId");
+		uid = xmlnode_get_data(contactId);
+
+		contactInfo = xmlnode_get_child(contactNode,"contactInfo");
+		contactType = xmlnode_get_child(contactInfo,"contactType");
+		type = xmlnode_get_data(contactType);
+
+		/*setup the Display Name*/
+		if (!strcmp(type, "Me")){
+			char *friendly;
+			friendly = xmlnode_get_data(xmlnode_get_child(contactInfo, "displayName"));
+			purple_connection_set_display_name(session->account->gc, purple_url_decode(friendly));
+			g_free(friendly);
+			g_free(uid);
+			g_free(type);
+			continue; /* Not adding own account as buddy to buddylist */
+		}
+		usertype = msn_get_user_type(type);
+		passportName = xmlnode_get_child(contactInfo,"passportName");
+		if (passportName == NULL) {
+			xmlnode *emailsNode, *contactEmailNode, *emailNode;
+			xmlnode *messengerEnabledNode;
+			char *msnEnabled;
+
+			/*TODO: add it to the none-instant Messenger group and recognize as email Membership*/
+			/*Yahoo User?*/
+			emailsNode = xmlnode_get_child(contactInfo,"emails");
+			if (emailsNode == NULL) {
+				/*TODO:  need to support the Mobile type*/
+				g_free(uid);
+				g_free(type);
+				continue;
+			}
+			for(contactEmailNode = xmlnode_get_child(emailsNode,"ContactEmail");contactEmailNode;
+					contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){
+				messengerEnabledNode = xmlnode_get_child(contactEmailNode,"isMessengerEnabled");
+				if(messengerEnabledNode == NULL){
+					g_free(uid);
+					g_free(type);
+					break;
+				}
+				msnEnabled = xmlnode_get_data(messengerEnabledNode);
+				if(!strcmp(msnEnabled,"true")){
+					/*Messenger enabled, Get the Passport*/
+					emailNode = xmlnode_get_child(contactEmailNode,"email");
+					passport = xmlnode_get_data(emailNode);
+					purple_debug_info("MsnAB","Yahoo User %s\n",passport);
+					usertype = MSN_USER_TYPE_YAHOO;
+					g_free(uid);
+					g_free(type);
+					g_free(passport);
+					g_free(msnEnabled);
+					break;
+				}else{
+					/*TODO maybe we can just ignore it in Purple?*/
+					emailNode = xmlnode_get_child(contactEmailNode,"email");
+					passport = xmlnode_get_data(emailNode);
+					purple_debug_info("MSNAB","Other type user\n");
+				}
+				g_free(msnEnabled);
+			}
+		} else {
+			passport = xmlnode_get_data(passportName);
+		}
+
+		if (passport == NULL) {
+			g_free(uid);
+			g_free(type);
+			continue;
+		}
+
+		displayName = xmlnode_get_child(contactInfo,"displayName");
+		if (displayName == NULL) {
+			Name = g_strdup(passport);
+		} else {
+			Name = xmlnode_get_data(displayName);
+		}
+
+		purple_debug_misc("MsnAB","passport:{%s} uid:{%s} display:{%s}\n",
+						passport,uid,Name);
+
+		user = msn_userlist_find_add_user(session->userlist, passport,Name);
+		msn_user_set_uid(user,uid);
+		msn_user_set_type(user, usertype);
+		g_free(Name);
+		g_free(passport);
+		g_free(uid);
+		g_free(type);
+
+		purple_debug_misc("MsnAB","parse guid...\n");
+		groupIds = xmlnode_get_child(contactInfo,"groupIds");
+		if (groupIds) {
+			for (guid = xmlnode_get_child(groupIds, "guid");guid;
+							guid = xmlnode_get_next_twin(guid)){
+				char *group_id;
+				group_id = xmlnode_get_data(guid);
+				msn_user_add_group_id(user,group_id);
+				purple_debug_misc("MsnAB","guid:%s\n",group_id);
+				g_free(group_id);
+			}
+		} else {
+			/*not in any group,Then set default group*/
+			msn_user_add_group_id(user, MSN_INDIVIDUALS_GROUP_ID);
+		}
+
+		msn_got_lst_user(session, user, MSN_LIST_FL_OP, NULL);
+	}
+}
+
+static gboolean
+msn_parse_addressbook(MsnContact * contact)
+{
+	MsnSession * session;
+	xmlnode * node,*body,*response,*result;
+	xmlnode *groups;
+	xmlnode	*contacts;
+	xmlnode *abNode;
+	xmlnode *fault, *faultstringnode, *faultdetail, *errorcode;
+
+	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);
+		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->child->name,node->lastchild->name);
+	
+	body = xmlnode_get_child(node,"Body");
+	purple_debug_misc("MSN AddressBook","body{%p},name:%s\n",body,body->name);
+	
+	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);
+			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);
+						
+				if ( !strncmp(errorcodestring, "ABDoesNotExist", 14) ) {
+					g_free(errorcodestring);
+					xmlnode_free(node);
+					return TRUE;
+				}
+				g_free(errorcodestring);
+			}
+		}
+		xmlnode_free(node);
+		return FALSE;
+	}
+
+
+	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);
+
+	/*Process Group List*/
+	groups = xmlnode_get_child(result,"groups");
+	if (groups != NULL) {
+		msn_parse_addressbook_groups(contact, groups);
+	}
+
+	/*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",
+					  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);
+		purple_blist_add_group(g, NULL);
+	}
+
+	/*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);
+	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);
+	}
+
+	/*Process contact List*/
+	purple_debug_info("MSNAB","process contact list...\n");
+	contacts =xmlnode_get_child(result,"contacts");
+	if (contacts != NULL) {
+		msn_parse_addressbook_contacts(contact, contacts);
+	}
+
+	abNode =xmlnode_get_child(result,"ab");
+	if(abNode != NULL){
+		xmlnode *LastChangeNode, *DynamicItemLastChangedNode;
+		char *lastchange, *dynamicChange;
+
+		LastChangeNode = xmlnode_get_child(abNode,"lastChange");
+		lastchange = xmlnode_get_data(LastChangeNode);
+		purple_debug_info("MsnAB"," lastchanged Time:{%s}\n",lastchange);
+		purple_account_set_string(session->account, "ablastChange", lastchange);
+
+		DynamicItemLastChangedNode = xmlnode_get_child(abNode,"DynamicItemLastChanged");
+		dynamicChange = xmlnode_get_data(DynamicItemLastChangedNode);
+		purple_debug_info("MsnAB"," DynamicItemLastChanged :{%s}\n",dynamicChange);
+		purple_account_set_string(session->account, "DynamicItemLastChanged", lastchange);
+		g_free(dynamicChange);
+		g_free(lastchange);
+	}
+
+	xmlnode_free(node);
+	msn_soap_free_read_buf(contact->soapconn);
+	return TRUE;
+}
+
+static void
+msn_get_address_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+	MsnContact *contact;
+	MsnSession *session;
+
+	if (soapconn->body == NULL)
+		return;
+
+	contact = soapconn->parent;
+	g_return_if_fail(contact != NULL);
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+	purple_debug_misc("MSN AddressBook", "Got the Address Book!\n");
+
+	if ( msn_parse_addressbook(contact) ) {
+		//msn_soap_free_read_buf(soapconn);
+
+		if (!session->logged_in) {
+			msn_send_privacy(session->account->gc);
+			msn_notification_dump_contact(session);
+		}
+	} 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)
+		*/
+		/*
+		msn_get_address_book(contact, NULL, NULL);
+		*/
+		msn_session_disconnect(session);
+		purple_connection_error(session->account->gc, _("Unable to retrieve MSN Address Book"));
+	}
+
+	/*free the read buffer*/
+	msn_soap_free_read_buf(soapconn);
+}
+
+/**/
+static void
+msn_address_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	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)
+{
+	MsnSoapReq *soap_request;
+	char *body = NULL;
+	char *ab_update_str,*update_str;
+
+	purple_debug_misc("MSN AddressBook","Getting Address Book\n");
+
+	/*build SOAP and POST it*/
+	if ( LastChanged != NULL ) {
+		ab_update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML,LastChanged);
+	} else {
+		ab_update_str = g_strdup("");
+	}
+	if ( dynamicItemLastChange != NULL ) {
+		update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML,
+									 dynamicItemLastChange);
+	} else {
+		update_str = g_strdup(ab_update_str);
+	}
+	g_free(ab_update_str);
+	
+
+	body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], 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_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	g_free(body);
+}
+
+static void
+msn_add_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state = NULL;
+	MsnUserList *userlist;
+	MsnUser *user;
+	
+	g_return_if_fail(soapconn->data_cb != NULL);
+	g_return_if_fail(soapconn->session != NULL);
+	g_return_if_fail(soapconn->session->userlist != NULL);
+
+	state = (MsnCallbackState *) soapconn->data_cb;
+
+	if (soapconn->body == NULL) {
+		msn_callback_state_free(state);
+		return;
+	}
+	
+	userlist = soapconn->session->userlist;
+	
+	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);
+
+	user = msn_userlist_find_add_user(userlist, state->who, state->who);
+	msn_user_add_group_id(user, state->guid);
+
+	if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
+		msn_del_contact_from_list(soapconn->session->contact, NULL, state->who, MSN_LIST_PL);
+	} else {
+		msn_soap_free_read_buf(soapconn);
+	}
+	
+	msn_callback_state_free(state);
+}
+
+static void
+msn_add_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MSNCL","Add contact request written\n");
+	soapconn->read_cb = msn_add_contact_read_cb;
+}
+
+/* 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;
+	gchar *soap_action;
+/*	gchar *escaped_displayname;
+
+
+	 if (displayname != NULL) {
+		escaped_displayname = g_markup_decode_text(displayname, -1);
+	 } else {
+		escaped_displayname = passport;
+	 }
+	contact_xml = g_strdup_printf(MSN_XML_ADD_CONTACT, escaped_displayname, passport);
+*/
+	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);
+//	}
+	body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE, contact_xml);
+
+	g_free(contact_xml);
+
+	/*build SOAP and POST it*/
+	soap_action = g_strdup(MSN_CONTACT_ADD_SOAP_ACTION);
+
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_ADDRESS_BOOK_POST_URL,
+					soap_action,
+					body,
+					state,
+					msn_add_contact_read_cb,
+					msn_add_contact_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+	g_free(soap_action);
+	g_free(body);
+}
+
+static void
+msn_add_contact_to_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state; 
+	MsnUserList *userlist;
+
+	g_return_if_fail(soapconn->data_cb != NULL);
+	g_return_if_fail(soapconn->session != NULL);
+	g_return_if_fail(soapconn->session->userlist != NULL);
+
+	userlist = soapconn->session->userlist;
+
+	state = (MsnCallbackState *) soapconn->data_cb;
+
+	if (soapconn->body == NULL) {
+		msn_callback_state_free(state);
+		return;
+	}
+	
+	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 (state->action & MSN_ADD_BUDDY) {
+		MsnUser *user = msn_userlist_find_user(userlist, state->who);
+
+        	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 (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
+			msn_del_contact_from_list(soapconn->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(soapconn->session->contact, state->who, state->old_group_name);
+	} else {
+		msn_callback_state_free(state);
+		msn_soap_free_read_buf(soapconn);
+	}
+}
+
+static void
+msn_add_contact_to_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MSNCL","Add contact to group request sent!\n");
+	soapconn->read_cb = msn_add_contact_to_group_read_cb;
+}
+
+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, *soap_action, *contact_xml;
+
+	g_return_if_fail(passport != NULL);
+	g_return_if_fail(groupId != NULL);
+
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(contact->session != NULL);
+	g_return_if_fail(contact->session->userlist != NULL);
+	
+	userlist = contact->session->userlist;
+
+	if (!strcmp(groupId, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(groupId, MSN_NON_IM_GROUP_ID)) {
+		
+		user = msn_userlist_find_add_user(userlist, passport, passport);
+
+		if (state->action & MSN_ADD_BUDDY) {
+			msn_add_contact(contact, state, passport);
+			return;
+		}
+
+		if (state->action & MSN_MOVE_BUDDY) {
+			msn_user_add_group_id(user, groupId);
+			msn_del_contact_from_group(contact, passport, state->old_group_name);
+		} else {
+			msn_callback_state_free(state);
+		}
+
+		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);
+	}
+
+	if (user->uid != NULL) {
+		contact_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
+	} else {
+		contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
+	}
+
+	body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE, groupId, contact_xml);
+	g_free(contact_xml);
+
+	/*build SOAP and POST it*/
+	soap_action = g_strdup(MSN_ADD_CONTACT_GROUP_SOAP_ACTION);
+
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_ADDRESS_BOOK_POST_URL,
+					soap_action,
+					body,
+					state,
+					msn_add_contact_to_group_read_cb,
+					msn_add_contact_to_group_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+	g_free(soap_action);
+	g_free(body);
+}
+
+
+
+static void
+msn_delete_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+
+	if (soapconn->body == NULL)
+		return;
+
+	// we should probably delete it from the userlist aswell
+	purple_debug_info("MSNCL","Delete contact successful\n");
+	msn_soap_free_read_buf(soapconn);
+}
+
+static void
+msn_delete_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MSNCL","Delete contact request written\n");
+	soapconn->read_cb = msn_delete_contact_read_cb;
+}
+
+/*delete a Contact*/
+void
+msn_delete_contact(MsnContact *contact, const char *contactId)
+{	
+	gchar *body = NULL;
+	gchar *contact_id_xml = NULL ;
+	MsnSoapReq *soap_request;
+
+	g_return_if_fail(contactId != NULL);
+	contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, 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,
+					NULL,
+					msn_delete_contact_read_cb,
+					msn_delete_contact_written_cb);
+
+	g_free(contact_id_xml);
+
+	/* POST the SOAP request */
+	msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+	g_free(body);
+}
+
+static void
+msn_del_contact_from_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state = (MsnCallbackState *) soapconn->data_cb;
+
+	if (soapconn->body == NULL) {
+		msn_callback_state_free(state);
+		return;
+	}
+	
+	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);
+	}
+	
+	msn_callback_state_free(state);
+	msn_soap_free_read_buf(soapconn);
+	return;
+}
+
+static void
+msn_del_contact_from_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+	
+	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, *soap_action, *contact_id_xml;
+	const gchar *groupId;
+	
+	g_return_if_fail(passport != NULL);
+	g_return_if_fail(group_name != NULL);
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(contact->session != NULL);
+	g_return_if_fail(contact->session->userlist != NULL);
+	
+	userlist = contact->session->userlist;
+	
+	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);
+	} else {
+		purple_debug_warning("MSN CL", "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);
+		return;
+	}
+
+	if ( !strcmp(groupId, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(groupId, MSN_NON_IM_GROUP_ID)) {
+		msn_user_remove_group_id(user, groupId);
+		return;
+	}
+
+	state = msn_callback_state_new();
+	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);
+	g_free(contact_id_xml);
+	
+	/*build SOAP and POST it*/
+	soap_action = g_strdup(MSN_CONTACT_DEL_GROUP_SOAP_ACTION);
+	
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					    MSN_ADDRESS_BOOK_POST_URL,
+					    soap_action,
+					    body,
+					    state,
+					    msn_del_contact_from_group_read_cb,
+					    msn_del_contact_from_group_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	
+	g_free(soap_action);
+	g_free(body);
+}
+
+
+static void
+msn_update_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+
+	if (soapconn->body == NULL)
+		return;
+
+	purple_debug_info("MSN CL","Contact updated successfully\n");
+}
+
+static void
+msn_update_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MSN CL","Update contact information request sent\n");
+	soapconn->read_cb = msn_update_contact_read_cb;
+}
+
+/* Update a contact's nickname */
+
+void
+msn_update_contact(MsnContact *contact, const char* nickname)
+{
+	MsnSoapReq *soap_request;
+	gchar *body = NULL, *escaped_nickname;
+
+	purple_debug_info("MSN CL","Update contact information with new friendly name: %s\n", nickname);
+	
+	escaped_nickname = g_markup_escape_text(nickname, -1);
+
+	body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, escaped_nickname);
+	
+	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_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+	g_free(body);
+}
+
+
+static void
+msn_del_contact_from_list_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state = NULL;
+
+	g_return_if_fail(soapconn->data_cb != NULL);
+	g_return_if_fail(soapconn->session != NULL);
+	g_return_if_fail(soapconn->session->contact != NULL);
+
+	state = (MsnCallbackState *) soapconn->data_cb;
+
+	if (soapconn->body == NULL) {
+		msn_callback_state_free(state);
+		return;
+	}
+	
+	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) {
+		msn_add_contact_to_list(soapconn->session->contact, state, state->who, MSN_LIST_RL);
+		return;
+	}
+
+	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;
+	}
+
+	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;
+	}
+
+	msn_callback_state_free(state);
+	msn_soap_free_read_buf(soapconn);
+}
+
+static void
+msn_del_contact_from_list_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+
+	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;
+
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(passport != NULL);
+	g_return_if_fail(list < 5);
+
+	purple_debug_info("MSN CL", "Deleting contact %s from %s list\n", passport, MsnMemberRole[list]);
+
+	if (state == NULL) {
+		state = msn_callback_state_new();
+	}
+	msn_callback_state_set_list_id(state, list);
+	msn_callback_state_set_who(state, passport);
+
+	if (list == MSN_LIST_PL) {
+		g_return_if_fail(contact->session != NULL);
+		g_return_if_fail(contact->session->userlist != NULL);
+
+		user = msn_userlist_find_user(contact->session->userlist, passport);
+
+		partner_scenario = MSN_PS_CONTACT_API;
+		member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, user->membership_id[MSN_LIST_PL]);
+	} else {
+		/* list == MSN_LIST_AL || list == MSN_LIST_BL */
+		partner_scenario = MSN_PS_BLOCK_UNBLOCK;
+		
+		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
+	}
+
+	body = g_strdup_printf( MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE,
+			        MsnSoapPartnerScenarioText[partner_scenario],
+			        MsnMemberRole[list],
+			        member);
+	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_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	
+	g_free(body);
+}
+
+static void
+msn_add_contact_to_list_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state = NULL;
+
+	g_return_if_fail(soapconn->data_cb != NULL);
+
+	state = (MsnCallbackState *) soapconn->data_cb;
+	
+	if (soapconn->body == NULL) {
+		msn_callback_state_free(state);
+		return;
+	}
+	
+	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 && (state->action & MSN_DENIED_BUDDY) ) {
+		g_return_if_fail(soapconn->session != NULL);
+		g_return_if_fail(soapconn->session->contact != NULL);
+
+		msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_BL);
+		return;
+	}
+
+	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);
+}
+
+
+static void
+msn_add_contact_to_list_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+
+	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;
+
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(passport != NULL);
+	g_return_if_fail(list < 5);
+
+	purple_debug_info("MSN CL", "Adding contact %s to %s list\n", passport, MsnMemberRole[list]);
+
+	if (state == NULL) {
+		state = msn_callback_state_new();
+	}
+	msn_callback_state_set_list_id(state, list);
+	msn_callback_state_set_who(state, passport);
+
+	partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK;
+
+	member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
+
+	body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE, 
+			       MsnSoapPartnerScenarioText[partner_scenario],
+			       MsnMemberRole[list], 
+			       member);
+
+	g_free(member);
+
+	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_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+	g_free(body);
+}
+
+
+#if 0
+static void
+msn_gleams_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	purple_debug_info("MSNP14","Gleams read done\n");
+}
+
+static void
+msn_gleams_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MSNP14","finish Group written\n");
+	soapconn->read_cb = msn_gleams_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+/*get the gleams info*/
+void
+msn_get_gleams(MsnContact *contact)
+{
+	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_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+}
+#endif
+
+
+/***************************************************************
+ * Group Operations
+ ***************************************************************/
+
+static void
+msn_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnUserList *userlist;
+	MsnCallbackState *state = NULL;
+	
+	purple_debug_info("MSN CL", "Group request successful.\n");
+	
+	g_return_if_fail(soapconn->session != NULL);
+	g_return_if_fail(soapconn->session->userlist != NULL);
+	g_return_if_fail(soapconn->session->contact != NULL);
+
+	state = (MsnCallbackState *) soapconn->data_cb;
+	
+	if (soapconn->body == NULL) {
+		msn_callback_state_free(state);
+		return;
+	}
+	
+	if (state) {
+		userlist = soapconn->session->userlist;
+		
+		if (state->action & MSN_RENAME_GROUP) {
+			msn_userlist_rename_group_id(soapconn->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);
+
+			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;
+			}
+			
+			if (state->action & MSN_MOVE_BUDDY) {
+				msn_add_contact_to_group(soapconn->session->contact, state, state->who, guid); 
+				return;
+			}
+		}
+		
+		if (state->action & MSN_DEL_GROUP) {
+			GList *l;
+			
+			msn_userlist_remove_group_id(soapconn->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);
+}
+
+static void
+msn_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	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);
+
+	if (state == NULL) {
+		state = msn_callback_state_new();
+	}
+
+	msn_callback_state_set_action(state, MSN_ADD_GROUP);
+	msn_callback_state_set_new_group_name(state, group_name);
+
+	/* 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);
+
+	/*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_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	
+	g_free(body);
+}
+
+/* delete group */
+void
+msn_del_group(MsnSession *session, const gchar *group_name)
+{
+	MsnSoapReq *soap_request;
+	MsnContact *contact;
+	MsnCallbackState *state;
+	char *body = NULL;
+	const gchar *guid;
+
+	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);
+	
+	guid = msn_userlist_find_group_id(session->userlist, group_name);
+	
+	/* if group uid we need to del is NULL, 
+	*  we need to delete nothing
+	*/
+	if (guid == NULL) {
+		purple_debug_info("MSN CL", "Group %s guid not found, returning.\n", group_name);
+		return;
+	}
+
+	if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) {
+		// XXX add back PurpleGroup since it isn't really removed in the server?
+		return;
+	}
+
+	state = msn_callback_state_new();
+	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_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+	g_free(body);
+}
+
+/* rename group */
+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;
+	const gchar * guid;
+	MsnCallbackState *state = msn_callback_state_new();
+	
+	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;
+
+	msn_callback_state_set_guid(state, guid);
+	msn_callback_state_set_new_group_name(state, new_group_name);
+
+	if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) {
+		msn_add_group(session, state, new_group_name);
+		// XXX move every buddy there (we probably need to fix concurrent SOAP reqs first)
+	}
+
+	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);
+	
+	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_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+	
+	g_free(escaped_group_name);
+	g_free(body);
+}
+
+void
+msn_contact_connect_init(MsnSoapConn *soapconn)
+{
+	msn_soap_init(soapconn, MSN_CONTACT_SERVER, 1,
+					msn_contact_login_connect_cb,
+					msn_contact_login_error_cb);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/contact.h	Sun Sep 16 18:06:22 2007 +0000
@@ -0,0 +1,446 @@
+/**
+ * @file contact.h			Header file for contact.c
+ *	Author
+ * 		MaYuan<mayuan2006@gmail.com>
+ * 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_CONTACT_H_
+#define _MSN_CONTACT_H_
+
+#define MSN_CONTACT_SERVER	"omega.contacts.msn.com"
+
+/* Get Contact List */
+
+#define MSN_GET_CONTACT_POST_URL	"/abservice/SharingService.asmx"
+#define MSN_GET_CONTACT_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/FindMembership"
+#define MSN_GET_CONTACT_UPDATE_XML "<View>Full</View>"\
+	"<deltasOnly>true</deltasOnly>"\
+	"<lastChange>%s</lastChange>"
+#define MSN_GET_CONTACT_TEMPLATE	"<?xml version='1.0' encoding='utf-8'?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+	"<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration xmlns=\"http://www.msn.com/webservices/AddressBook\">false</IsMigration>"\
+			"<PartnerScenario xmlns=\"http://www.msn.com/webservices/AddressBook\">%s</PartnerScenario>"\
+		 "</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest xmlns=\"http://www.msn.com/webservices/AddressBook\">false</ManagedGroupRequest>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+		"<FindMembership xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<serviceFilter xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+				"<Types xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+					"<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Messenger</ServiceType>"\
+					"<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Invitation</ServiceType>"\
+					"<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">SocialNetwork</ServiceType>"\
+					"<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Space</ServiceType>"\
+					"<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Profile</ServiceType>"\
+				"</Types>"\
+			"</serviceFilter>"\
+			"%s"\
+		"</FindMembership>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+/************************************************
+ * Address Book SOAP
+ * *********************************************/
+
+#define MSN_ADDRESS_BOOK_POST_URL	"/abservice/abservice.asmx"
+
+/* Create AddressBook template */
+#define MSN_ADD_ADDRESSBOOK_SOAP_ACTION     "http://www.msn.com/webservices/AddressBook/ABAdd"
+
+#define MSN_ADD_ADDRESSBOOK_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>Initial</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abInfo>"\
+				"<name/>"\
+				"<ownerPuid>0</ownerPuid>"\
+				"<ownerEmail>%s</ownerEmail>"\
+				"<fDefault>true</fDefault>"\
+			"</abInfo>"\
+		"</ABAdd>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+/* Get AddressBook */
+#define MSN_GET_ADDRESS_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABFindAll"
+#define MSN_GET_ADDRESS_FULL_TIME	"0001-01-01T00:00:00.0000000-08:00"
+#define MSN_GET_ADDRESS_UPDATE_XML "<deltasOnly>true</deltasOnly>"\
+	"<lastChange>%s</lastChange>"
+
+#define MSN_GET_GLEAM_UPDATE_XML \
+	"%s"\
+	"<dynamicItemView>Gleam</dynamicItemView>"\
+	"<dynamicItemLastChange>%s</dynamicItemLastChange>"
+
+#define MSN_GET_ADDRESS_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>%s</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<abView>Full</abView>"\
+			"%s"\
+		"</ABFindAll>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+
+/*Gleams SOAP request template*/
+#define MSN_GET_GLEAMS_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll"
+#define MSN_GLEAMS_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>Initial</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<abView>Full</abView>"\
+			"<dynamicItemView>Gleam</dynamicItemView>"\
+			"<dynamicItemLastChange>0001-01-01T00:00:00.0000000-08:00</dynamicItemLastChange>"\
+		"</ABFindAll>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+
+/*******************************************************
+ * Contact Management SOAP actions
+ *******************************************************/
+
+/* Add a new contact t*/
+#define MSN_CONTACT_ADD_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactAdd"
+#define MSN_CONTACT_LIVE_PENDING_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><contactType>LivePending</contactType><passportName>%s</passportName><isMessengerUser>true</isMessengerUser></contactInfo></Contact>"
+
+#define MSN_CONTACT_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+				"<contactInfo>"\
+					"<passportName>%s</passportName>"\
+					"<isSmtp>false</isSmtp>"\
+					"<isMessengerUser>true</isMessengerUser>"\
+				"</contactInfo>"\
+			"</Contact>"
+
+#define MSN_CONTACT_DISPLAYNAME_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><displayName>%s</displayName><passportName>%s</passportName><isMessengerUser>true</isMessengerUser></contactInfo></Contact>"
+
+#define MSN_ADD_CONTACT_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>ContactSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABContactAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts><options><EnableAllowListManagement>true</EnableAllowListManagement></options></ABContactAdd></soap:Body></soap:Envelope>"
+
+/* Add a contact to a group */
+#define MSN_ADD_CONTACT_GROUP_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupContactAdd"
+#define MSN_ADD_CONTACT_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>ContactSave</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABGroupContactAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<groupFilter>"\
+				"<groupIds>"\
+					"<guid>%s</guid>"\
+				"</groupIds>"\
+			"</groupFilter>"\
+			"<contacts>%s</contacts>"\
+			"<groupContactAddOptions>"\
+				"<fGenerateMissingQuickName>true</fGenerateMissingQuickName>"\
+				"<EnableAllowListManagement>true</EnableAllowListManagement>"\
+			"</groupContactAddOptions>"\
+		"</ABGroupContactAdd>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+/* Delete a contact from the Contact List */
+#define MSN_CONTACT_DEL_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactDelete"
+#define MSN_CONTACT_ID_XML		"<Contact><contactId>%s</contactId></Contact>"
+#define MSN_DEL_CONTACT_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts></ABContactDelete></soap:Body></soap:Envelope>"
+
+/* Remove a contact from a group */
+#define MSN_CONTACT_DEL_GROUP_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupContactDelete"
+#define MSN_CONTACT_DEL_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts><groupFilter><groupIds><guid>%s</guid></groupIds></groupFilter></ABGroupContactDelete></soap:Body></soap:Envelope>"
+
+
+/* Update Contact Nickname */
+#define MSN_CONTACT_UPDATE_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactUpdate"
+#define MSN_CONTACT_UPDATE_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>Timer</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<ABContactUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<abId>00000000-0000-0000-0000-000000000000</abId>"\
+			"<contacts>"\
+				"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+					"<contactInfo>"\
+						"<contactType>Me</contactType>"\
+						"<displayName>%s</displayName>"\
+					"</contactInfo>"\
+					"<propertiesChanged>DisplayName</propertiesChanged>"\
+				"</Contact>"\
+			"</contacts>"\
+		"</ABContactUpdate>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+
+/*******************************************************
+ * Add/Delete contact from lists SOAP actions
+ *******************************************************/
+
+/* block means delete from allow list and add contact to block list */
+#define MSN_SHARE_POST_URL		"/abservice/SharingService.asmx"
+
+#define MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/AddMember"
+#define MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/DeleteMember"
+
+#define MSN_MEMBER_PASSPORT_XML	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+					"<Type>Passport</Type>"\
+					"<State>Accepted</State>"\
+					"<PassportName>%s</PassportName>"\
+				"</Member>"
+
+#define MSN_MEMBER_MEMBERSHIPID_XML	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+						"<Type>Passport</Type>"\
+						"<MembershipId>%u</MembershipId>"\
+						"<State>Accepted</State>"\
+					"</Member>"
+
+/* first delete contact from allow list */
+
+#define MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>%s</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<DeleteMember xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<serviceHandle>"\
+				"<Id>0</Id>"\
+				"<Type>Messenger</Type>"\
+				"<ForeignId></ForeignId>"\
+			"</serviceHandle>"\
+			"<memberships>"\
+				"<Membership>"\
+					"<MemberRole>%s</MemberRole>"\
+					"<Members>"\
+						"%s"\
+					"</Members>"\
+				"</Membership>"\
+			"</memberships>"\
+		"</DeleteMember>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+#define MSN_CONTACT_ADD_TO_LIST_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+	"<soap:Header>"\
+		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+			"<IsMigration>false</IsMigration>"\
+			"<PartnerScenario>%s</PartnerScenario>"\
+		"</ABApplicationHeader>"\
+		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
+		"</ABAuthHeader>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<AddMember xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+			"<serviceHandle>"\
+				"<Id>0</Id>"\
+				"<Type>Messenger</Type>"\
+				"<ForeignId></ForeignId>"\
+			"</serviceHandle>"\
+			"<memberships>"\
+				"<Membership>"\
+					"<MemberRole>%s</MemberRole>"\
+					"<Members>"\
+						"%s"\
+					"</Members>"\
+				"</Membership>"\
+			"</memberships>"\
+		"</AddMember>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+
+
+/*******************************************************
+ * Group management SOAP actions
+ *******************************************************/
+
+/* add a group */
+#define MSN_GROUP_ADD_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupAdd"
+#define MSN_GROUP_ADD_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>GroupSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupAddOptions><fRenameOnMsgrConflict>false</fRenameOnMsgrConflict></groupAddOptions><groupInfo><GroupInfo><name>%s</name><groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType><fMessenger>false</fMessenger><annotations><Annotation><Name>MSN.IM.Display</Name><Value>1</Value></Annotation></annotations></GroupInfo></groupInfo></ABGroupAdd></soap:Body></soap:Envelope>"
+
+/* delete a group */
+#define MSN_GROUP_DEL_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupDelete"
+#define MSN_GROUP_DEL_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupFilter><groupIds><guid>%s</guid></groupIds></groupFilter></ABGroupDelete></soap:Body></soap:Envelope>"
+
+/* change a group's name */
+#define MSN_GROUP_RENAME_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupUpdate"
+#define MSN_GROUP_RENAME_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groups><Group><groupId>%s</groupId><groupInfo><name>%s</name></groupInfo><propertiesChanged>GroupName </propertiesChanged></Group></groups></ABGroupUpdate></soap:Body></soap:Envelope>"
+
+typedef enum 
+{
+	MSN_ADD_BUDDY			= 0x01,
+	MSN_MOVE_BUDDY			= 0x02,
+	MSN_ACCEPTED_BUDDY		= 0x04,
+	MSN_DENIED_BUDDY		= 0x08,
+	MSN_ADD_GROUP			= 0x10,
+	MSN_DEL_GROUP			= 0x20,
+	MSN_RENAME_GROUP		= 0x40,
+} MsnCallbackAction;
+
+typedef struct _MsnContact MsnContact;
+
+struct _MsnContact
+{
+	MsnSession *session;
+
+	MsnSoapConn *soapconn;
+};
+
+typedef struct _MsnCallbackState MsnCallbackState;
+
+struct _MsnCallbackState
+{
+	gchar * who;
+	gchar * old_group_name;
+	gchar * new_group_name;
+	gchar * guid;
+	MsnListId list_id;
+	MsnCallbackAction action;
+};
+
+typedef enum 
+{
+	MSN_PS_INITIAL,
+	MSN_PS_SAVE_CONTACT,
+	MSN_PS_PENDING_LIST,
+	MSN_PS_CONTACT_API,
+	MSN_PS_BLOCK_UNBLOCK
+} MsnSoapPartnerScenario;
+
+/************************************************
+ * function prototype
+ ************************************************/
+MsnContact * msn_contact_new(MsnSession *session);
+void msn_contact_destroy(MsnContact *contact);
+
+MsnCallbackState * msn_callback_state_new(void);
+void msn_callback_state_free(MsnCallbackState *state);
+void msn_callback_state_set_who(MsnCallbackState *state, const gchar *who);
+void msn_callback_state_set_old_group_name(MsnCallbackState *state,
+					   const gchar *old_group_name);
+void msn_callback_state_set_new_group_name(MsnCallbackState *state, 
+					   const gchar *new_group_name);
+void msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid);
+void msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id);
+void msn_callback_state_set_action(MsnCallbackState *state, 
+				   MsnCallbackAction action);
+
+void msn_contact_connect(MsnContact *contact);
+void msn_get_contact_list(MsnContact * contact, 
+			  const MsnSoapPartnerScenario partner_scenario,
+			  const char *update);
+void msn_get_address_book(MsnContact *contact, 
+			  const MsnSoapPartnerScenario partner_scenario,
+			  const char * update, const char * gupdate);
+
+/* contact SOAP operations */
+void msn_update_contact(MsnContact *contact, const char* nickname);
+
+void msn_add_contact(MsnContact *contact, MsnCallbackState *state, 
+		     const char *passport);
+void msn_delete_contact(MsnContact *contact, const char *contactId);
+
+void msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, 
+			      const char *passport, const char *groupId);
+void msn_del_contact_from_group(MsnContact *contact, const char *passport, 
+				const char *group_name);
+/* group operations */
+void msn_add_group(MsnSession *session, MsnCallbackState *state, 
+					const char* group_name);
+void msn_del_group(MsnSession *session, const gchar *group_name);
+void msn_contact_rename_group(MsnSession *session, const char *old_group_name,
+						   const char *new_group_name);
+
+/* lists operations */
+void msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
+			     const gchar *passport, const MsnListId list);
+void msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state,
+			       const gchar *passport, const MsnListId list);
+
+void msn_contact_connect_init(MsnSoapConn *soapconn);
+
+#endif /* _MSN_CONTACT_H_ */
+
--- a/libpurple/protocols/msn/dialog.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/dialog.c	Sun Sep 16 18:06:22 2007 +0000
@@ -36,6 +36,7 @@
 
 /* Remove the buddy referenced by the MsnAddRemData before the serverside list is changed.
  * If the buddy will be added, he'll be added back; if he will be removed, he won't be. */
+/* Actually with our MSNP14 code that isn't true yet, he won't be added back :( */
 static void
 msn_complete_sync_issue(MsnAddRemData *data)
 {
@@ -44,28 +45,32 @@
 
 	if (data->group != NULL)
 		group = purple_find_group(data->group);
-	
+
 	if (group != NULL)
 		buddy = purple_find_buddy_in_group(purple_connection_get_account(data->gc), data->who, group);
 	else
 		buddy = purple_find_buddy(purple_connection_get_account(data->gc), data->who);
-	
+
 	if (buddy != NULL)
 		purple_blist_remove_buddy(buddy);
 }
 
+
 static void
 msn_add_cb(MsnAddRemData *data)
 {
-	MsnSession *session;
-	MsnUserList *userlist;
-
+#if 0
+	/* this *should* be necessary !! */
 	msn_complete_sync_issue(data);
+#endif
 
-	session = data->gc->proto_data;
-	userlist = session->userlist;
+	if (g_list_find(purple_connections_get_all(), data->gc) != NULL)
+	{
+		MsnSession *session = data->gc->proto_data;
+		MsnUserList *userlist = session->userlist;
 
-	msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, data->group);
+		msn_userlist_add_buddy(userlist, data->who, data->group);
+	}
 
 	g_free(data->group);
 	g_free(data->who);
@@ -75,17 +80,20 @@
 static void
 msn_rem_cb(MsnAddRemData *data)
 {
-	MsnSession *session;
-	MsnUserList *userlist;
-
 	msn_complete_sync_issue(data);
 
-	session = data->gc->proto_data;
-	userlist = session->userlist;
+	if (g_list_find(purple_connections_get_all(), data->gc) != NULL)
+	{
+		MsnSession *session = data->gc->proto_data;
+		MsnUserList *userlist = session->userlist;
 
-	msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->group);
+		if (data->group == NULL) {
+			msn_userlist_rem_buddy_from_list(userlist, data->who, MSN_LIST_FL);
+		} else {
+			g_free(data->group);
+		}
+	}
 
-	g_free(data->group);
 	g_free(data->who);
 	g_free(data);
 }
@@ -104,7 +112,7 @@
 
 	data        = g_new0(MsnAddRemData, 1);
 	data->who   = g_strdup(passport);
-	data->group = g_strdup(group_name);
+	data->group = group_name != NULL ? g_strdup(group_name) : NULL;
 	data->gc    = gc;
 
 	msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"),
--- a/libpurple/protocols/msn/directconn.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/directconn.c	Sun Sep 16 18:06:22 2007 +0000
@@ -76,7 +76,6 @@
  * Connection Functions
  **************************************************************************/
 
-#if 0
 static int
 create_listener(int port)
 {
@@ -160,7 +159,6 @@
 
 	return fd;
 }
-#endif
 
 static size_t
 msn_directconn_write(MsnDirectConn *directconn,
@@ -288,6 +286,11 @@
 		/* ERROR */
 		purple_debug_error("msn", "error reading\n");
 
+		if (directconn->inpa)
+			purple_input_remove(directconn->inpa);
+
+		close(directconn->fd);
+
 		msn_directconn_destroy(directconn);
 
 		return;
@@ -302,6 +305,11 @@
 		/* ERROR */
 		purple_debug_error("msn", "error reading\n");
 
+		if (directconn->inpa)
+			purple_input_remove(directconn->inpa);
+
+		close(directconn->fd);
+
 		msn_directconn_destroy(directconn);
 
 		return;
@@ -348,17 +356,22 @@
 		/* ERROR */
 		purple_debug_error("msn", "error reading\n");
 
+		if (directconn->inpa)
+			purple_input_remove(directconn->inpa);
+
+		close(directconn->fd);
+
 		msn_directconn_destroy(directconn);
 	}
 }
 
 static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
+connect_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	MsnDirectConn* directconn;
 	int fd;
 
-	purple_debug_misc("msn", "directconn: connect_cb: %d\n", source);
+	purple_debug_misc("msn", "directconn: connect_cb: %d, %d.\n", source, cond);
 
 	directconn = data;
 	directconn->connect_data = NULL;
@@ -434,7 +447,6 @@
 		return FALSE;
 }
 
-#if 0
 void
 msn_directconn_listen(MsnDirectConn *directconn)
 {
@@ -454,7 +466,6 @@
 	directconn->port = port;
 	directconn->c = 0;
 }
-#endif
 
 MsnDirectConn*
 msn_directconn_new(MsnSlpLink *slplink)
--- a/libpurple/protocols/msn/directconn.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/directconn.h	Sun Sep 16 18:06:22 2007 +0000
@@ -52,9 +52,7 @@
 MsnDirectConn *msn_directconn_new(MsnSlpLink *slplink);
 gboolean msn_directconn_connect(MsnDirectConn *directconn,
 								const char *host, int port);
-#if 0
 void msn_directconn_listen(MsnDirectConn *directconn);
-#endif
 void msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg);
 void msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce);
 void msn_directconn_destroy(MsnDirectConn *directconn);
--- a/libpurple/protocols/msn/group.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/group.c	Sun Sep 16 18:06:22 2007 +0000
@@ -25,18 +25,18 @@
 #include "group.h"
 
 MsnGroup *
-msn_group_new(MsnUserList *userlist, int id, const char *name)
+msn_group_new(MsnUserList *userlist, const char *id, const char *name)
 {
 	MsnGroup *group;
 
-	g_return_val_if_fail(id >= 0,      NULL);
+	g_return_val_if_fail(id != NULL,      NULL);
 	g_return_val_if_fail(name != NULL, NULL);
 
 	group = g_new0(MsnGroup, 1);
 
 	msn_userlist_add_group(userlist, group);
 
-	group->id      = id;
+	group->id      = g_strdup(id);
 	group->name    = g_strdup(name);
 
 	return group;
@@ -47,17 +47,18 @@
 {
 	g_return_if_fail(group != NULL);
 
+	g_free(group->id);
 	g_free(group->name);
 	g_free(group);
 }
 
 void
-msn_group_set_id(MsnGroup *group, int id)
+msn_group_set_id(MsnGroup *group, const char *id)
 {
 	g_return_if_fail(group != NULL);
-	g_return_if_fail(id >= 0);
+	g_return_if_fail(id != NULL);
 
-	group->id = id;
+	group->id = g_strdup(id);
 }
 
 void
@@ -72,10 +73,10 @@
 	group->name = g_strdup(name);
 }
 
-int
+char*
 msn_group_get_id(const MsnGroup *group)
 {
-	g_return_val_if_fail(group != NULL, -1);
+	g_return_val_if_fail(group != NULL, NULL);
 
 	return group->id;
 }
--- a/libpurple/protocols/msn/group.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/group.h	Sun Sep 16 18:06:22 2007 +0000
@@ -30,8 +30,21 @@
 
 #include "session.h"
 #include "user.h"
+#include "soap.h"
+#include "userlist.h"
 
-#include "userlist.h"
+#define MSN_ADD_GROUPS	"<GroupInfo><name>test111</name><groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType><fMessenger>false</fMessenger><annotations><Annotation><Name>MSN.IM.Display</Name><Value>1</Value></Annotation></annotations></GroupInfo>"
+
+#define MSN_ADD_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>GroupSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupAddOptions><fRenameOnMsgrConflict>false</fRenameOnMsgrConflict></groupAddOptions><groupInfo>%s</groupInfo></ABGroupAdd></soap:Body></soap:Envelope>"
+
+#define MSN_GROUP_IDS	"<guid>9e57e654-59f0-44d1-aedc-0a7500b7e51f</guid>"
+#define MSN_DELETE_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupFilter><groupIds>%s</groupIds></groupFilter></ABGroupDelete></soap:Body></soap:Envelope>"
+
+#define MSN_INDIVIDUALS_GROUP_ID	"1983"
+#define MSN_INDIVIDUALS_GROUP_NAME	"Other Contacts"
+
+#define MSN_NON_IM_GROUP_ID		"email"
+#define MSN_NON_IM_GROUP_NAME	"Non-IM Contacts"
 
 /**
  * A group.
@@ -39,8 +52,9 @@
 struct _MsnGroup
 {
 	MsnSession *session;    /**< The MSN session.           */
+	MsnSoapConn *soapconn;
 
-	int id;                 /**< The group ID.              */
+	char *id;                 /**< The group ID.              */
 	char *name;             /**< The name of the group.     */
 };
 
@@ -58,7 +72,7 @@
  *
  * @return A new group structure.
  */
-MsnGroup *msn_group_new(MsnUserList *userlist, int id, const char *name);
+MsnGroup *msn_group_new(MsnUserList *userlist, const char *id, const char *name);
 
 /**
  * Destroys a group structure.
@@ -73,7 +87,7 @@
  * @param group The group.
  * @param id    The ID.
  */
-void msn_group_set_id(MsnGroup *group, int id);
+void msn_group_set_id(MsnGroup *group, const char *id);
 
 /**
  * Sets the name for a group.
@@ -90,7 +104,7 @@
  *
  * @return The ID.
  */
-int msn_group_get_id(const MsnGroup *group);
+char* msn_group_get_id(const MsnGroup *group);
 
 /**
  * Returns the name for a group.
--- a/libpurple/protocols/msn/history.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/history.c	Sun Sep 16 18:06:22 2007 +0000
@@ -84,3 +84,4 @@
 		msn_transaction_destroy(trans);
 	}
 }
+
--- a/libpurple/protocols/msn/msg.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/msg.c	Sun Sep 16 18:06:22 2007 +0000
@@ -126,7 +126,7 @@
 	msn_message_set_charset(msg, "UTF-8");
 	msn_message_set_flag(msg, 'A');
 	msn_message_set_attr(msg, "X-MMS-IM-Format",
-						 "FN=MS%20Sans%20Serif; EF=; CO=0; PF=0");
+						 "FN=MS%20Sans%20Serif; EF=; CO=0; CS=86;PF=0");
 
 	message_cr = purple_str_add_cr(message);
 	msn_message_set_bin_data(msg, message_cr, strlen(message_cr));
@@ -206,7 +206,8 @@
 
 void
 msn_message_parse_payload(MsnMessage *msg,
-						  const char *payload, size_t payload_len)
+						  const char *payload, size_t payload_len,
+						  const char *line_dem,const char *body_dem)
 {
 	char *tmp_base, *tmp;
 	const char *content_type;
@@ -214,12 +215,11 @@
 	char **elems, **cur, **tokens;
 
 	g_return_if_fail(payload != NULL);
-
 	tmp_base = tmp = g_malloc0(payload_len + 1);
 	memcpy(tmp_base, payload, payload_len);
 
 	/* Parse the attributes. */
-	end = strstr(tmp, "\r\n\r\n");
+	end = strstr(tmp, body_dem);
 	/* TODO? some clients use \r delimiters instead of \r\n, the official client
 	 * doesn't send such messages, but does handle receiving them. We'll just
 	 * avoid crashing for now */
@@ -229,7 +229,7 @@
 	}
 	*end = '\0';
 
-	elems = g_strsplit(tmp, "\r\n", 0);
+	elems = g_strsplit(tmp, line_dem, 0);
 
 	for (cur = elems; *cur != NULL; cur++)
 	{
@@ -240,6 +240,7 @@
 		key = tokens[0];
 		value = tokens[1];
 
+		/*if not MIME content ,then return*/
 		if (!strcmp(key, "MIME-Version"))
 		{
 			g_strfreev(tokens);
@@ -274,7 +275,7 @@
 	g_strfreev(elems);
 
 	/* Proceed to the end of the "\r\n\r\n" */
-	tmp = end + 4;
+	tmp = end + strlen(body_dem);
 
 	/* Now we *should* be at the body. */
 	content_type = msn_message_get_content_type(msg);
@@ -480,6 +481,7 @@
 		{
 			memcpy(n, body, body_len);
 			n += body_len;
+			*n = '\0';
 		}
 	}
 
--- a/libpurple/protocols/msn/msg.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/msg.h	Sun Sep 16 18:06:22 2007 +0000
@@ -34,6 +34,12 @@
 
 typedef void (*MsnMsgCb)(MsnMessage *, void *data);
 
+#define MSG_BODY_DEM	"\r\n\r\n"
+#define MSG_LINE_DEM	"\r\n"
+
+#define MSG_OIM_BODY_DEM	"\n\n"
+#define MSG_OIM_LINE_DEM	"\n"
+
 /*
 typedef enum
 {
@@ -180,7 +186,8 @@
  * @param payload_len The length of the payload.
  */
 void msn_message_parse_payload(MsnMessage *msg, const char *payload,
-							   size_t payload_len);
+							   size_t payload_len,
+						  const char *line_dem,const char *body_dem);
 
 /**
  * Destroys a message.
--- a/libpurple/protocols/msn/msn-utils.c	Tue Sep 11 13:48:19 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,440 +0,0 @@
-/**
- * @file msn-utils.c Utility functions
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "msn.h"
-#include "msn-utils.h"
-
-void
-msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
-{
-	char *cur;
-	GString *pre  = g_string_new(NULL);
-	GString *post = g_string_new(NULL);
-	unsigned int colors[3];
-
-	if (pre_ret  != NULL) *pre_ret  = NULL;
-	if (post_ret != NULL) *post_ret = NULL;
-
-	cur = strstr(mime, "FN=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		pre = g_string_append(pre, "<FONT FACE=\"");
-
-		while (*cur && *cur != ';')
-		{
-			pre = g_string_append_c(pre, *cur);
-			cur++;
-		}
-
-		pre = g_string_append(pre, "\">");
-		post = g_string_prepend(post, "</FONT>");
-	}
-
-	cur = strstr(mime, "EF=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		while (*cur && *cur != ';')
-		{
-			pre = g_string_append_c(pre, '<');
-			pre = g_string_append_c(pre, *cur);
-			pre = g_string_append_c(pre, '>');
-			post = g_string_prepend_c(post, '>');
-			post = g_string_prepend_c(post, *cur);
-			post = g_string_prepend_c(post, '/');
-			post = g_string_prepend_c(post, '<');
-			cur++;
-		}
-	}
-
-	cur = strstr(mime, "CO=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		int i;
-
-		i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
-
-		if (i > 0)
-		{
-			char tag[64];
-
-			if (i == 1)
-			{
-				colors[1] = 0;
-				colors[2] = 0;
-			}
-			else if (i == 2)
-			{
-				unsigned int temp = colors[0];
-
-				colors[0] = colors[1];
-				colors[1] = temp;
-				colors[2] = 0;
-			}
-			else if (i == 3)
-			{
-				unsigned int temp = colors[2];
-
-				colors[2] = colors[0];
-				colors[0] = temp;
-			}
-
-			g_snprintf(tag, sizeof(tag),
-					   "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">",
-					   colors[0], colors[1], colors[2]);
-
-			pre = g_string_append(pre, tag);
-			post = g_string_prepend(post, "</FONT>");
-		}
-	}
-
-	cur = strstr(mime, "RL=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		if (*cur == '1')
-		{
-			/* RTL text was received */
-			pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
-			post = g_string_prepend(post, "</SPAN>");
-		}
-	}
-
-	cur = g_strdup(purple_url_decode(pre->str));
-	g_string_free(pre, TRUE);
-
-	if (pre_ret != NULL)
-		*pre_ret = cur;
-	else
-		g_free(cur);
-
-	cur = g_strdup(purple_url_decode(post->str));
-	g_string_free(post, TRUE);
-
-	if (post_ret != NULL)
-		*post_ret = cur;
-	else
-		g_free(cur);
-}
-
-/*
- * We need this because we're only supposed to encode spaces in the font
- * names. purple_url_encode() isn't acceptable.
- */
-static const char *
-encode_spaces(const char *str)
-{
-	static char buf[BUF_LEN];
-	const char *c;
-	char *d;
-
-	g_return_val_if_fail(str != NULL, NULL);
-
-	for (c = str, d = buf; *c != '\0'; c++)
-	{
-		if (*c == ' ')
-		{
-			*d++ = '%';
-			*d++ = '2';
-			*d++ = '0';
-		}
-		else
-			*d++ = *c;
-	}
-
-	return buf;
-}
-
-/*
- * Taken from the zephyr plugin.
- * This parses HTML formatting (put out by one of the gtkimhtml widgets
- * and converts it to msn formatting. It doesn't deal with the tag closing,
- * but gtkimhtml widgets give valid html.
- * It currently deals properly with <b>, <u>, <i>, <font face=...>,
- * <font color=...>, <span dir=...>, <span style="direction: ...">.
- * It ignores <font back=...> and <font size=...>
- */
-void
-msn_import_html(const char *html, char **attributes, char **message)
-{
-	int len, retcount = 0;
-	const char *c;
-	char *msg;
-	char *fontface = NULL;
-	char fonteffect[4];
-	char fontcolor[7];
-	char direction = '0';
-
-	gboolean has_bold = FALSE;
-	gboolean has_italic = FALSE;
-	gboolean has_underline = FALSE;
-	gboolean has_strikethrough = FALSE;
-
-	g_return_if_fail(html       != NULL);
-	g_return_if_fail(attributes != NULL);
-	g_return_if_fail(message    != NULL);
-
-	len = strlen(html);
-	msg = g_malloc0(len + 1);
-
-	memset(fontcolor, 0, sizeof(fontcolor));
-	strcat(fontcolor, "0");
-	memset(fonteffect, 0, sizeof(fonteffect));
-
-	for (c = html; *c != '\0';)
-	{
-		if (*c == '<')
-		{
-			if (!g_ascii_strncasecmp(c + 1, "br>", 3))
-			{
-				msg[retcount++] = '\r';
-				msg[retcount++] = '\n';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
-			{
-				if (!has_italic)
-				{
-					strcat(fonteffect, "I");
-					has_italic = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
-			{
-				if (!has_bold)
-				{
-					strcat(fonteffect, "B");
-					has_bold = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
-			{
-				if (!has_underline)
-				{
-					strcat(fonteffect, "U");
-					has_underline = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
-			{
-				if (!has_strikethrough)
-				{
-					strcat(fonteffect, "S");
-					has_strikethrough = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
-			{
-				c += 9;
-
-				if (!g_ascii_strncasecmp(c, "mailto:", 7))
-					c += 7;
-
-				while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
-					msg[retcount++] = *c++;
-
-				if (*c != '\0')
-					c += 2;
-
-				/* ignore descriptive string */
-				while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
-					c++;
-
-				if (*c != '\0')
-					c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "span", 4))
-			{
-				/* Bi-directional text support using CSS properties in span tags */
-				c += 5;
-
-				while (*c != '\0' && *c != '>')
-				{
-					while (*c == ' ')
-						c++;
-					if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
-					{
-						c += 9;
-						direction = '1';
-					}
-					else if (!g_ascii_strncasecmp(c, "style=\"", 7))
-					{
-						/* Parse inline CSS attributes */
-						char *attributes;
-						int attr_len = 0;
-						c += 7;
-						while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
-							attr_len++;
-						if (*(c + attr_len) == '"')
-						{
-							char *attr_dir;
-							attributes = g_strndup(c, attr_len);
-							attr_dir = purple_markup_get_css_property(attributes, "direction");
-							if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
-								direction = '1';
-							g_free(attr_dir);
-							g_free(attributes);
-						}
-
-					}
-					else
-					{
-						c++;
-					}
-				}
-				if (*c == '>')
-					c++;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "font", 4))
-			{
-				c += 5;
-
-				while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
-					c++;
-
-				if (!g_ascii_strncasecmp(c, "color=\"#", 7))
-				{
-					c += 8;
-
-					fontcolor[0] = *(c + 4);
-					fontcolor[1] = *(c + 5);
-					fontcolor[2] = *(c + 2);
-					fontcolor[3] = *(c + 3);
-					fontcolor[4] = *c;
-					fontcolor[5] = *(c + 1);
-
-					c += 8;
-				}
-				else if (!g_ascii_strncasecmp(c, "face=\"", 6))
-				{
-					const char *end = NULL;
-					const char *comma = NULL;
-					unsigned int namelen = 0;
-
-					c += 6;
-					end = strchr(c, '\"');
-					comma = strchr(c, ',');
-
-					if (comma == NULL || comma > end)
-						namelen = (unsigned int)(end - c);
-					else
-						namelen = (unsigned int)(comma - c);
-
-					fontface = g_strndup(c, namelen);
-					c = end + 2;
-				}
-				else
-				{
-					/* Drop all unrecognized/misparsed font tags */
-					while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
-						c++;
-
-					if (*c != '\0')
-						c += 2;
-				}
-			}
-			else
-			{
-				while ((*c != '\0') && (*c != '>'))
-					c++;
-				if (*c != '\0')
-					c++;
-			}
-		}
-		else if (*c == '&')
-		{
-			if (!g_ascii_strncasecmp(c, "&lt;", 4))
-			{
-				msg[retcount++] = '<';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&gt;", 4))
-			{
-				msg[retcount++] = '>';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
-			{
-				msg[retcount++] = ' ';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&quot;", 6))
-			{
-				msg[retcount++] = '"';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&amp;", 5))
-			{
-				msg[retcount++] = '&';
-				c += 5;
-			}
-			else if (!g_ascii_strncasecmp(c, "&apos;", 6))
-			{
-				msg[retcount++] = '\'';
-				c += 6;
-			}
-			else
-				msg[retcount++] = *c++;
-		}
-		else
-			msg[retcount++] = *c++;
-	}
-
-	if (fontface == NULL)
-		fontface = g_strdup("MS Sans Serif");
-
-	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
-								  encode_spaces(fontface),
-								  fonteffect, fontcolor, direction);
-	*message = g_strdup(msg);
-
-	g_free(fontface);
-	g_free(msg);
-}
-
-void
-msn_parse_socket(const char *str, char **ret_host, int *ret_port)
-{
-	char *host;
-	char *c;
-	int port;
-
-	host = g_strdup(str);
-
-	if ((c = strchr(host, ':')) != NULL)
-	{
-		*c = '\0';
-		port = atoi(c + 1);
-	}
-	else
-		port = 1863;
-
-	*ret_host = host;
-	*ret_port = port;
-}
--- a/libpurple/protocols/msn/msn-utils.h	Tue Sep 11 13:48:19 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/**
- * @file msn-utils.h Utility functions
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#ifndef _MSN_UTILS_H_
-#define _MSN_UTILS_H_
-
-/**
- * Parses the MSN message formatting into a format compatible with Purple.
- *
- * @param mime     The mime header with the formatting.
- * @param pre_ret  The returned prefix string.
- * @param post_ret The returned postfix string.
- *
- * @return The new message.
- */
-void msn_parse_format(const char *mime, char **pre_ret, char **post_ret);
-
-/**
- * Parses the Purple message formatting (html) into the MSN format.
- *
- * @param html			The html message to format.
- * @param attributes	The returned attributes string.
- * @param message		The returned message string.
- *
- * @return The new message.
- */
-void msn_import_html(const char *html, char **attributes, char **message);
-
-void msn_parse_socket(const char *str, char **ret_host, int *ret_port);
-
-#endif /* _MSN_UTILS_H_ */
--- a/libpurple/protocols/msn/msn.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Sun Sep 16 18:06:22 2007 +0000
@@ -37,7 +37,7 @@
 #include "cmds.h"
 #include "core.h"
 #include "prpl.h"
-#include "msn-utils.h"
+#include "msnutils.h"
 #include "version.h"
 
 #include "switchboard.h"
@@ -151,7 +151,7 @@
 	return PURPLE_CMD_RET_OK;
 }
 
-static void
+void
 msn_act_id(PurpleConnection *gc, const char *entry)
 {
 	MsnCmdProc *cmdproc;
@@ -175,9 +175,12 @@
 		return;
 	}
 
-	msn_cmdproc_send(cmdproc, "REA", "%s %s",
-					 purple_account_get_username(account),
-					 alias);
+	if (*alias == '\0') {
+		alias = purple_url_encode(purple_account_get_username(account));
+	}
+
+	msn_cmdproc_send(cmdproc, "PRP", "MFN %s", alias);
+
 }
 
 static void
@@ -414,6 +417,28 @@
 	return user && user->mobile;
 }
 
+void
+msn_send_privacy(PurpleConnection *gc)
+{
+       PurpleAccount *account;
+        MsnSession *session;
+        MsnCmdProc *cmdproc;
+
+        account = purple_connection_get_account(gc);
+        session = gc->proto_data;
+        cmdproc = session->notification->cmdproc;
+
+        if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
+                account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
+        {
+                msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
+        }
+        else
+        {
+                msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
+        }
+}
+
 static void
 initiate_chat_cb(PurpleBlistNode *node, gpointer data)
 {
@@ -460,6 +485,7 @@
 	session = gc->proto_data;
 
 	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
+
 	if (xfer)
 	{
 		slplink = msn_session_get_slplink(session, who);
@@ -511,20 +537,27 @@
 	return "msn";
 }
 
+/*
+ * Set the User status text
+ * Add the PSM String Using "Name - PSM String" format
+ */
 static char *
 msn_status_text(PurpleBuddy *buddy)
 {
 	PurplePresence *presence;
 	PurpleStatus *status;
+	const char *msg, *cmedia;
 
 	presence = purple_buddy_get_presence(buddy);
 	status = purple_presence_get_active_status(presence);
 
-	if (!purple_presence_is_available(presence) && !purple_presence_is_idle(presence))
-	{
-		return g_strdup(purple_status_get_name(status));
-	}
+	msg = purple_status_get_attr_string(status, "message");
+	cmedia = purple_status_get_attr_string(status, "currentmedia");
 
+	if (cmedia)
+		return g_markup_escape_text(cmedia, -1);
+	else if (msg)
+		return g_markup_escape_text(msg, -1);
 	return NULL;
 }
 
@@ -540,8 +573,41 @@
 	
 	if (purple_presence_is_online(presence))
 	{
-		purple_notify_user_info_add_pair(user_info, _("Status"),
-									   (purple_presence_is_idle(presence) ? _("Idle") : purple_status_get_name(status)));
+		const char *psm, *currentmedia, *name;
+		char *tmp;
+
+		psm = purple_status_get_attr_string(status, "message");
+		currentmedia = purple_status_get_attr_string(status, "currentmedia");
+
+		if (!purple_presence_is_available(presence)) {
+			name = purple_status_get_name(status);
+		} else {
+			name = NULL;
+		}
+
+		if (name != NULL && *name) {
+			char *tmp2 = g_markup_escape_text(name, -1);
+
+			if (psm != NULL && *psm) {
+				tmp = g_markup_escape_text(psm, -1);
+				purple_notify_user_info_add_pair(user_info, tmp2, tmp);
+				g_free(tmp);
+			} else {
+				purple_notify_user_info_add_pair(user_info, _("Status"), tmp2);
+			}
+
+			g_free(tmp2);
+		} else {
+			tmp = g_markup_escape_text(psm, -1);
+			purple_notify_user_info_add_pair(user_info, _("Status"), tmp);
+			g_free(tmp);
+		}
+
+		if (currentmedia) {
+			tmp = g_markup_escape_text(currentmedia, -1);
+			purple_notify_user_info_add_pair(user_info, _("Current media"), tmp);
+			g_free(tmp);
+		}
 	}
 	
 	if (full && user)
@@ -566,29 +632,48 @@
 {
 	PurpleStatusType *status;
 	GList *types = NULL;
-
+#if 0
 	status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE,
 			NULL, NULL, FALSE, TRUE, FALSE);
+#endif
+	status = purple_status_type_new_with_attrs(
+				PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
+				"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+				"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+				NULL);
 	types = g_list_append(types, status);
 
-	status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
-			NULL, NULL, FALSE, TRUE, FALSE);
+	status = purple_status_type_new_with_attrs(
+			PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
+			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
 	types = g_list_append(types, status);
 
-	status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
-			"brb", _("Be Right Back"), FALSE, TRUE, FALSE);
+	status = purple_status_type_new_with_attrs(
+			PURPLE_STATUS_AWAY, "brb", _("Be Right Back"), TRUE, TRUE, FALSE,
+			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
 	types = g_list_append(types, status);
 
-	status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
-			"busy", _("Busy"), FALSE, TRUE, FALSE);
+	status = purple_status_type_new_with_attrs(
+			PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE,
+			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
 	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
-			"phone", _("On the Phone"), FALSE, TRUE, FALSE);
+	status = purple_status_type_new_with_attrs(
+			PURPLE_STATUS_UNAVAILABLE, "phone", _("On the Phone"), TRUE, TRUE, FALSE,
+			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
 	types = g_list_append(types, status);
-
-	status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
-			"lunch", _("Out to Lunch"), FALSE, TRUE, FALSE);
+	status = purple_status_type_new_with_attrs(
+			PURPLE_STATUS_AWAY, "lunch", _("Out to Lunch"), TRUE, TRUE, FALSE,
+			"message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+			"currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+			NULL);
 	types = g_list_append(types, status);
 
 	status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
@@ -598,11 +683,11 @@
 	status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
 			NULL, NULL, FALSE, TRUE, FALSE);
 	types = g_list_append(types, status);
-	
+
 	status = purple_status_type_new_full(PURPLE_STATUS_MOBILE,
 			"mobile", NULL, FALSE, FALSE, TRUE);
 	types = g_list_append(types, status);
-	
+
 	return types;
 }
 
@@ -785,6 +870,7 @@
 	char *msgformat;
 	char *msgtext;
 
+	purple_debug_info("MSNP14","send IM {%s} to %s\n",message,who);
 	account = purple_connection_get_account(gc);
 
 	if (buddy) {
@@ -798,62 +884,91 @@
 	}
 
 	msn_import_html(message, &msgformat, &msgtext);
+	if(msn_user_is_online(account, who)||
+		msn_user_is_yahoo(account, who)){
+		/*User online,then send Online Instant Message*/
 
-	if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564)
-	{
+		if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564)
+		{
+			g_free(msgformat);
+			g_free(msgtext);
+
+			return -E2BIG;
+		}
+
+		msg = msn_message_new_plain(msgtext);
+		msg->remote_user = g_strdup(who);
+		msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
+
 		g_free(msgformat);
 		g_free(msgtext);
 
-		return -E2BIG;
-	}
+		purple_debug_info("MSNP14","prepare to send online Message\n");
+		if (g_ascii_strcasecmp(who, purple_account_get_username(account)))
+		{
+			MsnSession *session;
+			MsnSwitchBoard *swboard;
 
-	msg = msn_message_new_plain(msgtext);
-	msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
-
-	g_free(msgformat);
-	g_free(msgtext);
+			session = gc->proto_data;
+			if(msn_user_is_yahoo(account,who)){
+				/*we send the online and offline Message to Yahoo User via UBM*/
+				purple_debug_info("MSNP14","send to Yahoo User\n");
+				uum_send_msg(session,msg);
+			}else{
+				purple_debug_info("MSNP14","send via switchboard\n");
+				swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM);
+				msn_switchboard_send_msg(swboard, msg, TRUE);
+			}
+		}
+		else
+		{
+			char *body_str, *body_enc, *pre, *post;
+			const char *format;
+			MsnIMData *imdata = g_new0(MsnIMData, 1);
+			/*
+			 * In MSN, you can't send messages to yourself, so
+			 * we'll fake like we received it ;)
+			 */
+			body_str = msn_message_to_string(msg);
+			body_enc = g_markup_escape_text(body_str, -1);
+			g_free(body_str);
 
-	if (g_ascii_strcasecmp(who, purple_account_get_username(account)))
-	{
-		MsnSession *session;
-		MsnSwitchBoard *swboard;
+			format = msn_message_get_attr(msg, "X-MMS-IM-Format");
+			msn_parse_format(format, &pre, &post);
+			body_str = g_strdup_printf("%s%s%s", pre ? pre :  "",
+									   body_enc ? body_enc : "", post ? post : "");
+			g_free(body_enc);
+			g_free(pre);
+			g_free(post);
+
+			serv_got_typing_stopped(gc, who);
+			imdata->gc = gc;
+			imdata->who = who;
+			imdata->msg = body_str;
+			imdata->flags = flags;
+			imdata->when = time(NULL);
+			g_idle_add(msn_send_me_im, imdata);
+		}
 
+		msn_message_destroy(msg);
+	}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;
-		swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM);
+		/* XXX/khc: hack */
+		if (!session->oim)
+			session->oim = msn_oim_new(session);
 
-		msn_switchboard_send_msg(swboard, msg, TRUE);
+		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_send_msg(oim);
 	}
-	else
-	{
-		char *body_str, *body_enc, *pre, *post;
-		const char *format;
-		MsnIMData *imdata = g_new0(MsnIMData, 1);
-		/*
-		 * In MSN, you can't send messages to yourself, so
-		 * we'll fake like we received it ;)
-		 */
-		body_str = msn_message_to_string(msg);
-		body_enc = g_markup_escape_text(body_str, -1);
-		g_free(body_str);
-
-		format = msn_message_get_attr(msg, "X-MMS-IM-Format");
-		msn_parse_format(format, &pre, &post);
-		body_str = g_strdup_printf("%s%s%s", pre ? pre :  "",
-								   body_enc ? body_enc : "", post ? post : "");
-		g_free(body_enc);
-		g_free(pre);
-		g_free(post);
-
-		serv_got_typing_stopped(gc, who);
-		imdata->gc = gc;
-		imdata->who = who;
-		imdata->msg = body_str;
-		imdata->flags = flags;
-		imdata->when = time(NULL);
-		g_idle_add(msn_send_me_im, imdata);
-	}
-
-	msn_message_destroy(msg);
 
 	return 1;
 }
@@ -992,6 +1107,7 @@
 	userlist = session->userlist;
 	who = msn_normalize(gc->account, buddy->name);
 
+	purple_debug_info("MSN","Add user:%s to group:%s\n", who, group->name);
 	if (!session->logged_in)
 	{
 #if 0
@@ -1025,8 +1141,7 @@
 	/* XXX - Would group ever be NULL here?  I don't think so...
 	 * shx: Yes it should; MSN handles non-grouped buddies, and this is only
 	 * internal. */
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_FL,
-						   group ? group->name : NULL);
+	msn_userlist_add_buddy(userlist, who, group ? group->name : NULL);
 }
 
 static void
@@ -1042,7 +1157,7 @@
 		return;
 
 	/* XXX - Does buddy->name need to be msn_normalize'd here?  --KingAnt */
-	msn_userlist_rem_buddy(userlist, buddy->name, MSN_LIST_FL, group->name);
+	msn_userlist_rem_buddy(userlist, buddy->name);
 }
 
 static void
@@ -1059,10 +1174,18 @@
 	if (!session->logged_in)
 		return;
 
-	if (user != NULL && user->list_op & MSN_LIST_BL_OP)
-		msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
+	if (user != NULL && user->list_op & MSN_LIST_BL_OP) {
+		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
 
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
+		/* delete contact from Block list and add it to Allow in the callback */
+		msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL);
+	} else {
+		/* just add the contact to Allow list */
+		msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_AL);
+	}
+
+
+	msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
 }
 
 static void
@@ -1079,10 +1202,17 @@
 	if (!session->logged_in)
 		return;
 
-	if (user != NULL && user->list_op & MSN_LIST_AL_OP)
-		msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
+	if (user != NULL && user->list_op & MSN_LIST_AL_OP) {
+		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
 
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
+		/* delete contact from Allow list and add it to Block in the callback */
+		msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL);
+	} else {
+		/* just add the contact to Block list */
+		msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_BL);
+	}
+
+	msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
 }
 
 static void
@@ -1100,10 +1230,12 @@
 
 	user = msn_userlist_find_user(userlist, who);
 
-	msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
+	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
+
+	msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL);
 
 	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
-		msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
+		msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
 }
 
 static void
@@ -1121,32 +1253,18 @@
 
 	user = msn_userlist_find_user(userlist, who);
 
-	msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
+	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
+
+	msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL);
 
 	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
-		msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
+		msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
 }
 
 static void
 msn_set_permit_deny(PurpleConnection *gc)
 {
-	PurpleAccount *account;
-	MsnSession *session;
-	MsnCmdProc *cmdproc;
-
-	account = purple_connection_get_account(gc);
-	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-
-	if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
-		account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
-	{
-		msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
-	}
-	else
-	{
-		msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
-	}
+	msn_send_privacy(gc);
 }
 
 static void
@@ -1283,24 +1401,20 @@
 				 PurpleGroup *group, GList *moved_buddies)
 {
 	MsnSession *session;
-	MsnCmdProc *cmdproc;
-	int old_gid;
-	const char *enc_new_group_name;
 
 	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-	enc_new_group_name = purple_url_encode(group->name);
-
-	old_gid = msn_userlist_find_group_id(session->userlist, old_name);
-
-	if (old_gid >= 0)
+	
+	g_return_if_fail(session != NULL);
+	g_return_if_fail(session->userlist != NULL);
+	
+	if (msn_userlist_find_group_with_name(session->userlist, old_name) != NULL)
 	{
-		msn_cmdproc_send(cmdproc, "REG", "%d %s 0", old_gid,
-						 enc_new_group_name);
+		msn_contact_rename_group(session, old_name, group->name);
 	}
 	else
 	{
-		msn_cmdproc_send(cmdproc, "ADG", "%s 0", enc_new_group_name);
+		/* not found */
+		msn_add_group(session, NULL, group->name);
 	}
 }
 
@@ -1360,15 +1474,20 @@
 {
 	MsnSession *session;
 	MsnCmdProc *cmdproc;
-	int group_id;
 
 	session = gc->proto_data;
 	cmdproc = session->notification->cmdproc;
 
-	if ((group_id = msn_userlist_find_group_id(session->userlist, group->name)) >= 0)
+	purple_debug_info("MSN", "Remove group %s\n", group->name);
+	/*we can't delete the default group*/
+	if(!strcmp(group->name, MSN_INDIVIDUALS_GROUP_NAME)||
+		!strcmp(group->name, MSN_NON_IM_GROUP_NAME))
 	{
-		msn_cmdproc_send(cmdproc, "RMG", "%d", group_id);
+		purple_debug_info("MSN", "This group can't be removed, returning.\n");
+		return ;
 	}
+	
+	msn_del_group(session, group->name);
 }
 
 /**
@@ -1418,12 +1537,11 @@
 {
 	char *p, *q;
 
-	if ((p = strstr(url_text, " contactparams:photopreauthurl=\"")) != NULL)
+	if ((p = strstr(url_text, PHOTO_URL)) != NULL)
 	{
-		p += strlen(" contactparams:photopreauthurl=\"");
+		p += strlen(PHOTO_URL);
 	}
-
-	if (p && (strncmp(p, "http://", 8) == 0) && ((q = strchr(p, '"')) != NULL))
+	if (p && (strncmp(p, "http://",strlen("http://")) == 0) && ((q = strchr(p, '"')) != NULL))
 			return g_strndup(p, q - p);
 
 	return NULL;
@@ -1488,7 +1606,7 @@
 	MsnGetInfoStepTwoData *info2_data = NULL;
 #endif
 
-	purple_debug_info("msn", "In msn_got_info\n");
+	purple_debug_info("msn", "In msn_got_info,url_text:{%s}\n",url_text);
 
 	/* Make sure the connection is still valid */
 	if (g_list_find(purple_connections_get_all(), info_data->gc) == NULL)
@@ -1872,6 +1990,7 @@
 #if PHOTO_SUPPORT
 	/* Find the URL to the photo; must be before the marshalling [Bug 994207] */
 	photo_url_text = msn_get_photo_url(url_text);
+	purple_debug_info("MSNP14","photo url:{%s}\n",photo_url_text);
 
 	/* Marshall the existing state */
 	info2_data = g_malloc0(sizeof(MsnGetInfoStepTwoData));
@@ -2096,7 +2215,7 @@
 	msn_add_deny,			/* add_deny */
 	msn_rem_permit,			/* rem_permit */
 	msn_rem_deny,			/* rem_deny */
-	msn_set_permit_deny,		/* set_permit_deny */
+	msn_set_permit_deny,	/* set_permit_deny */
 	NULL,					/* join_chat */
 	NULL,					/* reject chat invite */
 	NULL,					/* get_chat_name */
@@ -2152,10 +2271,11 @@
 	"MSN",                                            /**< name           */
 	VERSION,                                          /**< version        */
 	                                                  /**  summary        */
-	N_("MSN Protocol Plugin"),
+	N_("Windows Live Messenger Protocol Plugin"),
 	                                                  /**  description    */
-	N_("MSN Protocol Plugin"),
-	"Christian Hammond <chipx86@gnupdate.org>",       /**< author         */
+	N_("Windows Live Messenger Protocol Plugin"),
+	"Christian Hammond <chipx86@gnupdate.org>, "
+	"MaYuan <mayuan2006@gmail.com>",				  /**< author         */
 	PURPLE_WEBSITE,                                     /**< homepage       */
 
 	msn_load,                                         /**< load           */
@@ -2180,11 +2300,11 @@
 	PurpleAccountOption *option;
 
 	option = purple_account_option_string_new(_("Server"), "server",
-											MSN_SERVER);
+											WLM_SERVER);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 											   option);
 
-	option = purple_account_option_int_new(_("Port"), "port", 1863);
+	option = purple_account_option_int_new(_("Port"), "port", WLM_PORT);
 	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
 											   option);
 
--- a/libpurple/protocols/msn/msn.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/msn.h	Sun Sep 16 18:06:22 2007 +0000
@@ -62,12 +62,23 @@
 #define USEROPT_MSNPORT 4
 #define MSN_PORT 1863
 
+/* Windows Live Messenger Server*/
+#define WLM_SERVER			"muser.messenger.hotmail.com"
+#define WLM_PORT			1863
+#define WLM_PROT_VER		13
+/*This MSNP14 Support chat with Yahoo Messenger*/
+#define WLM_YAHOO_PROT_VER	14
+
+#define WLM_MAX_PROTOCOL	14
+#define WLM_MIN_PROTOCOL	13
+
 #define MSN_TYPING_RECV_TIMEOUT 6
 #define MSN_TYPING_SEND_TIMEOUT	4
 
-#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"
+#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"w3
 #define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login="
 #define PROFILE_URL "http://spaces.live.com/profile.aspx?mem="
+#define PHOTO_URL	" contactparams:photopreauthurl=\""
 
 #define USEROPT_HOTMAIL 0
 
@@ -87,9 +98,11 @@
 	MSN_LIST_FL_OP = 0x01,
 	MSN_LIST_AL_OP = 0x02,
 	MSN_LIST_BL_OP = 0x04,
-	MSN_LIST_RL_OP = 0x08
+	MSN_LIST_RL_OP = 0x08,
+	MSN_LIST_PL_OP = 0x10
 
 } MsnListOp;
+#define MSN_LIST_OP_MASK	0x07
 
 typedef enum
 {
@@ -130,4 +143,7 @@
 	 (MSN_CLIENT_ID_RESERVED_2 <<  8) | \
 	 (MSN_CLIENT_ID_CAPABILITIES))
 
+void msn_act_id(PurpleConnection *gc, const char *entry);
+void msn_send_privacy(PurpleConnection *gc);
+
 #endif /* _MSN_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Sun Sep 16 18:06:22 2007 +0000
@@ -0,0 +1,591 @@
+/**
+ * @file msnutils.c Utility functions
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+#include "msn.h"
+#include "msnutils.h"
+#include "time.h"
+//#include <openssl/md5.h>
+
+char *rand_guid(void);
+
+/**************************************************************************
+ * Util
+ **************************************************************************/
+char *
+rand_guid()
+{
+	return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111);
+}
+
+void
+msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
+{
+	char *cur;
+	GString *pre  = g_string_new(NULL);
+	GString *post = g_string_new(NULL);
+	unsigned int colors[3];
+
+	if (pre_ret  != NULL) *pre_ret  = NULL;
+	if (post_ret != NULL) *post_ret = NULL;
+
+	cur = strstr(mime, "FN=");
+
+	if (cur && (*(cur = cur + 3) != ';'))
+	{
+		pre = g_string_append(pre, "<FONT FACE=\"");
+
+		while (*cur && *cur != ';')
+		{
+			pre = g_string_append_c(pre, *cur);
+			cur++;
+		}
+
+		pre = g_string_append(pre, "\">");
+		post = g_string_prepend(post, "</FONT>");
+	}
+
+	cur = strstr(mime, "EF=");
+
+	if (cur && (*(cur = cur + 3) != ';'))
+	{
+		while (*cur && *cur != ';')
+		{
+			pre = g_string_append_c(pre, '<');
+			pre = g_string_append_c(pre, *cur);
+			pre = g_string_append_c(pre, '>');
+			post = g_string_prepend_c(post, '>');
+			post = g_string_prepend_c(post, *cur);
+			post = g_string_prepend_c(post, '/');
+			post = g_string_prepend_c(post, '<');
+			cur++;
+		}
+	}
+
+	cur = strstr(mime, "CO=");
+
+	if (cur && (*(cur = cur + 3) != ';'))
+	{
+		int i;
+
+		i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
+
+		if (i > 0)
+		{
+			char tag[64];
+
+			if (i == 1)
+			{
+				colors[1] = 0;
+				colors[2] = 0;
+			}
+			else if (i == 2)
+			{
+				unsigned int temp = colors[0];
+
+				colors[0] = colors[1];
+				colors[1] = temp;
+				colors[2] = 0;
+			}
+			else if (i == 3)
+			{
+				unsigned int temp = colors[2];
+
+				colors[2] = colors[0];
+				colors[0] = temp;
+			}
+
+			g_snprintf(tag, sizeof(tag),
+					   "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">",
+					   colors[0], colors[1], colors[2]);
+
+			pre = g_string_append(pre, tag);
+			post = g_string_prepend(post, "</FONT>");
+		}
+	}
+
+	cur = strstr(mime, "RL=");
+
+	if (cur && (*(cur = cur + 3) != ';'))
+	{
+		if (*cur == '1')
+		{
+			/* RTL text was received */
+			pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
+			post = g_string_prepend(post, "</SPAN>");
+		}
+	}
+
+	cur = g_strdup(purple_url_decode(pre->str));
+	g_string_free(pre, TRUE);
+
+	if (pre_ret != NULL)
+		*pre_ret = cur;
+	else
+		g_free(cur);
+
+	cur = g_strdup(purple_url_decode(post->str));
+	g_string_free(post, TRUE);
+
+	if (post_ret != NULL)
+		*post_ret = cur;
+	else
+		g_free(cur);
+}
+
+/*encode the str to RFC2047 style
+ * Currently only support the UTF-8 and base64 encode
+ */
+char *
+msn_encode_mime(const char *str)
+{
+	char *base64;
+	
+	base64 = purple_base64_encode((guchar *)str, strlen(str));
+	return g_strdup_printf("=?utf-8?B?%s?=", base64);
+}
+
+/*
+ * We need this because we're only supposed to encode spaces in the font
+ * names. purple_url_encode() isn't acceptable.
+ */
+static const char *
+encode_spaces(const char *str)
+{
+	static char buf[BUF_LEN];
+	const char *c;
+	char *d;
+
+	g_return_val_if_fail(str != NULL, NULL);
+
+	for (c = str, d = buf; *c != '\0'; c++)
+	{
+		if (*c == ' ')
+		{
+			*d++ = '%';
+			*d++ = '2';
+			*d++ = '0';
+		}
+		else
+			*d++ = *c;
+	}
+
+	return buf;
+}
+
+/*
+ * Taken from the zephyr plugin.
+ * This parses HTML formatting (put out by one of the gtkimhtml widgets
+ * and converts it to msn formatting. It doesn't deal with the tag closing,
+ * but gtkimhtml widgets give valid html.
+ * It currently deals properly with <b>, <u>, <i>, <font face=...>,
+ * <font color=...>, <span dir=...>, <span style="direction: ...">.
+ * It ignores <font back=...> and <font size=...>
+ */
+void
+msn_import_html(const char *html, char **attributes, char **message)
+{
+	int len, retcount = 0;
+	const char *c;
+	char *msg;
+	char *fontface = NULL;
+	char fonteffect[4];
+	char fontcolor[7];
+	char direction = '0';
+
+	gboolean has_bold = FALSE;
+	gboolean has_italic = FALSE;
+	gboolean has_underline = FALSE;
+	gboolean has_strikethrough = FALSE;
+
+	g_return_if_fail(html       != NULL);
+	g_return_if_fail(attributes != NULL);
+	g_return_if_fail(message    != NULL);
+
+	len = strlen(html);
+	msg = g_malloc0(len + 1);
+
+	memset(fontcolor, 0, sizeof(fontcolor));
+	strcat(fontcolor, "0");
+	memset(fonteffect, 0, sizeof(fonteffect));
+
+	for (c = html; *c != '\0';)
+	{
+		if (*c == '<')
+		{
+			if (!g_ascii_strncasecmp(c + 1, "br>", 3))
+			{
+				msg[retcount++] = '\r';
+				msg[retcount++] = '\n';
+				c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
+			{
+				if (!has_italic)
+				{
+					strcat(fonteffect, "I");
+					has_italic = TRUE;
+				}
+				c += 3;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
+			{
+				if (!has_bold)
+				{
+					strcat(fonteffect, "B");
+					has_bold = TRUE;
+				}
+				c += 3;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
+			{
+				if (!has_underline)
+				{
+					strcat(fonteffect, "U");
+					has_underline = TRUE;
+				}
+				c += 3;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
+			{
+				if (!has_strikethrough)
+				{
+					strcat(fonteffect, "S");
+					has_strikethrough = TRUE;
+				}
+				c += 3;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
+			{
+				c += 9;
+
+				if (!g_ascii_strncasecmp(c, "mailto:", 7))
+					c += 7;
+
+				while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
+					msg[retcount++] = *c++;
+
+				if (*c != '\0')
+					c += 2;
+
+				/* ignore descriptive string */
+				while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
+					c++;
+
+				if (*c != '\0')
+					c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "span", 4))
+			{
+				/* Bi-directional text support using CSS properties in span tags */
+				c += 5;
+
+				while (*c != '\0' && *c != '>')
+				{
+					while (*c == ' ')
+						c++;
+					if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
+					{
+						c += 9;
+						direction = '1';
+					}
+					else if (!g_ascii_strncasecmp(c, "style=\"", 7))
+					{
+						/* Parse inline CSS attributes */
+						char *attributes;
+						int attr_len = 0;
+						c += 7;
+						while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
+							attr_len++;
+						if (*(c + attr_len) == '"')
+						{
+							char *attr_dir;
+							attributes = g_strndup(c, attr_len);
+							attr_dir = purple_markup_get_css_property(attributes, "direction");
+							if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
+								direction = '1';
+							g_free(attr_dir);
+							g_free(attributes);
+						}
+
+					}
+					else
+					{
+						c++;
+					}
+				}
+				if (*c == '>')
+					c++;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "font", 4))
+			{
+				c += 5;
+
+				while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
+					c++;
+
+				if (!g_ascii_strncasecmp(c, "color=\"#", 7))
+				{
+					c += 8;
+
+					fontcolor[0] = *(c + 4);
+					fontcolor[1] = *(c + 5);
+					fontcolor[2] = *(c + 2);
+					fontcolor[3] = *(c + 3);
+					fontcolor[4] = *c;
+					fontcolor[5] = *(c + 1);
+
+					c += 8;
+				}
+				else if (!g_ascii_strncasecmp(c, "face=\"", 6))
+				{
+					const char *end = NULL;
+					const char *comma = NULL;
+					unsigned int namelen = 0;
+
+					c += 6;
+					end = strchr(c, '\"');
+					comma = strchr(c, ',');
+
+					if (comma == NULL || comma > end)
+						namelen = (unsigned int)(end - c);
+					else
+						namelen = (unsigned int)(comma - c);
+
+					fontface = g_strndup(c, namelen);
+					c = end + 2;
+				}
+				else
+				{
+					/* Drop all unrecognized/misparsed font tags */
+					while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
+						c++;
+
+					if (*c != '\0')
+						c += 2;
+				}
+			}
+			else
+			{
+				while ((*c != '\0') && (*c != '>'))
+					c++;
+				if (*c != '\0')
+					c++;
+			}
+		}
+		else if (*c == '&')
+		{
+			if (!g_ascii_strncasecmp(c, "&lt;", 4))
+			{
+				msg[retcount++] = '<';
+				c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c, "&gt;", 4))
+			{
+				msg[retcount++] = '>';
+				c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
+			{
+				msg[retcount++] = ' ';
+				c += 6;
+			}
+			else if (!g_ascii_strncasecmp(c, "&quot;", 6))
+			{
+				msg[retcount++] = '"';
+				c += 6;
+			}
+			else if (!g_ascii_strncasecmp(c, "&amp;", 5))
+			{
+				msg[retcount++] = '&';
+				c += 5;
+			}
+			else if (!g_ascii_strncasecmp(c, "&apos;", 6))
+			{
+				msg[retcount++] = '\'';
+				c += 6;
+			}
+			else
+				msg[retcount++] = *c++;
+		}
+		else
+			msg[retcount++] = *c++;
+	}
+
+	if (fontface == NULL)
+		fontface = g_strdup("MS Sans Serif");
+
+	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
+								  encode_spaces(fontface),
+								  fonteffect, fontcolor, direction);
+	*message = g_strdup(msg);
+
+	g_free(fontface);
+	g_free(msg);
+}
+
+void
+msn_parse_socket(const char *str, char **ret_host, int *ret_port)
+{
+	char *host;
+	char *c;
+	int port;
+
+	host = g_strdup(str);
+
+	if ((c = strchr(host, ':')) != NULL){
+		*c = '\0';
+		port = atoi(c + 1);
+	}else{
+		port = 1863;
+	}
+
+	*ret_host = host;
+	*ret_port = port;
+}
+/***************************************************************************
+ * MSN Time Related Funciton
+ ***************************************************************************/
+#if 0
+int
+msn_convert_iso8601(const char *timestr,struct tm tm_time)
+{
+	char temp[64];
+	struct tm ctime;
+	time_t ts;
+
+	purple_debug_info("MSNP14","convert string is{%s}\n",timestr);
+	tzset();
+	/*copy string first*/
+	memset(temp, 0, sizeof(temp));
+	strncpy(temp, timestr, strlen(timestr));
+
+	/*convert via strptime()*/
+	memset(&ctime, 0, sizeof(struct tm));
+	strptime(temp, "%d %b %Y %T %Z", &ctime);
+	ts = mktime(&ctime) - timezone;
+	localtime_r(&ts, tm_time);
+}
+#endif
+
+/***************************************************************************
+ * MSN Challenge Computing Function
+ ***************************************************************************/
+
+/*
+ * Handle MSN Chanllege computation
+ *This algorithm reference with http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
+ */
+#define BUFSIZE	256
+void 
+msn_handle_chl(char *input, char *output)
+{
+		PurpleCipher *cipher;
+		PurpleCipherContext *context;
+		char *productKey = MSNP13_WLM_PRODUCT_KEY,
+			 *productID  = MSNP13_WLM_PRODUCT_ID,
+			 *hexChars   = "0123456789abcdef",
+			 buf[BUFSIZE];
+		unsigned char md5Hash[16], *newHash;
+		unsigned int *md5Parts, *chlStringParts, newHashParts[5];
+
+		long long nHigh=0, nLow=0;
+
+		int i;
+
+		/* Create the MD5 hash by using Purple MD5 algorithm*/
+		cipher = purple_ciphers_find_cipher("md5");
+		context = purple_cipher_context_new(cipher, NULL);
+
+		purple_cipher_context_append(context, (const guchar *)input,
+						strlen(input));
+		purple_cipher_context_append(context, (const guchar *)productKey,
+						strlen(productKey));
+		purple_cipher_context_digest(context, sizeof(md5Hash), md5Hash, NULL);
+		purple_cipher_context_destroy(context);
+
+		/* Split it into four integers */
+		md5Parts = (unsigned int *)md5Hash;
+		for(i=0; i<4; i++){  
+				/* adjust endianess */
+				md5Parts[i] = GUINT_TO_LE(md5Parts[i]);
+
+				/* & each integer with 0x7FFFFFFF          */
+				/* and save one unmodified array for later */
+				newHashParts[i] = md5Parts[i];
+				md5Parts[i] &= 0x7FFFFFFF;
+		}
+
+		/* make a new string and pad with '0' */
+		snprintf(buf, BUFSIZE-5, "%s%s", input, productID);
+		i = strlen(buf);
+		memset(&buf[i], '0', 8 - (i % 8));
+		buf[i + (8 - (i % 8))]='\0';
+
+		/* split into integers */
+		chlStringParts = (unsigned int *)buf;
+
+		/* this is magic */
+		for (i=0; i<(strlen(buf)/4)-1; i+=2){
+				long long temp;
+
+				chlStringParts[i]   = GUINT_TO_LE(chlStringParts[i]);
+				chlStringParts[i+1] = GUINT_TO_LE(chlStringParts[i+1]);
+
+				temp=(md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF;
+				nHigh=(md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF;
+				nLow=nLow + nHigh + temp;
+		}
+		nHigh=(nHigh+md5Parts[1]) % 0x7FFFFFFF;
+		nLow=(nLow+md5Parts[3]) % 0x7FFFFFFF;
+
+		newHashParts[0]^=nHigh;
+		newHashParts[1]^=nLow;
+		newHashParts[2]^=nHigh;
+		newHashParts[3]^=nLow;
+
+		/* adjust endianness */
+		for(i=0; i<4; i++)
+				newHashParts[i] = GUINT_TO_LE(newHashParts[i]); 
+
+		/* make a string of the parts */
+		newHash = (unsigned char *)newHashParts;
+
+		/* convert to hexadecimal */
+		for (i=0; i<16; i++)
+		{
+				output[i*2]=hexChars[(newHash[i]>>4)&0xF];
+				output[(i*2)+1]=hexChars[newHash[i]&0xF];
+		}
+
+		output[32]='\0';
+
+//		purple_debug_info("MSNP14","chl output{%s}\n",output);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/msnutils.h	Sun Sep 16 18:06:22 2007 +0000
@@ -0,0 +1,60 @@
+/**
+ * @file msnutils.h Utility functions
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+#ifndef _MSN_UTILS_H_
+#define _MSN_UTILS_H_
+
+/*encode the str to RFC2047 style*/
+char * msn_encode_mime(const char *str);
+
+/**
+ * Generate the Random GUID
+ */
+char * rand_guid(void);
+
+/**
+ * Parses the MSN message formatting into a format compatible with Purple.
+ *
+ * @param mime     The mime header with the formatting.
+ * @param pre_ret  The returned prefix string.
+ * @param post_ret The returned postfix string.
+ *
+ * @return The new message.
+ */
+void msn_parse_format(const char *mime, char **pre_ret, char **post_ret);
+
+/**
+ * Parses the Purple message formatting (html) into the MSN format.
+ *
+ * @param html			The html message to format.
+ * @param attributes	The returned attributes string.
+ * @param message		The returned message string.
+ *
+ * @return The new message.
+ */
+void msn_import_html(const char *html, char **attributes, char **message);
+
+void msn_parse_socket(const char *str, char **ret_host, int *ret_port);
+void msn_handle_chl(char *input, char *output);
+
+#endif /* _MSN_UTILS_H_ */
--- a/libpurple/protocols/msn/nexus.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/nexus.c	Sun Sep 16 18:06:22 2007 +0000
@@ -22,9 +22,15 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #include "msn.h"
+#include "soap.h"
 #include "nexus.h"
 #include "notification.h"
 
+#undef NEXUS_LOGIN_TWN
+
+/*Local Function Prototype*/
+static void nexus_login_connect_cb(gpointer data, PurpleSslConnection *gsc,PurpleInputCondition cond);
+
 /**************************************************************************
  * Main
  **************************************************************************/
@@ -36,6 +42,9 @@
 
 	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);
 
@@ -45,24 +54,14 @@
 void
 msn_nexus_destroy(MsnNexus *nexus)
 {
-	if (nexus->gsc)
-		purple_ssl_close(nexus->gsc);
-
-	g_free(nexus->login_host);
-
-	g_free(nexus->login_path);
-
 	if (nexus->challenge_data != NULL)
 		g_hash_table_destroy(nexus->challenge_data);
 
-	if (nexus->input_handler > 0)
-		purple_input_remove(nexus->input_handler);
-	g_free(nexus->write_buf);
-	g_free(nexus->read_buf);
-
+	msn_soap_destroy(nexus->soapconn);
 	g_free(nexus);
 }
 
+#if 0 /* khc */
 /**************************************************************************
  * Util
  **************************************************************************/
@@ -121,285 +120,242 @@
 	nexus->written_cb(nexus, source, 0);
 }
 
+#endif
 /**************************************************************************
  * Login
  **************************************************************************/
-
 static void
-login_connect_cb(gpointer data, PurpleSslConnection *gsc,
-				 PurpleInputCondition cond);
+nexus_login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+{
+	MsnSoapConn * soapconn = data;
+	MsnSession *session;
+
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+	soapconn->gsc = NULL;
 
+	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 */
+}
+
+/*process the SOAP reply, get the Authentication Info*/
 static void
-login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+nexus_login_read_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
+	MsnSoapConn * soapconn = data;	
 	MsnNexus *nexus;
 	MsnSession *session;
 
-	nexus = data;
-	g_return_if_fail(nexus != NULL);
+	char *base, *c;
+	char *msn_twn_t,*msn_twn_p;
+	char *login_params;
+	char **elems, **cur, **tokens;
+	char * cert_str;
 
-	nexus->gsc = NULL;
-
+	nexus = soapconn->parent;
+	g_return_if_fail(nexus != NULL);
 	session = nexus->session;
 	g_return_if_fail(session != NULL);
 
-	msn_session_set_error(session, MSN_ERROR_AUTH, _("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 */
+	/*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*/
+	if (session->passport_info.t != NULL){
+			g_free(session->passport_info.t);
+	}
+	session->passport_info.t = g_strdup(msn_twn_t);
+
+	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);
+
+	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;
 }
 
 static void
 nexus_login_written_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
-	MsnNexus *nexus = data;
-	MsnSession *session;
-	int len;
+	MsnSoapConn * soapconn = data;	
 
-	session = nexus->session;
-	g_return_if_fail(session != NULL);
-
-	if (nexus->input_handler == 0)
-		/* TODO: Use purple_ssl_input_add()? */
-		nexus->input_handler = purple_input_add(nexus->gsc->fd,
-			PURPLE_INPUT_READ, nexus_login_written_cb, nexus);
+	soapconn->read_cb = nexus_login_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
 
 
-	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;
-
-	purple_ssl_close(nexus->gsc);
-	nexus->gsc = NULL;
-
-	purple_debug_misc("msn", "ssl buffer: {%s}\n", nexus->read_buf);
-
-	if (strstr(nexus->read_buf, "HTTP/1.1 302") != NULL)
-	{
-		/* Redirect. */
-		char *location, *c;
-
-		location = strstr(nexus->read_buf, "Location: ");
-		if (location == NULL)
-		{
-			g_free(nexus->read_buf);
-			nexus->read_buf = NULL;
-			nexus->read_len = 0;
-
-			return;
-		}
-		location = strchr(location, ' ') + 1;
-
-		if ((c = strchr(location, '\r')) != NULL)
-			*c = '\0';
-
-		/* Skip the http:// */
-		if ((c = strchr(location, '/')) != NULL)
-			location = c + 2;
-
-		if ((c = strchr(location, '/')) != NULL)
-		{
-			g_free(nexus->login_path);
-			nexus->login_path = g_strdup(c);
-
-			*c = '\0';
-		}
-
-		g_free(nexus->login_host);
-		nexus->login_host = g_strdup(location);
-
-		nexus->gsc = purple_ssl_connect(session->account,
-				nexus->login_host, PURPLE_SSL_DEFAULT_PORT,
-				login_connect_cb, login_error_cb, nexus);
-	}
-	else if (strstr(nexus->read_buf, "HTTP/1.1 401 Unauthorized") != NULL)
-	{
-		const char *error;
-
-		if ((error = strstr(nexus->read_buf, "WWW-Authenticate")) != NULL)
-		{
-			if ((error = strstr(error, "cbtxt=")) != NULL)
-			{
-				const char *c;
-				char *temp;
-
-				error += strlen("cbtxt=");
-
-				if ((c = strchr(error, '\n')) == NULL)
-					c = error + strlen(error);
-
-				temp = g_strndup(error, c - error);
-				error = purple_url_decode(temp);
-				g_free(temp);
-				if ((temp = strstr(error, " Do one of the following or try again:")) != NULL)
-					*temp = '\0';
-			}
-		}
-
-		msn_session_set_error(session, MSN_ERROR_AUTH, error);
-	}
-	else if (strstr(nexus->read_buf, "HTTP/1.1 503 Service Unavailable"))
-	{
-		msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
-	}
-	else if (strstr(nexus->read_buf, "HTTP/1.1 200 OK"))
-	{
-		char *base, *c;
-		char *login_params;
-
-#if 0
-		/* All your base are belong to us. */
-		base = buffer;
-
-		/* For great cookie! */
-		while ((base = strstr(base, "Set-Cookie: ")) != NULL)
-		{
-			base += strlen("Set-Cookie: ");
-
-			c = strchr(base, ';');
-
-			session->login_cookies =
-				g_list_append(session->login_cookies,
-							  g_strndup(base, c - base));
-		}
+/*when connect, do the SOAP Style windows Live ID authentication */
+void
+nexus_login_connect_cb(gpointer data, PurpleSslConnection *gsc,
+				 PurpleInputCondition cond)
+{
+	MsnSoapConn *soapconn;
+	MsnNexus * nexus;
+	MsnSession *session;
+	char *ru,*lc,*id,*tw,*ct,*kpp,*kv,*ver,*rn,*tpf;
+	char *fs0,*fs;
+	char *username, *password;
+	char *request_str, *head, *tail;
+#ifdef NEXUS_LOGIN_TWN
+	char *challenge_str;
+#else
+	char *rst1_str,*rst2_str,*rst3_str;
 #endif
 
-		base  = strstr(nexus->read_buf, "Authentication-Info: ");
+	purple_debug_info("MSN Nexus","Starting Windows Live ID authentication\n");
+
+	soapconn = data;
+	g_return_if_fail(soapconn != NULL);
+
+	nexus = soapconn->parent;
+	g_return_if_fail(nexus != NULL);
 
-		g_return_if_fail(base != NULL);
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+	msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
+
+	/*prepare the Windows Live ID authentication token*/
+	username = g_strdup(purple_account_get_username(session->account));
+	password = g_strdup(purple_connection_get_password(session->account->gc));
 
-		base  = strstr(base, "from-PP='");
-		base += strlen("from-PP='");
-		c     = strchr(base, '\'');
+	lc =	(char *)g_hash_table_lookup(nexus->challenge_data, "lc");
+	id =	(char *)g_hash_table_lookup(nexus->challenge_data, "id");
+	tw =	(char *)g_hash_table_lookup(nexus->challenge_data, "tw");
+	fs0=	(char *)g_hash_table_lookup(nexus->challenge_data, "fs");
+	ru =	(char *)g_hash_table_lookup(nexus->challenge_data, "ru");
+	ct =	(char *)g_hash_table_lookup(nexus->challenge_data, "ct");
+	kpp=	(char *)g_hash_table_lookup(nexus->challenge_data, "kpp");
+	kv =	(char *)g_hash_table_lookup(nexus->challenge_data, "kv");
+	ver=	(char *)g_hash_table_lookup(nexus->challenge_data, "ver");
+	rn =	(char *)g_hash_table_lookup(nexus->challenge_data, "rn");
+	tpf=	(char *)g_hash_table_lookup(nexus->challenge_data, "tpf");
 
-		login_params = g_strndup(base, c - base);
-
-		msn_got_login_params(session, login_params);
-
-		g_free(login_params);
-
+	/*
+	 * add some fail-safe code to avoid windows Purple Crash bug #1540454
+	 * If any of these string is NULL, will return Authentication Fail!
+	 * for when windows g_strdup_printf() implementation get NULL point,It crashed!
+	 */
+	if(!(lc && id && tw && ru && ct && kpp && kv && ver && tpf)){
+		purple_debug_error("MSN Nexus","WLM Authenticate Key Error!\n");
+		msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication Failed"));
+		g_free(username);
+		g_free(password);
+		purple_ssl_close(gsc);
 		msn_nexus_destroy(nexus);
 		session->nexus = NULL;
 		return;
 	}
 
-	g_free(nexus->read_buf);
-	nexus->read_buf = NULL;
-	nexus->read_len = 0;
-
-}
-
-/* this guards against missing hash entries */
-static char *
-nexus_challenge_data_lookup(GHashTable *challenge_data, const char *key)
-{
-	char *entry;
-
-	return (entry = (char *)g_hash_table_lookup(challenge_data, key)) ?
-		entry : "(null)";
-}
+	/*
+	 * in old MSN NS server's "USR TWN S" return,didn't include fs string
+	 * so we use a default "1" for fs.
+	 */
+	if(fs0){
+		fs = g_strdup(fs0);
+	}else{
+		fs = g_strdup("1");
+	}
 
-void
-login_connect_cb(gpointer data, PurpleSslConnection *gsc,
-				 PurpleInputCondition cond)
-{
-	MsnNexus *nexus;
-	MsnSession *session;
-	char *username, *password;
-	char *request_str, *head, *tail;
-	char *buffer = NULL;
-	guint32 ctint;
-
-	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_GET_COOKIE);
+#ifdef NEXUS_LOGIN_TWN
+	challenge_str = g_strdup_printf(
+		"lc=%s&amp;id=%s&amp;tw=%s&amp;fs=%s&amp;ru=%s&amp;ct=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s&amp;tpf=%s\r\n",
+		lc,id,tw,fs,ru,ct,kpp,kv,ver,rn,tpf
+		);
 
-	username =
-		g_strdup(purple_url_encode(purple_account_get_username(session->account)));
-
-	password =
-		g_strdup(purple_url_encode(purple_connection_get_password(session->account->gc)));
-
-	ctint = strtoul((char *)g_hash_table_lookup(nexus->challenge_data, "ct"), NULL, 10) + 200;
-
-	head = g_strdup_printf(
-		"GET %s HTTP/1.1\r\n"
-		"Authorization: Passport1.4 OrgVerb=GET,OrgURL=%s,sign-in=%s",
-		nexus->login_path,
-		(char *)g_hash_table_lookup(nexus->challenge_data, "ru"),
-		username);
+	/*build the SOAP windows Live ID XML body */
+	tail = g_strdup_printf(TWN_ENVELOP_TEMPLATE,username,password,challenge_str	);
+	g_free(challenge_str);
+#else
+	rst1_str = g_strdup_printf(
+		"id=%s&amp;tw=%s&amp;fs=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s",
+		id,tw,fs,kpp,kv,ver,rn
+		);
+	rst2_str = g_strdup_printf(
+		"fs=%s&amp;id=%s&amp;kv=%s&amp;rn=%s&amp;tw=%s&amp;ver=%s",
+		fs,id,kv,rn,tw,ver
+		);
+	rst3_str = g_strdup_printf("id=%s",id);
+	tail = g_strdup_printf(TWN_LIVE_ENVELOP_TEMPLATE,username,password,rst1_str,rst2_str,rst3_str);
+	g_free(rst1_str);
+	g_free(rst2_str);
+	g_free(rst3_str);
+#endif
+	g_free(fs);
 
-	tail = g_strdup_printf(
-		"lc=%s,id=%s,tw=%s,fs=%s,ru=%s,ct=%" G_GUINT32_FORMAT ",kpp=%s,kv=%s,ver=%s,tpf=%s\r\n"
-		"User-Agent: MSMSGS\r\n"
-		"Host: %s\r\n"
-		"Connection: Keep-Alive\r\n"
-		"Cache-Control: no-cache\r\n",
-		nexus_challenge_data_lookup(nexus->challenge_data, "lc"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "id"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "tw"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "fs"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "ru"),
-		ctint,
-		nexus_challenge_data_lookup(nexus->challenge_data, "kpp"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "kv"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "ver"),
-		nexus_challenge_data_lookup(nexus->challenge_data, "tpf"),
-		nexus->login_host);
+	soapconn->login_path = g_strdup(TWN_POST_URL);
+	head = 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: %d\r\n"
+					"Connection: Keep-Alive\r\n"
+					"Cache-Control: no-cache\r\n\r\n",
+					soapconn->login_path,soapconn->login_host,(int)strlen(tail));
 
-	buffer = g_strdup_printf("%s,pwd=XXXXXXXX,%s\r\n", head, tail);
-	request_str = g_strdup_printf("%s,pwd=%s,%s\r\n", head, password, tail);
+	request_str = g_strdup_printf("%s%s", head,tail);
 
-	purple_debug_misc("msn", "Sending: {%s}\n", buffer);
-
-	g_free(buffer);
+#ifdef MSN_SOAP_DEBUG	
+	purple_debug_misc("MSN Nexus", "TWN Sending:\n%s\n", request_str);
+#endif
 	g_free(head);
 	g_free(tail);
 	g_free(username);
 	g_free(password);
 
-	nexus->write_buf = request_str;
-	nexus->written_len = 0;
-
-	nexus->read_len = 0;
-
-	nexus->written_cb = nexus_login_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);
+	/*prepare to send the SOAP request*/
+	msn_soap_write(soapconn,request_str,nexus_login_written_cb);
 
 	return;
-
-
 }
 
+#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;
 
@@ -408,6 +364,7 @@
 		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);
 
@@ -470,10 +427,13 @@
 }
 
 
+#endif
+
 /**************************************************************************
  * Connect
  **************************************************************************/
 
+#if 0 /* khc */
 static void
 nexus_connect_cb(gpointer data, PurpleSslConnection *gsc,
 				 PurpleInputCondition cond)
@@ -502,10 +462,13 @@
 	nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
 }
 
+#endif
+
 void
 msn_nexus_connect(MsnNexus *nexus)
 {
-	nexus->gsc = purple_ssl_connect(nexus->session->account,
-			"nexus.passport.com", PURPLE_SSL_DEFAULT_PORT,
-			nexus_connect_cb, login_error_cb, nexus);
+	/*  Authenticate via Windows Live ID. */
+	purple_debug_info("MSN Nexus","msn_nexus_connect()\n");
+	msn_soap_init(nexus->soapconn,MSN_TWN_SERVER,1,nexus_login_connect_cb,nexus_login_error_cb);
+	msn_soap_connect(nexus->soapconn);
 }
--- a/libpurple/protocols/msn/nexus.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/nexus.h	Sun Sep 16 18:06:22 2007 +0000
@@ -24,25 +24,124 @@
 #ifndef _MSN_NEXUS_H_
 #define _MSN_NEXUS_H_
 
+#include "soap.h"
+
+/*#define MSN_TWN_SERVER	"loginnet.passport.com"*/
+#define MSN_TWN_SERVER	"login.live.com"
+
+#define TWN_START_TOKEN		"<wsse:BinarySecurityToken Id=\"PPToken1\">"
+#define TWN_END_TOKEN		"</wsse:BinarySecurityToken>"
+
+#define TWN_POST_URL			"/RST.srf"
+#define TWN_ENVELOP_TEMPLATE 	"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"\
+						"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\
+						"<Header>"\
+						"<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\
+						"<ps:HostingApp>{3:B}</ps:HostingApp>"\
+						"<ps:BinaryVersion>4</ps:BinaryVersion>"\
+						"<ps:UIVersion>1</ps:UIVersion>"\
+						"<ps:Cookies></ps:Cookies>"\
+						"<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>"\
+						"</ps:AuthInfo>"\
+						"<wsse:Security>"\
+						"<wsse:UsernameToken Id=\"user\">"\
+						"<wsse:Username>%s</wsse:Username>"\
+						"<wsse:Password>%s</wsse:Password>"\
+						"</wsse:UsernameToken>"\
+						"</wsse:Security>"\
+						"</Header>"\
+						"<Body>"\
+						"<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\
+						"<wst:RequestSecurityToken Id=\"RST0\">"\
+						"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+						"<wsp:AppliesTo>"\
+						"<wsa:EndpointReference>"\
+						"<wsa:Address>http://Passport.NET/tb</wsa:Address>"\
+						"</wsa:EndpointReference>"\
+						"</wsp:AppliesTo>"\
+						"</wst:RequestSecurityToken>"\
+						"<wst:RequestSecurityToken Id=\"RST1\">"\
+						"<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+						"<wsp:AppliesTo>"\
+						"<wsa:EndpointReference>"\
+						"<wsa:Address>messenger.msn.com</wsa:Address>"\
+						"</wsa:EndpointReference>"\
+						"</wsp:AppliesTo>"\
+						"<wsse:PolicyReference URI=\"?%s\">"\
+						"</wsse:PolicyReference>"\
+						"</wst:RequestSecurityToken>"\
+						"</ps:RequestMultipleSecurityTokens>"\
+						"</Body>"\
+						"</Envelope>"
+
+#define TWN_LIVE_START_TOKEN	"<wsse:BinarySecurityToken Id=\"PPToken1\">"
+#define TWN_LIVE_END_TOKEN	"</wsse:BinarySecurityToken>"
+#define TWN_LIVE_ENVELOP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"\
+"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\
+  "<Header>"\
+    "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\
+      "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>"\
+      "<ps:BinaryVersion>4</ps:BinaryVersion>"\
+      "<ps:UIVersion>1</ps:UIVersion>"\
+      "<ps:Cookies></ps:Cookies>"\
+      "<ps:RequestParams>AQAAAAIAAABsYwQAAAAyMDUy</ps:RequestParams>"\
+    "</ps:AuthInfo>"\
+    "<wsse:Security>"\
+      "<wsse:UsernameToken Id=\"user\">"\
+        "<wsse:Username>%s</wsse:Username>"\
+        "<wsse:Password>%s</wsse:Password>"\
+      "</wsse:UsernameToken>"\
+    "</wsse:Security>"\
+  "</Header>"\
+  "<Body>"\
+    "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\
+      "<wst:RequestSecurityToken Id=\"RST0\">"\
+        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+        "<wsp:AppliesTo>"\
+          "<wsa:EndpointReference>"\
+            "<wsa:Address>http://Passport.NET/tb</wsa:Address>"\
+          "</wsa:EndpointReference>"\
+        "</wsp:AppliesTo>"\
+      "</wst:RequestSecurityToken>"\
+      "<wst:RequestSecurityToken Id=\"RST1\">"\
+        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+        "<wsp:AppliesTo>"\
+          "<wsa:EndpointReference>"\
+            "<wsa:Address>messenger.msn.com</wsa:Address>"\
+          "</wsa:EndpointReference>"\
+        "</wsp:AppliesTo>"\
+        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
+      "</wst:RequestSecurityToken>"\
+      "<wst:RequestSecurityToken Id=\"RST2\">"\
+        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+        "<wsp:AppliesTo>"\
+          "<wsa:EndpointReference>"\
+            "<wsa:Address>contacts.msn.com</wsa:Address>"\
+         "</wsa:EndpointReference>"\
+        "</wsp:AppliesTo>"\
+       "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
+     " </wst:RequestSecurityToken>"\
+      "<wst:RequestSecurityToken Id=\"RST3\">"\
+        "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+        "<wsp:AppliesTo>"\
+          "<wsa:EndpointReference>"\
+            "<wsa:Address>voice.messenger.msn.com</wsa:Address>"\
+          "</wsa:EndpointReference>"\
+       " </wsp:AppliesTo>"\
+        "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
+      "</wst:RequestSecurityToken>"\
+    "</ps:RequestMultipleSecurityTokens>"\
+  "</Body>"\
+"</Envelope>"
+
 typedef struct _MsnNexus MsnNexus;
 
 struct _MsnNexus
 {
 	MsnSession *session;
-
-	char *login_host;
-	char *login_path;
+	MsnSoapConn *soapconn;
+	char * challenge_data_str;
 	GHashTable *challenge_data;
-	PurpleSslConnection *gsc;
-
-	guint input_handler;
-
-	char *write_buf;
-	gsize written_len;
-	PurpleInputFunction written_cb;
-
-	char *read_buf;
-	gsize read_len;
 };
 
 void msn_nexus_connect(MsnNexus *nexus);
--- a/libpurple/protocols/msn/notification.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Sun Sep 16 18:06:22 2007 +0000
@@ -25,7 +25,7 @@
 #include "notification.h"
 #include "state.h"
 #include "error.h"
-#include "msn-utils.h"
+#include "msnutils.h"
 #include "page.h"
 
 #include "userlist.h"
@@ -34,6 +34,15 @@
 
 static MsnTable *cbs_table;
 
+/****************************************************************************
+ * 	Local Function Prototype
+ ****************************************************************************/
+
+static void msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len);
+static void
+msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport,
+					 MsnListOp list_op, MsnUserType type);
+
 /**************************************************************************
  * Main
  **************************************************************************/
@@ -102,9 +111,11 @@
 	account = session->account;
 
 	/* Allocate an array for CVR0, NULL, and all the versions */
-	a = c = g_new0(char *, session->protocol_ver - 8 + 3);
+//	a = c = g_new0(char *, session->protocol_ver - WLM_MIN_PROTOCOL + 3);
+	a = c = g_new0(char *, WLM_MAX_PROTOCOL - WLM_MIN_PROTOCOL + 3);
 
-	for (i = session->protocol_ver; i >= 8; i--)
+//	for (i = session->protocol_ver; i >= WLM_MIN_PROTOCOL; i--)
+	for (i = WLM_MAX_PROTOCOL; i >= WLM_MIN_PROTOCOL; i--)
 		*c++ = g_strdup_printf("MSNP%d", i);
 
 	*c++ = g_strdup("CVR0");
@@ -112,9 +123,13 @@
 	vers = g_strjoinv(" ", a);
 
 	if (session->login_step == MSN_LOGIN_STEP_START)
+	{
 		msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE);
+	}
 	else
+	{
 		msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE2);
+	}
 
 	msn_cmdproc_send(cmdproc, "VER", "%s", vers);
 
@@ -153,7 +168,7 @@
  **************************************************************************/
 
 static void
-group_error_helper(MsnSession *session, const char *msg, int group_id, int error)
+group_error_helper(MsnSession *session, const char *msg, const char *group_id, int error)
 {
 	PurpleAccount *account;
 	PurpleConnection *gc;
@@ -172,9 +187,7 @@
 		else
 		{
 			const char *group_name;
-			group_name =
-				msn_userlist_find_group_name(session->userlist,
-											 group_id);
+			group_name = msn_userlist_find_group_name(session->userlist,group_id);
 			reason = g_strdup_printf(_("%s is not a valid group."),
 									 group_name);
 		}
@@ -214,7 +227,6 @@
 	PurpleAccount *account;
 
 	account = cmdproc->session->account;
-
 	msn_cmdproc_send(cmdproc, "USR", "TWN I %s",
 					 purple_account_get_username(account));
 }
@@ -232,14 +244,17 @@
 
 	if (!g_ascii_strcasecmp(cmd->params[1], "OK"))
 	{
-		/* OK */
+		/* authenticate OK */
+		/* friendly name part no longer true in msnp11 */
+#if 0
 		const char *friendly = purple_url_decode(cmd->params[3]);
 
 		purple_connection_set_display_name(gc, friendly);
-
+#endif
 		msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN);
 
-		msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
+//		msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
+		//TODO we should use SOAP contact to fetch contact list
 	}
 	else if (!g_ascii_strcasecmp(cmd->params[1], "TWN"))
 	{
@@ -249,15 +264,20 @@
 		session->nexus = msn_nexus_new(session);
 
 		/* Parse the challenge data. */
-
+		session->nexus->challenge_data_str = g_strdup(cmd->params[3]);
 		elems = g_strsplit(cmd->params[3], ",", 0);
 
 		for (cur = elems; *cur != NULL; cur++)
 		{
-				tokens = g_strsplit(*cur, "=", 2);
+			tokens = g_strsplit(*cur, "=", 2);
+			if(tokens[0]&&tokens[1])
+			{
+				purple_debug_info("MSNP14","challenge %p,key:%s,value:%s\n",
+									session->nexus->challenge_data,tokens[0],tokens[1]);
 				g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]);
-				/* Don't free each of the tokens, only the array. */
-				g_free(tokens);
+			}
+			/* Don't free each of the tokens, only the array. */
+			g_free(tokens);
 		}
 
 		g_strfreev(elems);
@@ -322,8 +342,15 @@
 		return;
 	}
 
+	/*
+	 * Windows Live Messenger 8.0 
+	 * Notice :CVR String discriminate!
+	 * reference of http://www.microsoft.com/globaldev/reference/oslocversion.mspx
+	 * to see the Local ID
+	 */
 	msn_cmdproc_send(cmdproc, "CVR",
-					 "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s",
+//					 "0x0409 winnt 5.1 i386 MSG80BETA 8.0.0689 msmsgs %s",
+					"0x0804 winnt 5.1 i386 MSNMSGR 8.0.0792 msmsgs %s",
 					 purple_account_get_username(account));
 }
 
@@ -366,7 +393,7 @@
 
 	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
 
-	msn_message_parse_payload(msg, payload, len);
+	msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM);
 #ifdef MSN_DEBUG_NS
 	msn_message_show_readable(msg, "Notification", TRUE);
 #endif
@@ -379,9 +406,12 @@
 static void
 msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
+	purple_debug_info("MSNP14","Processing MSG... \n");
+	if(cmd->payload_len == 0){
+		return;
+	}
 	/* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued
 	 * command and we are processing it */
-
 	if (cmd->payload == NULL)
 	{
 		cmdproc->last_cmd->payload_cb  = msg_cmd_post;
@@ -391,32 +421,145 @@
 	{
 		g_return_if_fail(cmd->payload_cb != NULL);
 
+		purple_debug_info("MSNP14","MSG payload:{%s}\n",cmd->payload);
 		cmd->payload_cb(cmdproc, cmd, cmd->payload, cmd->payload_len);
 	}
 }
 
+/*send Message to Yahoo Messenger*/
+void
+uum_send_msg(MsnSession *session,MsnMessage *msg)
+{
+	MsnCmdProc *cmdproc;
+	MsnTransaction *trans;
+	char *payload;
+	gsize payload_len;
+	int type;
+	
+	cmdproc = session->notification->cmdproc;
+	g_return_if_fail(msg     != NULL);
+	payload = msn_message_gen_payload(msg, &payload_len);
+	purple_debug_info("MSNP14","send UUM,payload{%s},strlen:%d,len:%d\n",
+		payload,strlen(payload),payload_len);
+	type = msg->type;
+	trans = msn_transaction_new(cmdproc, "UUM","%s 32 %d %d",msg->remote_user,type,payload_len);
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+	msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+static void
+ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+			 size_t len)
+{
+	MsnMessage *msg;
+	PurpleConnection *gc;
+	const char *passport;
+	const char *content_type;
+
+	purple_debug_info("MSNP14","Process UBM payload:%s\n",payload);
+	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
+
+	msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM);
+#ifdef MSN_DEBUG_NS
+	msn_message_show_readable(msg, "Notification", TRUE);
+#endif
+
+	gc = cmdproc->session->account->gc;
+	passport = msg->remote_user;
+
+	content_type = msn_message_get_content_type(msg);
+	purple_debug_info("MSNP14","type:%d\n",content_type);
+	if(!strcmp(content_type,"text/plain")){
+		const char *value;
+		const char *body;
+		char *body_str;
+		char *body_enc;
+		char *body_final = NULL;
+		size_t body_len;
+
+		body = msn_message_get_bin_data(msg, &body_len);
+		body_str = g_strndup(body, body_len);
+		body_enc = g_markup_escape_text(body_str, -1);
+		g_free(body_str);
+
+		if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)	{
+			char *pre, *post;
+
+			msn_parse_format(value, &pre, &post);
+			body_final = g_strdup_printf("%s%s%s", pre ? pre : "",
+							body_enc ? body_enc : "", post ? post : "");
+			g_free(pre);
+			g_free(post);
+		}
+		g_free(body_enc);
+		serv_got_im(gc, passport, body_final, 0, time(NULL));
+	}
+	if(!strcmp(content_type,"text/x-msmsgscontrol")){
+		if(msn_message_get_attr(msg, "TypingUser") != NULL){
+			serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
+						PURPLE_TYPING);
+		}
+	}
+	if(!strcmp(content_type,"text/x-msnmsgr-datacast")){
+		char *username, *str;
+		PurpleAccount *account;
+		PurpleBuddy *buddy;
+		const char *user;
+
+		account = cmdproc->session->account;
+		user = msg->remote_user;
+
+		if ((buddy = purple_find_buddy(account, user)) != NULL){
+			username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
+		}else{
+			username = g_markup_escape_text(user, -1);
+		}
+
+		str = g_strdup_printf(_("%s just sent you a Nudge!"), username);
+		g_free(username);
+		msn_session_report_user(cmdproc->session,user,str,PURPLE_MESSAGE_SYSTEM);
+		g_free(str);
+	}
+	msn_message_destroy(msg);
+}
+
+/*Yahoo msg process*/
+static void
+ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MSNP14","Processing UBM... \n");
+	if(cmd->payload_len == 0){
+		return;
+	}
+	/* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued
+	 * command and we are processing it */
+	if (cmd->payload == NULL){
+		cmdproc->last_cmd->payload_cb  = ubm_cmd_post;
+		cmdproc->servconn->payload_len = atoi(cmd->params[2]);
+	}else{
+		g_return_if_fail(cmd->payload_cb != NULL);
+
+		purple_debug_info("MSNP14","UBM payload:{%s}\n",cmd->payload);
+		ubm_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len);
+	}
+}
+
 /**************************************************************************
  * Challenges
+ *  we use MD5 to caculate the Challenges
  **************************************************************************/
-
 static void
 chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnTransaction *trans;
 	char buf[33];
-	const char *challenge_resp;
-	PurpleCipher *cipher;
-	PurpleCipherContext *context;
-	guchar digest[16];
-	int i;
 
+#if 0
 	cipher = purple_ciphers_find_cipher("md5");
 	context = purple_cipher_context_new(cipher, NULL);
-
 	purple_cipher_context_append(context, (const guchar *)cmd->params[1],
 							   strlen(cmd->params[1]));
-
-	challenge_resp = "VT6PX?UQTM4WM%YR";
+	challenge_resp = MSNP13_WLM_PRODUCT_KEY;
 
 	purple_cipher_context_append(context, (const guchar *)challenge_resp,
 							   strlen(challenge_resp));
@@ -424,9 +567,14 @@
 	purple_cipher_context_destroy(context);
 
 	for (i = 0; i < 16; i++)
+	{
 		g_snprintf(buf + (i*2), 3, "%02x", digest[i]);
-
-	trans = msn_transaction_new(cmdproc, "QRY", "%s 32", "PROD0038W!61ZTF9");
+	}
+#else
+	msn_handle_chl(cmd->params[1], buf);
+#endif
+//	purple_debug_info("MSNP14","<<challenge:{%s}:{%s}\n",cmd->params[1],buf);
+	trans = msn_transaction_new(cmdproc, "QRY", "%s 32", MSNP13_WLM_PRODUCT_ID);
 
 	msn_transaction_set_payload(trans, buf, 32);
 
@@ -436,43 +584,301 @@
 /**************************************************************************
  * Buddy Lists
  **************************************************************************/
+/* add contact to xmlnode */
+static void
+msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnUserType type)
+{
+	xmlnode *d_node,*c_node;
+	char **tokens;
+	char *email,*domain;
+	char *list_op_str,*type_str;
+
+	purple_debug_info("MSNP14","Passport: %s, type: %d\n", passport, type);
+	tokens = g_strsplit(passport, "@", 2);
+	email = tokens[0];
+	domain = tokens[1];
+
+	/*find a domain Node*/
+	for(d_node = xmlnode_get_child(mlNode,"d"); d_node; d_node = xmlnode_get_next_twin(d_node))
+	{
+		const char * attr = NULL;
+		purple_debug_info("MSNP14","d_node: %s\n",d_node->name);
+		attr = xmlnode_get_attrib(d_node,"n");
+		if(attr == NULL){
+			continue;
+		}
+		if(!strcmp(attr,domain)){
+			break;
+		}
+	}
+	if(d_node == NULL)
+	{
+		/*domain not found, create a new domain Node*/
+		purple_debug_info("MSNP14","get No d_node\n");
+		d_node = xmlnode_new("d");
+		xmlnode_set_attrib(d_node,"n",domain);
+		xmlnode_insert_child(mlNode,d_node);
+	}
+
+	/*create contact node*/
+	c_node = xmlnode_new("c");
+	xmlnode_set_attrib(c_node,"n",email);
+
+	list_op_str = g_strdup_printf("%d",list_op);
+	purple_debug_info("MSNP14","list_op: %d\n",list_op);
+	xmlnode_set_attrib(c_node,"l",list_op_str);
+	g_free(list_op_str);
+
+	if (type != MSN_USER_TYPE_UNKNOWN) {
+		type_str = g_strdup_printf("%d", type);
+	} else {
+		if (msn_user_is_yahoo(session->account, passport))
+			type_str = g_strdup_printf("%d", MSN_USER_TYPE_YAHOO);
+		else
+			type_str = g_strdup_printf("%d", MSN_USER_TYPE_PASSPORT);
+	}
+	/*mobile*/
+	//type_str = g_strdup_printf("4");
+	xmlnode_set_attrib(c_node,"t",type_str);
+	g_free(type_str);
+
+	xmlnode_insert_child(d_node, c_node);
+
+	g_strfreev(tokens);
+}
 
 static void
-add_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len)
+{
+	MsnTransaction *trans;
+	purple_debug_info("MSN Notification","Sending ADL with payload: %s\n", payload);
+	trans = msn_transaction_new(cmdproc, "ADL","%d", strlen(payload));
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+	msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+/*dump contact info to NS*/
+void
+msn_notification_dump_contact(MsnSession *session)
+{
+	MsnUser *user;
+	GList *l;
+	xmlnode *adl_node;
+	char *payload;
+	int payload_len;
+	int adl_count = 0;
+	const char *display_name;
+
+	adl_node = xmlnode_new("ml");
+	adl_node->child = NULL;
+	xmlnode_set_attrib(adl_node, "l", "1");
+
+	/*get the userlist*/
+	for (l = session->userlist->users; l != NULL; l = l->next){
+		user = l->data;
+
+		/* skip RL & PL during initial dump */
+		if (!(user->list_op & MSN_LIST_OP_MASK))
+			continue;
+
+		msn_add_contact_xml(session, adl_node, user->passport,
+			user->list_op & MSN_LIST_OP_MASK, user->type);
+
+		/* each ADL command may contain up to 150 contacts */
+		if (++adl_count % 150 == 0 || l->next == NULL) {
+			payload = xmlnode_to_str(adl_node,&payload_len);
+
+			msn_notification_post_adl(session->notification->cmdproc,
+				payload, payload_len);
+
+			g_free(payload);
+			xmlnode_free(adl_node);
+
+			if (l->next) {
+				adl_node = xmlnode_new("ml");
+				adl_node->child = NULL;
+				xmlnode_set_attrib(adl_node, "l", "1");
+			}
+		}
+	}
+
+	if (adl_count == 0) {
+		payload = xmlnode_to_str(adl_node,&payload_len);
+
+		msn_notification_post_adl(session->notification->cmdproc, payload, payload_len);
+
+		g_free(payload);
+		xmlnode_free(adl_node);
+	}
+
+	display_name = purple_connection_get_display_name(session->account->gc);
+	if (display_name 
+	    && strcmp(display_name, 
+		      purple_account_get_username(session->account))) {
+		msn_act_id(session->account->gc, display_name);
+	}
+
+}
+
+/*Post FQY to NS,Inform add a Yahoo User*/
+void
+msn_notification_send_fqy(MsnSession *session, const char *passport)
+{
+	MsnTransaction *trans;
+	MsnCmdProc *cmdproc;
+	char* email,*domain,*payload;
+	char **tokens;
+
+	cmdproc = session->notification->cmdproc;
+
+	tokens = g_strsplit(passport, "@", 2);
+	email = tokens[0];
+	domain = tokens[1];
+
+	payload = g_strdup_printf("<ml><d n=\"%s\"><c n=\"%s\"/></d></ml>", domain, email);
+	trans = msn_transaction_new(cmdproc, "FQY","%d", strlen(payload));
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+	msn_cmdproc_send_trans(cmdproc, trans);
+
+	g_free(payload);
+	g_free(tokens);
+}
+
+static void
+blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+}
+
+static void
+adl_cmd_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+		                         size_t len)
+{
+	xmlnode *root, *domain_node;
+
+	purple_debug_misc("MSN Notification", "Parsing received ADL XML data\n");
+
+	g_return_if_fail(payload != NULL);
+	
+	root = xmlnode_from_str(payload, (gssize) len);
+	
+	if (root == NULL) {
+		purple_debug_info("MSN Notification", "Invalid XML!\n");
+		return;
+	}
+	for (domain_node = xmlnode_get_child(root, "d"); domain_node; domain_node = xmlnode_get_next_twin(domain_node)) {
+		const gchar * domain = NULL; 
+		xmlnode *contact_node = NULL;
+
+		domain = xmlnode_get_attrib(domain_node, "n");
+
+		for (contact_node = xmlnode_get_child(domain_node, "c"); contact_node; contact_node = xmlnode_get_next_twin(contact_node)) {
+//			gchar *name = NULL, *friendlyname = NULL, *passport= NULL;
+			const gchar *list;
+			gint list_op = 0;
+
+//			name = xmlnode_get_attrib(contact_node, "n");
+			list = xmlnode_get_attrib(contact_node, "l");
+			if (list != NULL) {
+				list_op = atoi(list);
+			}
+//			friendlyname = xmlnode_get_attrib(contact_node, "f");
+
+//			passport = g_strdup_printf("%s@%s", name, domain);
+
+//			if (friendlyname != NULL) {
+//				decoded_friendlyname = g_strdup(purple_url_decode(friendlyname));
+//			} else {
+//				decoded_friendlyname = g_strdup(passport);
+//			}
+
+			if (list_op & MSN_LIST_RL_OP) {
+				/* someone is adding us */
+//				got_new_entry(cmdproc->session->account->gc, passport, decoded_friendly_name);
+				msn_get_contact_list(cmdproc->session->contact, MSN_PS_PENDING_LIST, NULL);
+			}
+
+//			g_free(decoded_friendly_name);
+//			g_free(passport);
+		}
+	}
+
+	xmlnode_free(root);
+}
+
+static void
+adl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnSession *session;
-	MsnUser *user;
-	const char *list;
-	const char *passport;
-	const char *friendly;
-	MsnListId list_id;
-	int group_id;
 
-	list     = cmd->params[1];
-	passport = cmd->params[3];
-	friendly = purple_url_decode(cmd->params[4]);
+	g_return_if_fail(cmdproc != NULL);
+	g_return_if_fail(cmdproc->session != NULL);
+	g_return_if_fail(cmdproc->last_cmd != NULL);
+	g_return_if_fail(cmd != NULL);
 
 	session = cmdproc->session;
 
-	user = msn_userlist_find_user(session->userlist, passport);
+	if ( !strcmp(cmd->params[1], "OK")) {
+		/* ADL ack */
+		msn_session_finish_login(session);
+	} else {
+		cmdproc->last_cmd->payload_cb = adl_cmd_parse;
+	}
+
+	return;
+}
 
-	if (user == NULL)
-	{
-		user = msn_user_new(session->userlist, passport, friendly);
-		msn_userlist_add_user(session->userlist, user);
-	}
-	else
-		msn_user_set_friendly_name(user, friendly);
+static void
+adl_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+	MsnSession *session;
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	char *reason = NULL;
+
+	session = cmdproc->session;
+	account = session->account;
+	gc = purple_account_get_connection(account);
+
+	purple_debug_error("msn","ADL error\n");
+	reason = g_strdup_printf(_("Unknown error (%d)"), error);
+	purple_notify_error(gc, NULL, _("Unable to add user"), reason);
+	g_free(reason);
+}
 
-	list_id = msn_get_list_id(list);
+static void
+fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+			 size_t len)
+{
+	purple_debug_info("MSN Notification","FQY payload:\n%s\n", payload);
+	g_return_if_fail(cmdproc->session != NULL);
+	g_return_if_fail(cmdproc->session->contact != NULL);
+//	msn_notification_post_adl(cmdproc, payload, len);
+//	msn_get_address_book(cmdproc->session->contact, MSN_AB_SAVE_CONTACT, NULL, NULL);
+}
 
-	if (cmd->param_count >= 6)
-		group_id = atoi(cmd->params[5]);
-	else
-		group_id = -1;
+static void
+fqy_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MSNP14","Process FQY\n");
+	cmdproc->last_cmd->payload_cb = fqy_cmd_post;
+}
 
-	msn_got_add_user(session, user, list_id, group_id);
-	msn_user_update(user);
+static void
+rml_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+#if 0
+	MsnTransaction *trans;
+	char * payload;
+#endif
+
+	purple_debug_info("MSNP14","Process RML\n");
+#if 0
+	trans = msn_transaction_new(cmdproc, "RML","");
+
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+
+	msn_cmdproc_send_trans(cmdproc, trans);
+#endif
 }
 
 static void
@@ -567,24 +973,22 @@
 
 	group_name = purple_url_decode(cmd->params[2]);
 
-	msn_group_new(session->userlist, group_id, group_name);
+	msn_group_new(session->userlist, cmd->params[3], group_name);
 
-	/* There is a user that must me moved to this group */
+	/* There is a user that must be moved to this group */
 	if (cmd->trans->data)
 	{
 		/* msn_userlist_move_buddy(); */
 		MsnUserList *userlist = cmdproc->session->userlist;
-		MsnMoveBuddy *data = cmd->trans->data;
+		MsnCallbackState *data = cmd->trans->data;
 
 		if (data->old_group_name != NULL)
 		{
-			msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->old_group_name);
+			msn_userlist_move_buddy(userlist, data->who, data->old_group_name, group_name);
 			g_free(data->old_group_name);
+		} else {
+			// msn_add_contact_to_group(userlist, data, data->who, group_name);
 		}
-
-		msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, group_name);
-		g_free(data->who);
-
 	}
 }
 
@@ -642,6 +1046,7 @@
 	PurpleConnection *gc;
 	MsnUser *user;
 	MsnObject *msnobj;
+	int wlmclient;
 	const char *state, *passport, *friendly;
 
 	session = cmdproc->session;
@@ -650,7 +1055,9 @@
 
 	state    = cmd->params[1];
 	passport = cmd->params[2];
-	friendly = purple_url_decode(cmd->params[3]);
+	/*if a contact is actually on the WLM part or the yahoo part*/
+	wlmclient = atoi(cmd->params[3]);
+	friendly = purple_url_decode(cmd->params[4]);
 
 	user = msn_userlist_find_user(session->userlist, passport);
 
@@ -658,9 +1065,9 @@
 
 	msn_user_set_friendly_name(user, friendly);
 
-	if (session->protocol_ver >= 9 && cmd->param_count == 6)
+	if (session->protocol_ver >= 9 && cmd->param_count == 8)
 	{
-		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
+		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6]));
 		msn_user_set_object(user, msnobj);
 	}
 
@@ -692,6 +1099,7 @@
 	MsnUser *user;
 	MsnObject *msnobj;
 	int clientid;
+	int wlmclient;
 	const char *state, *passport, *friendly, *old_friendly;
 
 	session = cmdproc->session;
@@ -700,7 +1108,8 @@
 
 	state    = cmd->params[0];
 	passport = cmd->params[1];
-	friendly = purple_url_decode(cmd->params[2]);
+	wlmclient = atoi(cmd->params[2]);
+	friendly = purple_url_decode(cmd->params[3]);
 
 	user = msn_userlist_find_user(session->userlist, passport);
 
@@ -713,10 +1122,9 @@
 
 	if (session->protocol_ver >= 9)
 	{
-		if (cmd->param_count == 5)
+		if (cmd->param_count == 7)
 		{
-			msnobj =
-				msn_object_new_from_string(purple_url_decode(cmd->params[4]));
+			msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
 			msn_user_set_object(user, msnobj);
 		}
 		else
@@ -725,7 +1133,7 @@
 		}
 	}
 
-	clientid = atoi(cmd->params[3]);
+	clientid = atoi(cmd->params[4]);
 	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE);
 
 	msn_user_set_state(user, state);
@@ -797,7 +1205,9 @@
 prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnSession *session = cmdproc->session;
-	const char *type, *value;
+	const char *type, *value, *friendlyname;
+
+	purple_debug_info("MSN Notification", "prp_cmd()\n");
 
 	g_return_if_fail(cmd->param_count >= 3);
 
@@ -821,6 +1231,18 @@
 			msn_user_set_work_phone(session->user, NULL);
 		else if (!strcmp(type, "PHM"))
 			msn_user_set_mobile_phone(session->user, NULL);
+		else {
+			type = cmd->params[1];
+			if (!strcmp(type, "MFN")) {
+				friendlyname = purple_url_decode(cmd->params[2]);
+				
+				msn_update_contact(session->contact, friendlyname);
+
+				purple_connection_set_display_name(
+					purple_account_get_connection(session->account),
+					friendlyname);
+			}
+		}
 	}
 }
 
@@ -828,11 +1250,10 @@
 reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnSession *session;
-	int group_id;
-	const char *group_name;
+	const char *group_id, *group_name;
 
 	session = cmdproc->session;
-	group_id = atoi(cmd->params[2]);
+	group_id = cmd->params[2];
 	group_name = purple_url_decode(cmd->params[3]);
 
 	msn_userlist_rename_group_id(session->userlist, group_id, group_name);
@@ -841,27 +1262,26 @@
 static void
 reg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
 {
-	int group_id;
+	const char * group_id;
 	char **params;
 
 	params = g_strsplit(trans->params, " ", 0);
 
-	group_id = atoi(params[0]);
+	group_id = params[0];
 
 	group_error_helper(cmdproc->session, _("Unable to rename group"), group_id, error);
 
 	g_strfreev(params);
 }
 
+#if 0
 static void
 rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnSession *session;
 	MsnUser *user;
-	const char *list;
-	const char *passport;
+	const char *group_id, *list, *passport;
 	MsnListId list_id;
-	int group_id;
 
 	session = cmdproc->session;
 	list = cmd->params[1];
@@ -873,22 +1293,23 @@
 	list_id = msn_get_list_id(list);
 
 	if (cmd->param_count == 5)
-		group_id = atoi(cmd->params[4]);
+		group_id = cmd->params[4];
 	else
-		group_id = -1;
+		group_id = NULL;
 
 	msn_got_rem_user(session, user, list_id, group_id);
 	msn_user_update(user);
 }
+#endif
 
 static void
 rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnSession *session;
-	int group_id;
+	const char *group_id;
 
 	session = cmdproc->session;
-	group_id = atoi(cmd->params[2]);
+	group_id = cmd->params[2];
 
 	msn_userlist_remove_group_id(session->userlist, group_id);
 }
@@ -896,18 +1317,19 @@
 static void
 rmg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
 {
-	int group_id;
+	const char *group_id;
 	char **params;
 
 	params = g_strsplit(trans->params, " ", 0);
 
-	group_id = atoi(params[0]);
+	group_id = params[0];
 
 	group_error_helper(cmdproc->session, _("Unable to delete group"), group_id, error);
 
 	g_strfreev(params);
 }
 
+#if 0
 static void
 syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -938,6 +1360,7 @@
 	session->sync = sync;
 	cmdproc->cbs_table = sync->cbs_table;
 }
+#endif
 
 /**************************************************************************
  * Misc commands
@@ -1146,6 +1569,103 @@
 	g_free(host);
 }
 
+static void
+gcf_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+			 size_t len)
+{
+	xmlnode * root;
+	gchar * buf;
+
+	g_return_if_fail(cmd->payload != NULL);
+
+	if ( (root = xmlnode_from_str(cmd->payload, cmd->payload_len)) == NULL)
+	{
+		purple_debug_error("MSN","Unable to parse GCF payload into a XML tree");
+		return;
+	}
+	
+	buf = xmlnode_to_formatted_str(root, NULL);
+
+	/* get the payload content */
+	purple_debug_info("MSNP14","GCF command payload:\n%s\n",buf);
+	
+	g_free(buf);
+	xmlnode_free(root);
+}
+
+static void
+gcf_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MSNP14","Processing GCF command\n");
+	cmdproc->last_cmd->payload_cb  = gcf_cmd_post;
+	return;
+}
+
+static void
+sbs_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MSNP14","Processing SBS... \n");
+	if(cmd->payload_len == 0){
+		return;
+	}
+	/*get the payload content*/
+}
+
+/*
+ * Get the UBX's PSM info
+ * Post it to the User status
+ * Thanks for Chris <ukdrizzle@yahoo.co.uk>'s code
+ */
+static void
+ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+			 size_t len)
+{
+	MsnSession *session;
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	MsnUser *user;
+	const char *passport;
+	char *psm_str, *currentmedia_str, *str;
+
+	/*get the payload content*/
+//	purple_debug_info("MSNP14","UBX {%s} payload{%s}\n",cmd->params[0], cmd->payload);
+
+	session = cmdproc->session;
+	account = session->account;
+	gc = purple_account_get_connection(account);
+
+	passport = cmd->params[0];
+	user = msn_userlist_find_user(session->userlist, passport);
+	
+	psm_str = msn_get_psm(cmd->payload,len);
+	currentmedia_str = msn_parse_currentmedia(
+	                                 str = msn_get_currentmedia(cmd->payload, len));
+	g_free(str);
+
+	msn_user_set_statusline(user, psm_str);
+	msn_user_set_currentmedia(user, currentmedia_str);
+	msn_user_update(user);
+
+	g_free(psm_str);
+	g_free(currentmedia_str);
+}
+
+static void
+ubx_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_misc("MSNP14","UBX received.\n");
+	if(cmd->payload_len == 0){
+		return;
+	}
+	cmdproc->last_cmd->payload_cb  = ubx_cmd_post;
+}
+
+static void
+uux_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_misc("MSNP14","UUX received.\n");
+}
+
 /**************************************************************************
  * Message Types
  **************************************************************************/
@@ -1155,6 +1675,7 @@
 {
 	MsnSession *session;
 	const char *value;
+	const char *clLastChange;
 
 	session = cmdproc->session;
 
@@ -1187,10 +1708,27 @@
 	}
 
 	if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL)
+	{
 		session->passport_info.client_port = ntohs(atoi(value));
+	}
 
 	if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL)
 		session->passport_info.sl = atol(value);
+
+	/*starting retrieve the contact list*/
+	clLastChange = purple_account_get_string(session->account, "CLLastChange", NULL);
+	session->contact = msn_contact_new(session);
+#ifdef MSN_PARTIAL_LISTS
+	/* msn_userlist_load defeats all attempts at trying to detect blist sync issues */
+	msn_userlist_load(session);
+	msn_get_contact_list(session->contact, MSN_PS_INITIAL, clLastChange);
+#else
+	/* always get the full list? */
+	msn_get_contact_list(session->contact, MSN_PS_INITIAL, NULL);
+#endif
+#if 0
+	msn_contact_connect(session->contact);
+#endif
 }
 
 static void
@@ -1246,6 +1784,86 @@
 	g_hash_table_destroy(table);
 }
 
+/*offline Message notification process*/
+static void
+initial_mdata_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+	MsnSession *session;
+	PurpleConnection *gc;
+	GHashTable *table;
+	const char *mdata, *unread;
+
+	session = cmdproc->session;
+	gc = session->account->gc;
+
+	if (strcmp(msg->remote_user, "Hotmail"))
+		/* This isn't an official message. */
+		return;
+
+	/*new a oim session*/
+	session->oim = msn_oim_new(session);
+//	msn_oim_connect(session->oim);
+
+	table = msn_message_get_hashtable_from_body(msg);
+
+	mdata = g_hash_table_lookup(table, "Mail-Data");
+
+	if (mdata != NULL)
+		msn_parse_oim_msg(session->oim, mdata);
+
+	if (g_hash_table_lookup(table, "Inbox-URL") == NULL)
+	{
+		g_hash_table_destroy(table);
+		return;
+	}
+
+	if (session->passport_info.file == NULL)
+	{
+		MsnTransaction *trans;
+		trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX");
+		msn_transaction_queue_cmd(trans, msg->cmd);
+
+		msn_cmdproc_send_trans(cmdproc, trans);
+
+		g_hash_table_destroy(table);
+		return;
+	}
+
+	if (!purple_account_get_check_mail(session->account))
+	{
+		g_hash_table_destroy(table);
+		return;
+	}
+
+	unread = g_hash_table_lookup(table, "Inbox-Unread");
+
+	if (unread != NULL)
+	{
+		int count = atoi(unread);
+
+		if (count > 0)
+		{
+			const char *passport;
+			const char *url;
+
+			passport = msn_user_get_passport(session->user);
+			url = session->passport_info.file;
+
+			purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL,
+							   &passport, &url, NULL, NULL);
+		}
+	}
+
+	g_hash_table_destroy(table);
+}
+
+/*offline Message Notification*/
+static void
+delete_oim_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+	purple_debug_misc("MSN Notification","Delete OIM message.\n");
+}
+
 static void
 email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
@@ -1347,48 +1965,62 @@
 }
 
 void
-msn_notification_add_buddy(MsnNotification *notification, const char *list,
-						   const char *who, const char *store_name,
-						   int group_id)
+msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list_id,
+						   	  const char *who)
 {
 	MsnCmdProc *cmdproc;
+	MsnListOp list_op = 1 << list_id;
+	xmlnode *adl_node;
+	char *payload;
+	int payload_len;
+
 	cmdproc = notification->servconn->cmdproc;
 
-	if (group_id < 0 && !strcmp(list, "FL"))
-		group_id = 0;
+	adl_node = xmlnode_new("ml");
+	adl_node->child = NULL;
+
+	msn_add_contact_xml(notification->session, adl_node, who, list_op, 
+						MSN_USER_TYPE_PASSPORT);
 
-	if (group_id >= 0)
-	{
-		msn_cmdproc_send(cmdproc, "ADD", "%s %s %s %d",
-						 list, who, store_name, group_id);
-	}
-	else
-	{
-		msn_cmdproc_send(cmdproc, "ADD", "%s %s %s", list, who, store_name);
-	}
+	payload = xmlnode_to_str(adl_node,&payload_len);
+	xmlnode_free(adl_node);
+	
+	msn_notification_post_adl(notification->servconn->cmdproc,
+						payload,payload_len);
+	g_free(payload);
 }
 
 void
-msn_notification_rem_buddy(MsnNotification *notification, const char *list,
-						   const char *who, int group_id)
+msn_notification_rem_buddy_from_list(MsnNotification *notification, MsnListId list_id,
+						   const char *who)
 {
 	MsnCmdProc *cmdproc;
+	MsnTransaction *trans;
+	MsnListOp list_op = 1 << list_id;
+	xmlnode *rml_node;
+	char *payload;
+	int payload_len;
+
 	cmdproc = notification->servconn->cmdproc;
 
-	if (group_id >= 0)
-	{
-		msn_cmdproc_send(cmdproc, "REM", "%s %s %d", list, who, group_id);
-	}
-	else
-	{
-		msn_cmdproc_send(cmdproc, "REM", "%s %s", list, who);
-	}
+	rml_node = xmlnode_new("ml");
+	rml_node->child = NULL;
+
+	msn_add_contact_xml(notification->session, rml_node, who, list_op, MSN_USER_TYPE_PASSPORT);
+
+	payload = xmlnode_to_str(rml_node, &payload_len);
+	xmlnode_free(rml_node);
+
+	purple_debug_info("MSN Notification","Send RML with payload:\n%s\n", payload);
+	trans = msn_transaction_new(cmdproc, "RML","%d", strlen(payload));
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+	msn_cmdproc_send_trans(cmdproc, trans);
+	g_free(payload);
 }
 
 /**************************************************************************
  * Init
  **************************************************************************/
-
 void
 msn_notification_init(void)
 {
@@ -1399,18 +2031,18 @@
 	/* Synchronous */
 	msn_table_add_cmd(cbs_table, "CHG", "CHG", NULL);
 	msn_table_add_cmd(cbs_table, "CHG", "ILN", iln_cmd);
-	msn_table_add_cmd(cbs_table, "ADD", "ADD", add_cmd);
-	msn_table_add_cmd(cbs_table, "ADD", "ILN", iln_cmd);
-	msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd);
+	msn_table_add_cmd(cbs_table, "ADL", "ILN", iln_cmd);
+//	msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd);	/* Removed as of MSNP13 */
 	msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
 	msn_table_add_cmd(cbs_table, "USR", "XFR", xfr_cmd);
-	msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd);
+	msn_table_add_cmd(cbs_table, "USR", "GCF", gcf_cmd);
+//	msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd);	/* Removed as of MSNP13 */
 	msn_table_add_cmd(cbs_table, "CVR", "CVR", cvr_cmd);
 	msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd);
 	msn_table_add_cmd(cbs_table, "REA", "REA", rea_cmd);
 	msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd);
-	/* msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); */
-	msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL);
+	msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd);
+//	msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL);
 	msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd);
 	msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd);
 	msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd);
@@ -1419,11 +2051,15 @@
 	/* Asynchronous */
 	msn_table_add_cmd(cbs_table, NULL, "IPG", ipg_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "UBM", ubm_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "GCF", gcf_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "SBS", sbs_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "NOT", not_cmd);
 
 	msn_table_add_cmd(cbs_table, NULL, "CHL", chl_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "REM", rem_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "ADD", add_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "RML", rml_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "ADL", adl_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "FQY", fqy_cmd);
 
 	msn_table_add_cmd(cbs_table, NULL, "QRY", NULL);
 	msn_table_add_cmd(cbs_table, NULL, "QNG", qng_cmd);
@@ -1433,11 +2069,15 @@
 	msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "RNG", rng_cmd);
 
+	msn_table_add_cmd(cbs_table, NULL, "UBX", ubx_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "UUX", uux_cmd);
+
 	msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd);
 
 	msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd);
 
 	msn_table_add_error(cbs_table, "ADD", add_error);
+	msn_table_add_error(cbs_table, "ADL", adl_error);
 	msn_table_add_error(cbs_table, "REG", reg_error);
 	msn_table_add_error(cbs_table, "RMG", rmg_error);
 	/* msn_table_add_error(cbs_table, "REA", rea_error); */
@@ -1446,12 +2086,24 @@
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsprofile",
 						   profile_msg);
+	/*initial OIM notification*/
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsinitialmdatanotification",
+							initial_mdata_msg);	
+	/*OIM notification when user online*/
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsoimnotification",
+							initial_mdata_msg);	
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsinitialemailnotification",
 						   initial_email_msg);
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsemailnotification",
 						   email_msg);
+	/*delete an offline Message notification*/
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsactivemailnotification",
+						   delete_oim_msg);
 	msn_table_add_msg_type(cbs_table,
 						   "application/x-msmsgssystemmessage",
 						   system_msg);
@@ -1462,3 +2114,4 @@
 {
 	msn_table_destroy(cbs_table);
 }
+
--- a/libpurple/protocols/msn/notification.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/notification.h	Sun Sep 16 18:06:22 2007 +0000
@@ -24,6 +24,14 @@
 #ifndef _MSN_NOTIFICATION_H_
 #define _MSN_NOTIFICATION_H_
 
+/*MSN protocol challenge info*/
+/*MSNP13 challenge*/
+#define MSNP13_WLM_PRODUCT_KEY	"O4BG@C7BWLYQX?5G"
+#define MSNP13_WLM_PRODUCT_ID	"PROD01065C%ZFN6F"
+
+#define MSNP10_PRODUCT_KEY		"VT6PX?UQTM4WM%YR"
+#define MSNP10_PRODUCT_ID		"PROD0038W!61ZTF9" 
+
 typedef struct _MsnNotification MsnNotification;
 
 #include "session.h"
@@ -40,21 +48,24 @@
 };
 
 #include "state.h"
+void uum_send_msg(MsnSession *session,MsnMessage *msg);
 
 void msn_notification_end(void);
 void msn_notification_init(void);
 
-void msn_notification_add_buddy(MsnNotification *notification,
-								const char *list, const char *who,
-								const char *store_name, int group_id);
-void msn_notification_rem_buddy(MsnNotification *notification,
-								const char *list, const char *who,
-								int group_id);
+void msn_notification_add_buddy_to_list(MsnNotification *notification,
+					MsnListId list_id, const char *who);
+void msn_notification_rem_buddy_from_list(MsnNotification *notification,
+					  MsnListId list_id, const char *who);
+
+void msn_notification_send_fqy(MsnSession *session, const char *passport);
+
 MsnNotification *msn_notification_new(MsnSession *session);
 void msn_notification_destroy(MsnNotification *notification);
 gboolean msn_notification_connect(MsnNotification *notification,
-							  const char *host, int port);
+				  const char *host, int port);
 void msn_notification_disconnect(MsnNotification *notification);
+void msn_notification_dump_contact(MsnSession *session);
 
 /**
  * Closes a notification.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/oim.c	Sun Sep 16 18:06:22 2007 +0000
@@ -0,0 +1,717 @@
+/**
+ * @file oim.c 
+ * 	get and send MSN offline Instant Message via SOAP request
+ *	Author
+ * 		MaYuan<mayuan2006@gmail.com>
+ * 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 "msn.h"
+#include "soap.h"
+#include "oim.h"
+#include "msnutils.h"
+
+/*Local Function Prototype*/
+static void msn_oim_post_single_get_msg(MsnOim *oim,const 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);
+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 char *msn_oim_msg_to_str(MsnOim *oim, const char *body);
+static void msn_oim_send_process(MsnOim *oim, const char *body, int len);
+
+/*new a OIM object*/
+MsnOim *
+msn_oim_new(MsnSession *session)
+{
+	MsnOim *oim;
+
+	oim = g_new0(MsnOim, 1);
+	oim->session = session;
+	oim->retrieveconn = msn_soap_new(session,oim,1);
+	
+	oim->oim_list	= NULL;
+	oim->sendconn = msn_soap_new(session,oim,1);
+	oim->run_id = rand_guid();
+	oim->challenge = NULL;
+	oim->send_queue = g_queue_new();
+	oim->send_seq = 1;
+	return oim;
+}
+
+/*destroy the oim object*/
+void
+msn_oim_destroy(MsnOim *oim)
+{
+	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);
+	
+	while((request = g_queue_pop_head(oim->send_queue)) != NULL){
+		msn_oim_free_send_req(request);
+	}
+	g_queue_free(oim->send_queue);
+	
+	g_free(oim);
+}
+
+static MsnOimSendReq *
+msn_oim_new_send_req(const char *from_member, const char*friendname,
+					 const char* to_member, gint send_seq,
+					 const char *msg)
+{
+	MsnOimSendReq *request;
+	
+	request = g_new0(MsnOimSendReq, 1);
+	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;
+}
+
+static void
+msn_oim_free_send_req(MsnOimSendReq *req)
+{
+	g_return_if_fail(req != NULL);
+
+	g_free(req->from_member);
+	g_free(req->friendname);
+	g_free(req->to_member);
+	g_free(req->oim_msg);
+	
+	g_free(req);
+}
+
+/****************************************
+ * OIM send SOAP request
+ * **************************************/
+/*encode the message to OIM Message Format*/
+static char *
+msn_oim_msg_to_str(MsnOim *oim, const char *body)
+{
+	char *oim_body,*oim_base64;
+	
+	purple_debug_info("MSNP14","encode OIM Message...\n");	
+	oim_base64 = purple_base64_encode((const guchar *)body, strlen(body));
+	purple_debug_info("MSNP14","encoded base64 body:{%s}\n",oim_base64);	
+	oim_body = g_strdup_printf(MSN_OIM_MSG_TEMPLATE,
+				oim->run_id,oim->send_seq,oim_base64);
+
+	return oim_body;
+}
+
+/*oim SOAP server login error*/
+static void
+msn_oim_send_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+{
+	MsnSoapConn *soapconn = data;
+	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 void
+msn_oim_send_connect_cb(gpointer data, PurpleSslConnection *gsc,
+				 PurpleInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+	MsnSession * session;
+	MsnOim *oim;
+
+	oim = soapconn->parent;
+	g_return_if_fail(oim != NULL);
+
+	session = oim->session;
+	g_return_if_fail(session != NULL);
+}
+
+/*
+ * 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)
+{
+	xmlnode *responseNode, *bodyNode;
+	xmlnode	*faultNode, *faultCodeNode, *faultstringNode;
+	xmlnode *detailNode, *challengeNode;
+	char *faultCodeStr = NULL, *faultstring = NULL;
+
+	responseNode = xmlnode_from_str(body,len);
+	g_return_if_fail(responseNode != NULL);
+	bodyNode = xmlnode_get_child(responseNode,"Body");
+	faultNode = xmlnode_get_child(bodyNode,"Fault");
+	if(faultNode == NULL){
+		/*Send OK! return*/
+		MsnOimSendReq *request;
+		
+		purple_debug_info("MSNP14","send OIM OK!");
+		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,msn_oim_send_connect_init);
+		return;
+	}
+	/*get the challenge,and repost it*/
+	faultCodeNode = xmlnode_get_child(faultNode,"faultcode");
+	if(faultCodeNode == NULL){
+		purple_debug_info("MSNP14","faultcode Node is NULL\n");
+		goto oim_send_process_fail;
+	}
+	faultCodeStr = xmlnode_get_data(faultCodeNode);
+	purple_debug_info("MSNP14","fault code:{%s}\n",faultCodeStr);
+#if 0
+	if(!strcmp(faultCodeStr,"q0:AuthenticationFailed")){
+		/*other Fault Reason?*/
+		goto oim_send_process_fail;
+	}
+#endif
+
+	faultstringNode = xmlnode_get_child(faultNode,"faultstring");
+	faultstring = xmlnode_get_data(faultstringNode);
+	purple_debug_info("MSNP14","fault string :{%s}\n",faultstring);
+
+	/* lock key fault reason,
+	 * compute the challenge and resend it
+	 */
+	detailNode = xmlnode_get_child(faultNode, "detail");
+	if(detailNode == NULL){
+		goto oim_send_process_fail;
+	}
+	challengeNode = xmlnode_get_child(detailNode,"LockKeyChallenge");
+	if (challengeNode == NULL) {
+		goto oim_send_process_fail;
+	}
+
+	g_free(oim->challenge);
+	oim->challenge = xmlnode_get_data(challengeNode);
+	purple_debug_info("MSNP14","lockkey:{%s}\n",oim->challenge);
+
+	/*repost the send*/
+	purple_debug_info("MSNP14","prepare to repost the send...\n");
+	msn_oim_send_msg(oim);
+
+oim_send_process_fail:
+	g_free(faultstring);
+	g_free(faultCodeStr);
+	xmlnode_free(responseNode);
+	return ;
+}
+
+static void
+msn_oim_send_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnSession *session = soapconn->session;
+	MsnOim * oim;
+
+	if (soapconn->body == NULL)
+		return;
+
+	g_return_if_fail(session != NULL);
+	oim = soapconn->session->oim;
+	g_return_if_fail(oim != NULL);
+
+	purple_debug_info("MSNP14","read buffer:{%s}\n",soapconn->body);
+	msn_oim_send_process(oim,soapconn->body,soapconn->body_len);
+}
+
+static void
+msn_oim_send_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	soapconn->read_cb = msn_oim_send_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+void
+msn_oim_prep_send_msg_info(MsnOim *oim, const char *membername,
+						   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);
+}
+
+/*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;
+	char *msg_body;
+	char buf[33];
+
+	g_return_if_fail(oim != NULL);
+	oim_request = g_queue_pop_head(oim->send_queue);
+	g_return_if_fail(oim_request != NULL);
+
+	purple_debug_info("MSNP14","send single OIM Message\n");
+	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("MSNP14","no lock key challenge,wait for SOAP Fault and Resend\n");
+		buf[0]='\0';
+	}
+	purple_debug_info("MSNP14","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,
+					oim_request->from_member,
+					oim_request->friendname,
+					oim_request->to_member,
+					mspauth,
+					MSNP13_WLM_PRODUCT_ID,
+					buf,
+					oim_request->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);
+	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,msn_oim_send_connect_init);
+}
+
+/****************************************
+ * OIM delete SOAP request
+ * **************************************/
+static void
+msn_oim_delete_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+
+	if (soapconn->body == NULL)
+		return;
+	purple_debug_info("MSNP14","OIM delete read buffer:{%s}\n",soapconn->body);
+
+	msn_soap_free_read_buf(soapconn);
+	/*get next single Offline Message*/
+	msn_soap_post(soapconn,NULL,msn_oim_retrieve_connect_init);
+}
+
+static void
+msn_oim_delete_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+
+	soapconn->read_cb = msn_oim_delete_read_cb;
+}
+
+/*Post to get the Offline Instant Message*/
+static void
+msn_oim_post_delete_msg(MsnOim *oim,const char *msgid)
+{
+	MsnSoapReq *soap_request;
+	const char *soap_body,*t,*p;
+
+	g_return_if_fail(oim != NULL);
+	g_return_if_fail(msgid != NULL);
+
+	purple_debug_info("MSNP14","Delete single OIM Message {%s}\n",msgid);
+	t = oim->session->passport_info.t;
+	p = oim->session->passport_info.p;
+
+	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_soap_post(oim->retrieveconn,soap_request,msn_oim_retrieve_connect_init);
+}
+
+/****************************************
+ * OIM get SOAP request
+ * **************************************/
+/*oim SOAP server login error*/
+static void
+msn_oim_get_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+{
+	MsnSoapConn *soapconn = data;
+	MsnSession *session;
+
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+	msn_soap_clean_unhandled_request(soapconn);
+
+//	msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to OIM server"));
+}
+
+/*msn oim SOAP server connect process*/
+static void
+msn_oim_get_connect_cb(gpointer data, PurpleSslConnection *gsc,
+				 PurpleInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+	MsnSession * session;
+	MsnOim *oim;
+
+	oim = soapconn->parent;
+	g_return_if_fail(oim != NULL);
+
+	session = oim->session;
+	g_return_if_fail(session != NULL);
+
+	purple_debug_info("MSNP14","oim get SOAP Server connected!\n");
+}
+
+/* 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
+msn_oim_parse_timestamp(const char *timestamp)
+{
+	char month_str[4], tz_str[6];
+	char *tz_ptr = tz_str;
+	static const char *months[] = {
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
+	};
+	struct tm t;
+	memset(&t, 0, sizeof(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) {
+		gboolean offset_positive = TRUE;
+		int tzhrs;
+		int tzmins;
+		
+		for (t.tm_mon = 0;
+			 months[t.tm_mon] != NULL &&
+				 strcmp(months[t.tm_mon], month_str) != 0; t.tm_mon++);
+		if (months[t.tm_mon] != NULL) {
+			if (*tz_str == '-') {
+				offset_positive = FALSE;
+				tz_ptr++;
+			} else if (*tz_str == '+') {
+				tz_ptr++;
+			}
+
+			if (sscanf(tz_ptr, "%02d%02d", &tzhrs, &tzmins) == 2) {
+				time_t tzoff = tzhrs * 60 * 60 + tzmins * 60;
+#ifdef _WIN32
+				long sys_tzoff;
+#endif
+
+				if (!offset_positive)
+					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;
+#else
+#	ifdef HAVE_TIMEZONE
+				tzset();    /* making sure */
+				tzoff -= timezone;
+#	endif
+#endif
+#endif /* _WIN32 */
+
+				return mktime(&t) + tzoff;
+			}
+		}
+	}
+
+	purple_debug_info("MSNP14:OIM", "Can't parse timestamp %s\n", timestamp);
+	return time(NULL);
+}
+
+/*Post the Offline Instant Message to User Conversation*/
+static void
+msn_oim_report_to_user(MsnOim *oim, const char *msg_str)
+{
+	MsnMessage *message;
+	char *date,*from,*decode_msg;
+	gsize body_len;
+	char **tokens;
+	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("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");
+	if(strstr(from," ")){
+		has_nick = 1;
+	}
+	if(has_nick){
+		tokens = g_strsplit(from , " " , 2);
+		passport_str = g_strdup(tokens[1]);
+		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("MSNP14","oim Date:{%s},passport{%s}\n",
+					date,passport_str);
+	}
+	start = strstr(passport_str,"<");
+	start += 1;
+	end = strstr(passport_str,">");
+	passport = g_strndup(start,end - start);
+	g_free(passport_str);
+	purple_debug_info("MSNP14","oim Date:{%s},passport{%s}\n",date,passport);
+
+	stamp = msn_oim_parse_timestamp(date);
+
+	serv_got_im(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);
+	}
+
+	g_free(passport);
+}
+
+/* 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 void
+msn_oim_get_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnOim * oim = soapconn->session->oim;
+
+	if (soapconn->body == NULL)
+		return;
+
+	purple_debug_info("MSNP14","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);
+
+	/*get next single Offline Message*/
+	msn_soap_post(soapconn,NULL,msn_oim_retrieve_connect_init);
+}
+
+static void
+msn_oim_get_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+
+	soapconn->read_cb = msn_oim_get_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+/* parse the oim XML data 
+ * and post it to the soap server to get the Offline Message
+ * */
+void
+msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg)
+{
+	xmlnode *node, *mNode,*ENode,*INode,*rtNode,*nNode;
+	char *passport,*msgid,*nickname, *unread, *rTime = NULL;
+	MsnSession *session = oim->session;
+
+	purple_debug_info("MSNP14:OIM", "%s", xmlmsg);
+
+	node = xmlnode_from_str(xmlmsg, strlen(xmlmsg));
+	if (strcmp(node->name, "MD") != 0) {
+		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);
+
+		if (count > 0)
+		{
+			const char *passport;
+			const char *url;
+
+			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);
+		}
+	}
+
+	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);
+			rtNode = NULL;
+		}
+/*		purple_debug_info("MSNP14","E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime); */
+
+		oim->oim_list = g_list_append(oim->oim_list,strdup(msgid));
+		msn_oim_post_single_get_msg(oim,msgid);
+		g_free(passport);
+		g_free(msgid);
+		g_free(rTime);
+		rTime = NULL;
+		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)
+{
+	MsnSoapReq *soap_request;
+	const char *soap_body,*t,*p;
+
+	purple_debug_info("MSNP14","Get single OIM Message\n");
+	t = oim->session->passport_info.t;
+	p = oim->session->passport_info.p;
+
+	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_soap_post(oim->retrieveconn,soap_request,msn_oim_retrieve_connect_init);
+}
+
+/*msn oim retrieve server connect init */
+static void
+msn_oim_retrieve_connect_init(MsnSoapConn *soapconn)
+{
+	purple_debug_info("MSNP14","msn_oim_connect...\n");
+	msn_soap_init(soapconn,MSN_OIM_RETRIEVE_HOST,1,
+					msn_oim_get_connect_cb,
+					msn_oim_get_error_cb);
+}
+
+/*Msn OIM Send Server Connect Init Function*/
+static void
+msn_oim_send_connect_init(MsnSoapConn *sendconn)
+{
+	purple_debug_info("MSNP14","msn oim send connect init...\n");
+	msn_soap_init(sendconn,MSN_OIM_SEND_HOST,1,
+					msn_oim_send_connect_cb,
+					msn_oim_send_error_cb);
+}
+
+/*endof oim.c*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/oim.h	Sun Sep 16 18:06:22 2007 +0000
@@ -0,0 +1,148 @@
+/**
+ * @file oim.h			Header file for oim.c
+ *	Author
+ * 		MaYuan<mayuan2006@gmail.com>
+ * 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_OIM_H_
+#define _MSN_OIM_H_
+
+/*OIM Retrieve SOAP Template*/
+#define MSN_OIM_RETRIEVE_HOST	"rsi.hotmail.com"
+#define MSN_OIM_RETRIEVE_URL	"/rsi/rsi.asmx"
+#define MSN_OIM_GET_SOAP_ACTION	"http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage"
+
+#define MSN_OIM_GET_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+	"<soap:Header>"\
+		"<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
+			"<t>%s</t>"\
+			"<p>%s</p>"\
+		"</PassportCookie>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<GetMessage xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
+			"<messageId>%s</messageId>"\
+			"<alsoMarkAsRead>false</alsoMarkAsRead>"\
+		"</GetMessage>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+/*OIM Delete SOAP Template*/
+#define MSN_OIM_DEL_SOAP_ACTION	"http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages"
+
+#define MSN_OIM_DEL_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+	"<soap:Header>"\
+		"<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
+			"<t>%s</t>"\
+			" <p>%s</p>"\
+		"</PassportCookie>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<DeleteMessages xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
+			"<messageIds>"\
+				"<messageId>%s</messageId>"\
+			"</messageIds>"\
+		"</DeleteMessages>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+/*OIM Send SOAP Template*/
+#define MSN_OIM_MSG_TEMPLATE "MIME-Version: 1.0\n"\
+	"Content-Type: text/plain; charset=UTF-8\n"\
+	"Content-Transfer-Encoding: base64\n"\
+	"X-OIM-Message-Type: OfflineMessage\n"\
+	"X-OIM-Run-Id: {%s}\n"\
+	"X-OIM-Sequence-Num: %d\n\n"\
+	"%s"
+
+#define MSN_OIM_SEND_HOST	"ows.messenger.msn.com"
+#define MSN_OIM_SEND_URL	"/OimWS/oim.asmx"
+#define MSN_OIM_SEND_SOAP_ACTION	"http://messenger.msn.com/ws/2004/09/oim/Store"
+#define MSN_OIM_SEND_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+	"<soap:Header>"\
+		"<From memberName=\"%s\" friendlyName=\"%s\" xml:lang=\"en-US\" proxy=\"MSNMSGR\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\" msnpVer=\"MSNP14\" buildVer=\"8.0.0792\"/>"\
+		"<To memberName=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>"\
+		"<Ticket passport=\"%s\" appid=\"%s\" lockkey=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>"\
+		"<Sequence xmlns=\"http://schemas.xmlsoap.org/ws/2003/03/rm\">"\
+			"<Identifier xmlns=\"http://schemas.xmlsoap.org/ws/2002/07/utility\">http://messenger.msn.com</Identifier>"\
+			"<MessageNumber>%d</MessageNumber>"\
+		"</Sequence>"\
+	"</soap:Header>"\
+	"<soap:Body>"\
+		"<MessageType xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">text</MessageType>"\
+		"<Content xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">%s</Content>"\
+	"</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
+{
+	MsnSession *session;
+
+	MsnSoapConn *retrieveconn;
+	GList * oim_list;
+
+	MsnSoapConn *sendconn;
+	char *challenge;
+	char *run_id;
+	gint send_seq;
+	GQueue *send_queue;
+};
+
+/****************************************************
+ * function prototype
+ * **************************************************/
+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);
+
+/*Send OIM Message*/
+void msn_oim_prep_send_msg_info(MsnOim *oim, const char *membername,
+								const char *friendname, const char *tomember,
+								const char * msg);
+
+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/servconn.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/servconn.c	Sun Sep 16 18:06:22 2007 +0000
@@ -166,7 +166,7 @@
  **************************************************************************/
 
 static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
+connect_cb(gpointer data, gint source, const char *error_message)
 {
 	MsnServConn *servconn;
 
@@ -243,7 +243,9 @@
 		return TRUE;
 	}
 	else
+	{
 		return FALSE;
+	}
 }
 
 void
@@ -388,14 +390,21 @@
 
 	len = read(servconn->fd, buf, sizeof(buf) - 1);
 
-	if (len < 0 && errno == EAGAIN)
-		return;
-	else if (len <= 0)
-	{
-		purple_debug_error("msn", "servconn read error, len: %d error: %s\n", len, strerror(errno));
-		msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ);
+	if (len <= 0) {
+		switch (errno) {
+
+			case 0:	
 
-		return;
+			case EBADF:
+			case EAGAIN: return;
+	
+			default: purple_debug_error("msn", "servconn read error,"
+						"len: %d, errno: %d, error: %s\n",
+						len, errno, strerror(errno));
+				 msn_servconn_got_error(servconn, 
+						 MSN_SERVCONN_ERROR_READ);
+				 return;
+		}
 	}
 
 	buf[len] = '\0';
@@ -444,6 +453,7 @@
 		else
 		{
 			msn_cmdproc_process_cmd_text(servconn->cmdproc, cur);
+			servconn->payload_len = servconn->cmdproc->last_cmd->payload_len;
 		}
 	} while (servconn->connected && !servconn->wasted && servconn->rx_len > 0);
 
--- a/libpurple/protocols/msn/session.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/session.c	Sun Sep 16 18:06:22 2007 +0000
@@ -43,7 +43,9 @@
 	session->user = msn_user_new(session->userlist,
 								 purple_account_get_username(account), NULL);
 
-	session->protocol_ver = 9;
+	/*if you want to chat with Yahoo Messenger*/
+	//session->protocol_ver = WLM_YAHOO_PROT_VER;
+	session->protocol_ver = WLM_PROT_VER;
 	session->conv_seq = 1;
 
 	return session;
@@ -70,6 +72,8 @@
 
 	msn_userlist_destroy(session->userlist);
 
+	g_free(session->passport_info.t);
+	g_free(session->passport_info.p);
 	g_free(session->passport_info.kv);
 	g_free(session->passport_info.sid);
 	g_free(session->passport_info.mspauth);
@@ -87,6 +91,11 @@
 	if (session->nexus != NULL)
 		msn_nexus_destroy(session->nexus);
 
+	if (session->contact != NULL)
+		msn_contact_destroy(session->contact);
+	if (session->oim != NULL)
+		msn_oim_destroy(session->oim);
+
 	if (session->user != NULL)
 		msn_user_destroy(session->user);
 
@@ -154,6 +163,37 @@
 	return NULL;
 }
 
+static PurpleConversation *
+msn_session_get_conv(MsnSession *session,const char *passport)
+{
+	PurpleAccount *account;
+	PurpleConversation * conv;
+
+	g_return_val_if_fail(session != NULL, NULL);
+	account = session->account;
+
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+									passport, account);
+	if(conv == NULL){
+		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, passport);
+	}
+	return conv;
+}
+
+/* put Message to User Conversation
+ *
+ * 	passport - the one want to talk to you
+ */
+void
+msn_session_report_user(MsnSession *session,const char *passport,char *msg,PurpleMessageFlags flags)
+{
+	PurpleConversation * conv;
+
+	if ((conv = msn_session_get_conv(session,passport)) != NULL){
+		purple_conversation_write(conv, NULL, msg, flags, time(NULL));
+	}
+}
+
 MsnSwitchBoard *
 msn_session_find_swboard_with_conv(MsnSession *session, PurpleConversation *conv)
 {
@@ -229,13 +269,14 @@
 
 	/* The core used to use msn_add_buddy to add all buddies before
 	 * being logged in. This no longer happens, so we manually iterate
-	 * over the whole buddy list to identify sync issues. */
-
-	for (gnode = purple_blist_get_root(); gnode; gnode = gnode->next) {
+	 * over the whole buddy list to identify sync issues. 
+	 */
+	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
 		PurpleGroup *group = (PurpleGroup *)gnode;
-		const char *group_name = group->name;
+		const char *group_name;
 		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
+		group_name = group->name;
 		for(cnode = gnode->child; cnode; cnode = cnode->next) {
 			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
@@ -252,21 +293,17 @@
 
 					if ((remote_user != NULL) && (remote_user->list_op & MSN_LIST_FL_OP))
 					{
-						int group_id;
 						GList *l;
 
-						group_id = msn_userlist_find_group_id(remote_user->userlist,
-								group_name);
-
 						for (l = remote_user->group_ids; l != NULL; l = l->next)
 						{
-							if (group_id == GPOINTER_TO_INT(l->data))
+							const char *name = msn_userlist_find_group_name(remote_user->userlist, l->data);
+							if (name && !g_strcasecmp(group_name, name))
 							{
 								found = TRUE;
 								break;
 							}
 						}
-
 					}
 
 					if (!found)
@@ -419,3 +456,4 @@
 		msn_cmdproc_send(session->notification->cmdproc, "URL", "%s", "INBOX");
 	}
 }
+
--- a/libpurple/protocols/msn/session.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/session.h	Sun Sep 16 18:06:22 2007 +0000
@@ -38,6 +38,8 @@
 #include "cmdproc.h"
 #include "nexus.h"
 #include "httpconn.h"
+#include "contact.h"
+#include "oim.h"
 
 #include "userlist.h"
 #include "sync.h"
@@ -94,6 +96,8 @@
 
 	MsnNotification *notification;
 	MsnNexus *nexus;
+	MsnContact *contact;
+	MsnOim		*oim;
 	MsnSync *sync;
 
 	MsnUserList *userlist;
@@ -105,8 +109,15 @@
 
 	int conv_seq; /**< The current conversation sequence number. */
 
+	/*psm info*/
+	char *psm;
+
 	struct
 	{
+		/*t and p, get via USR TWN*/
+		char *t;
+		char *p;
+
 		char *kv;
 		char *sid;
 		char *mspauth;
@@ -114,7 +125,6 @@
 		char *file;
 		char *client_ip;
 		int client_port;
-
 	} passport_info;
 };
 
@@ -224,4 +234,8 @@
  */
 void msn_session_finish_login(MsnSession *session);
 
+/*post message to User*/
+void msn_session_report_user(MsnSession *session,const char *passport,
+							char *msg,PurpleMessageFlags flags);
+
 #endif /* _MSN_SESSION_H_ */
--- a/libpurple/protocols/msn/slp.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/slp.c	Sun Sep 16 18:06:22 2007 +0000
@@ -33,6 +33,8 @@
 
 /* ms to delay between sending buddy icon requests to the server. */
 #define BUDDY_ICON_DELAY 20000
+/*debug SLP*/
+#define MSN_DEBUG_UD
 
 static void send_ok(MsnSlpCall *slpcall, const char *branch,
 					const char *type, const char *content);
@@ -777,11 +779,11 @@
 	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) {
 
 		/* FIXME: it would be better if we wrote the data as we received it
-		          instead of all at once, calling write multiple times and
-		          close once at the very end
-		*/
+		   instead of all at once, calling write multiple times and
+		   close once at the very end
+		 */
 		purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
-		purple_conv_custom_smiley_close(conv, slpcall->data_info);
+		purple_conv_custom_smiley_close(conv, slpcall->data_info );
 	}
 #ifdef MSN_DEBUG_UD
 	purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
--- a/libpurple/protocols/msn/slpcall.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Sun Sep 16 18:06:22 2007 +0000
@@ -22,6 +22,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 #include "msn.h"
+#include "msnutils.h"
 #include "slpcall.h"
 #include "slpsession.h"
 
@@ -30,24 +31,6 @@
 /* #define MSN_DEBUG_SLPCALL */
 
 /**************************************************************************
- * Util
- **************************************************************************/
-
-static char *
-rand_guid()
-{
-	return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111,
-			rand() % 0xAAFF + 0x1111);
-}
-
-/**************************************************************************
  * Main
  **************************************************************************/
 
--- a/libpurple/protocols/msn/slplink.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/slplink.c	Sun Sep 16 18:06:22 2007 +0000
@@ -112,8 +112,10 @@
 	if (slplink->remote_user != NULL)
 		g_free(slplink->remote_user);
 
+#if 0
 	if (slplink->directconn != NULL)
 		msn_directconn_destroy(slplink->directconn);
+#endif
 
 	while (slplink->slp_calls != NULL)
 		msn_slp_call_destroy(slplink->slp_calls->data);
@@ -244,11 +246,13 @@
 void
 msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg)
 {
+#if 0
 	if (slplink->directconn != NULL)
 	{
 		msn_directconn_send_msg(slplink->directconn, msg);
 	}
 	else
+#endif
 	{
 		if (slplink->swboard == NULL)
 		{
@@ -634,9 +638,10 @@
 			MsnDirectConn *directconn;
 
 			directconn = slplink->directconn;
-
+#if 0
 			if (!directconn->acked)
 				msn_directconn_send_handshake(directconn);
+#endif
 		}
 		else if (slpmsg->flags == 0x0 || slpmsg->flags == 0x20 ||
 				 slpmsg->flags == 0x1000030)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/soap.c	Sun Sep 16 18:06:22 2007 +0000
@@ -0,0 +1,732 @@
+/**
+ * @file soap.c 
+ * 	SOAP connection related process
+ *	Author
+ * 		MaYuan<mayuan2006@gmail.com>
+ * 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 "msn.h"
+#include "soap.h"
+
+
+/*local function prototype*/
+void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step);
+
+/*setup the soap process step*/
+void
+msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step)
+{
+	soapconn->step = step;
+}
+
+//msn_soap_new(MsnSession *session,gpointer data,int sslconn)
+/*new a soap connection*/
+MsnSoapConn *
+msn_soap_new(MsnSession *session,gpointer data,int sslconn)
+{
+	MsnSoapConn *soapconn;
+
+	soapconn = g_new0(MsnSoapConn, 1);
+	soapconn->session = session;
+	soapconn->parent = data;
+	soapconn->ssl_conn = sslconn;
+
+	soapconn->gsc = NULL;
+	soapconn->input_handler = 0;
+	soapconn->output_handler = 0;
+
+	msn_soap_set_process_step(soapconn,MSN_SOAP_UNCONNECTED);
+	soapconn->soap_queue = g_queue_new();
+	return soapconn;
+}
+
+/*ssl soap connect callback*/
+void
+msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc,
+				 PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn;
+	MsnSession *session;
+
+	purple_debug_misc("MSN SOAP","SOAP server connection established!\n");
+
+	soapconn = data;
+	g_return_if_fail(soapconn != NULL);
+
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+	soapconn->gsc = gsc;
+
+	/*connection callback*/
+	if(soapconn->connect_cb != NULL){
+		soapconn->connect_cb(data,gsc,cond);
+	}
+
+	msn_soap_set_process_step(soapconn,MSN_SOAP_CONNECTED);
+	/*we do the SOAP request here*/
+	msn_soap_post_head_request(soapconn);
+}
+
+/*ssl soap error callback*/
+static void
+msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+{	
+	MsnSoapConn * soapconn = data;
+
+	g_return_if_fail(data != NULL);
+	purple_debug_warning("MSN SOAP","Soap connection error!\n");
+	msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED);
+
+	/*error callback*/
+	if(soapconn->error_cb != NULL){
+		soapconn->error_cb(gsc,error,data);
+	}
+}
+
+/*init the soap connection*/
+void
+msn_soap_init(MsnSoapConn *soapconn,char * host,int ssl,
+				PurpleSslInputFunction	connect_cb,
+				PurpleSslErrorFunction	error_cb)
+{
+	purple_debug_misc("MSN SOAP","Initializing SOAP connection\n");
+	soapconn->login_host = g_strdup(host);
+	soapconn->ssl_conn = ssl;
+	soapconn->connect_cb = connect_cb;
+	soapconn->error_cb = error_cb;
+}
+
+/*connect the soap connection*/
+void
+msn_soap_connect(MsnSoapConn *soapconn)
+{
+	if(soapconn->ssl_conn){
+		purple_ssl_connect(soapconn->session->account, soapconn->login_host,
+				PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, msn_soap_error_cb,
+				soapconn);
+	}else{
+	}
+	msn_soap_set_process_step(soapconn,MSN_SOAP_CONNECTING);
+}
+
+/*close the soap connection*/
+void
+msn_soap_close(MsnSoapConn *soapconn)
+{
+	if(soapconn->ssl_conn){
+		if(soapconn->gsc != NULL){
+			purple_ssl_close(soapconn->gsc);
+			soapconn->gsc = NULL;
+		}
+	}else{
+	}
+	msn_soap_set_process_step(soapconn,MSN_SOAP_UNCONNECTED);
+}
+
+/*clean the unhandled SOAP request*/
+void
+msn_soap_clean_unhandled_request(MsnSoapConn *soapconn)
+{
+	MsnSoapReq *request;
+
+	g_return_if_fail(soapconn != NULL);
+
+	soapconn->body = NULL;
+
+	while ((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){
+		if (soapconn->read_cb) {
+			soapconn->read_cb(soapconn, -1, 0);
+		}
+		msn_soap_request_free(request);
+	}
+}
+
+/*destroy the soap connection*/
+void
+msn_soap_destroy(MsnSoapConn *soapconn)
+{
+	if(soapconn->login_host)
+		g_free(soapconn->login_host);
+
+	if(soapconn->login_path)
+		g_free(soapconn->login_path);
+
+	/*remove the write handler*/
+	if (soapconn->output_handler > 0){
+		purple_input_remove(soapconn->output_handler);
+		soapconn->output_handler = 0;
+	}
+	/*remove the read handler*/
+	if (soapconn->input_handler > 0){
+		purple_input_remove(soapconn->input_handler);
+		soapconn->input_handler = 0;
+	}
+	msn_soap_free_read_buf(soapconn);
+	msn_soap_free_write_buf(soapconn);
+
+	/*close ssl connection*/
+	msn_soap_close(soapconn);
+
+	/*process the unhandled soap request*/
+	msn_soap_clean_unhandled_request(soapconn);
+
+	g_queue_free(soapconn->soap_queue);
+	g_free(soapconn);
+}
+
+/*check the soap is connected?
+ * if connected return 1
+ */
+int
+msn_soap_connected(MsnSoapConn *soapconn)
+{
+	if(soapconn->ssl_conn){
+		return (soapconn->gsc == NULL? 0 : 1);
+	}
+	return(soapconn->fd>0? 1 : 0);
+}
+
+/*read and append the content to the buffer*/
+static gssize
+msn_soap_read(MsnSoapConn *soapconn)
+{
+	gssize len, requested_len;
+	char temp_buf[MSN_SOAP_READ_BUFF_SIZE];
+	
+	if ( soapconn->need_to_read == 0 || soapconn->need_to_read > MSN_SOAP_READ_BUFF_SIZE) {
+		requested_len = MSN_SOAP_READ_BUFF_SIZE;
+	}
+	else {
+		requested_len = soapconn->need_to_read;
+	}
+
+	if ( soapconn->ssl_conn ) {
+		len = purple_ssl_read(soapconn->gsc, temp_buf, requested_len);
+	} else {
+		len = read(soapconn->fd, temp_buf, requested_len);
+	}
+
+	
+	if ( len <= 0 ) {
+		switch (errno) {
+
+			case 0:
+			case EBADF: /* we are sometimes getting this in Windows */
+			case EAGAIN: return len;
+
+			default : purple_debug_error("MSN SOAP", "Read error!"
+						"read len: %d, error = %s\n",
+						len, strerror(errno));
+				  purple_input_remove(soapconn->input_handler);
+				  soapconn->input_handler = 0;
+				  g_free(soapconn->read_buf);
+				  soapconn->read_buf = NULL;
+				  soapconn->read_len = 0;
+				  /* TODO: error handling */
+				  return len;
+		}
+	}
+	else {
+		soapconn->read_buf = g_realloc(soapconn->read_buf,
+						soapconn->read_len + len + 1);
+		if ( soapconn->read_buf != NULL ) {
+			memcpy(soapconn->read_buf + soapconn->read_len, temp_buf, len);
+			soapconn->read_len += len;
+			soapconn->read_buf[soapconn->read_len] = '\0';
+		}
+		else {
+			purple_debug_error("MSN SOAP", "Failure re-allocating %d bytes of memory!\n", soapconn->read_len + len + 1);
+			exit(EXIT_FAILURE);
+		}
+			
+	}
+
+#if defined(MSN_SOAP_DEBUG)
+	if (len > 0)
+		purple_debug_info("MSN SOAP","Read %d bytes from SOAP server:\n%s\n", len, soapconn->read_buf + soapconn->read_len - len);
+#endif
+
+	return len;
+}
+
+/*read the whole SOAP server response*/
+void 
+msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+	MsnSession *session;
+	int len;
+	char * body_start,*body_len;
+	char *length_start,*length_end;
+#ifdef MSN_SOAP_DEBUG
+#if !defined(_WIN32)
+	gchar * formattedxml = NULL;
+	gchar * http_headers = NULL;
+	xmlnode * node = NULL;
+#endif
+	purple_debug_misc("MSN SOAP", "msn_soap_read_cb()\n");
+#endif
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+	
+	/*read the request header*/
+	len = msn_soap_read(soapconn);
+	
+	if ( len < 0 )
+		return;
+
+	if (soapconn->read_buf == NULL) {
+		return;
+	}
+
+	if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL) 
+		|| ( strstr(soapconn->read_buf, "HTTP/1.1 301") != NULL ) )
+	{
+		/* Redirect. */
+		char *location, *c;
+
+		purple_debug_info("MSN SOAP", "HTTP Redirect\n");
+		location = strstr(soapconn->read_buf, "Location: ");
+		if (location == NULL)
+		{
+			msn_soap_free_read_buf(soapconn);
+
+			return;
+		}
+		location = strchr(location, ' ') + 1;
+
+		if ((c = strchr(location, '\r')) != NULL)
+			*c = '\0';
+
+		/* Skip the http:// */
+		if ((c = strchr(location, '/')) != NULL)
+			location = c + 2;
+
+		if ((c = strchr(location, '/')) != NULL)
+		{
+			g_free(soapconn->login_path);
+			soapconn->login_path = g_strdup(c);
+
+			*c = '\0';
+		}
+
+		g_free(soapconn->login_host);
+		soapconn->login_host = g_strdup(location);
+
+		purple_ssl_connect(session->account, soapconn->login_host,
+			PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb,
+			msn_soap_error_cb, soapconn);
+	}
+	/* Another case of redirection, active on May, 2007
+	   See http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener#Redirect
+	 */
+	else if (strstr(soapconn->read_buf,
+                    "<faultcode>psf:Redirect</faultcode>") != NULL)
+	{
+		char *location, *c;
+
+		location = strstr(soapconn->read_buf, "<psf:redirectUrl>");
+		/* Omit the tag preceding the URL */
+		location += strlen("<psf:redirectUrl>");
+		location = strstr(location, ":/");
+		if (location == NULL)
+		{
+			msn_soap_free_read_buf(soapconn);
+			return;
+		}
+
+		location += strlen("://"); /* Skip http:// or https:// */
+
+		if ( (c = strstr(location, "</psf:redirectUrl>")) != NULL )
+			*c = '\0';
+
+		if ( (c = strstr(location, "/")) != NULL )
+		{
+			g_free(soapconn->login_path);
+			soapconn->login_path = g_strdup(c);
+			*c = '\0';
+		}
+
+		g_free(soapconn->login_host);
+		soapconn->login_host = g_strdup(location);
+
+		purple_ssl_connect(session->account, soapconn->login_host,
+			PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb,
+			msn_soap_error_cb, soapconn);
+	}
+	else if (strstr(soapconn->read_buf, "HTTP/1.1 401 Unauthorized") != NULL)
+	{
+		const char *error;
+
+		purple_debug_error("MSN SOAP", "Received HTTP error 401 Unauthorized\n");
+		if ((error = strstr(soapconn->read_buf, "WWW-Authenticate")) != NULL)
+		{
+			if ((error = strstr(error, "cbtxt=")) != NULL)
+			{
+				const char *c;
+				char *temp;
+
+				error += strlen("cbtxt=");
+
+				if ((c = strchr(error, '\n')) == NULL)
+					c = error + strlen(error);
+
+				temp = g_strndup(error, c - error);
+				error = purple_url_decode(temp);
+				g_free(temp);
+			}
+		}
+
+		msn_session_set_error(session, MSN_ERROR_AUTH, error);
+	}
+	/* Handle Passport 3.0 authentication failures.
+	 * Further info: http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
+	 */
+	else if (strstr(soapconn->read_buf,
+				"<faultcode>wsse:FailedAuthentication</faultcode>") != NULL)
+	{
+		char *faultstring;
+
+		faultstring = strstr(soapconn->read_buf, "<faultstring>");
+
+		if (faultstring != NULL)
+		{
+			faultstring += strlen("<faultstring>");
+			*strstr(soapconn->read_buf, "</faultstring>") = '\0';
+		}
+		
+		msn_session_set_error(session, MSN_ERROR_AUTH, faultstring);
+	}
+	else if (strstr(soapconn->read_buf, "HTTP/1.1 503 Service Unavailable"))
+	{
+		msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
+	}
+	else if ((strstr(soapconn->read_buf, "HTTP/1.1 200 OK"))
+		||(strstr(soapconn->read_buf, "HTTP/1.1 500")))
+	{
+			/*OK! process the SOAP body*/
+			body_start = (char *)g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n");
+			if (!body_start) {
+				return;
+			}
+			body_start += 4;
+
+			//	purple_debug_misc("msn", "Soap Read: {%s}\n", soapconn->read_buf);
+
+			/* we read the content-length*/
+			length_start = strstr(soapconn->read_buf, "Content-Length: ");
+			length_start += strlen("Content-Length: ");
+			length_end = strstr(length_start, "\r\n");
+			body_len = g_strndup(length_start, length_end - length_start);
+
+			/*setup the conn body */
+			soapconn->body		= body_start;
+			soapconn->body_len	= atoi(body_len);
+			g_free(body_len);
+#ifdef MSN_SOAP_DEBUG
+			purple_debug_misc("MSN SOAP","SOAP bytes read so far: %d, Content-Length: %d\n", soapconn->read_len, soapconn->body_len);
+#endif
+			soapconn->need_to_read = (body_start - soapconn->read_buf + soapconn->body_len) - soapconn->read_len;
+			if ( soapconn->need_to_read > 0 ) {
+				return;
+			}
+
+#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32)
+
+			node = xmlnode_from_str(soapconn->body, soapconn->body_len);
+	
+			if (node != NULL) {
+				formattedxml = xmlnode_to_formatted_str(node, NULL);
+				http_headers = g_strndup(soapconn->read_buf, soapconn->body - soapconn->read_buf);
+				
+				purple_debug_info("MSN SOAP","Data with XML payload received from the SOAP server:\n%s%s\n", http_headers, formattedxml);
+				g_free(http_headers);
+				g_free(formattedxml);
+				xmlnode_free(node);
+			}
+			else
+				purple_debug_info("MSN SOAP","Data received from the SOAP server:\n%s\n", soapconn->read_buf);
+#endif
+
+			/*remove the read handler*/
+			purple_input_remove(soapconn->input_handler);
+			soapconn->input_handler = 0;
+			/*
+			 * close the soap connection,if more soap request came,
+			 * Just reconnect to do it,
+			 *
+			 * To solve the problem described below:
+			 * When I post the soap request in one socket one after the other,
+			 * The first read is ok, But the second soap read always got 0 bytes,
+			 * Weird!
+			 * */
+			msn_soap_close(soapconn);
+
+			/*call the read callback*/
+			if ( soapconn->read_cb != NULL ) {
+				soapconn->read_cb(soapconn, source, 0);
+			}
+	}
+	return;
+}
+
+void 
+msn_soap_free_read_buf(MsnSoapConn *soapconn)
+{
+	g_return_if_fail(soapconn != NULL);
+	
+	if (soapconn->read_buf) {
+		g_free(soapconn->read_buf);
+	}
+	soapconn->read_buf = NULL;
+	soapconn->read_len = 0;
+	soapconn->need_to_read = 0;
+}
+
+void
+msn_soap_free_write_buf(MsnSoapConn *soapconn)
+{
+	g_return_if_fail(soapconn != NULL);
+
+	if (soapconn->write_buf) {
+		g_free(soapconn->write_buf);
+	}
+	soapconn->write_buf = NULL;
+	soapconn->written_len = 0;
+}
+
+/*Soap write process func*/
+static void
+msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+	int len, total_len;
+
+	g_return_if_fail(soapconn != NULL);
+	if ( soapconn->write_buf == NULL ) {
+		purple_debug_error("MSN SOAP","SOAP buffer is NULL\n");
+		purple_input_remove(soapconn->output_handler);
+		soapconn->output_handler = -1;
+		return;
+	}
+	total_len = strlen(soapconn->write_buf);
+
+	/* 
+	 * write the content to SSL server,
+	 */
+	len = purple_ssl_write(soapconn->gsc,
+		soapconn->write_buf + soapconn->written_len,
+		total_len - soapconn->written_len);
+
+	if (len < 0 && errno == EAGAIN)
+		return;
+	else if (len <= 0){
+		/*SSL write error!*/
+		purple_input_remove(soapconn->output_handler);
+		soapconn->output_handler = -1;
+		/* TODO: notify of the error */
+		purple_debug_error("MSN SOAP","Error writing to SSL connection!\n");
+		return;
+	}
+	soapconn->written_len += len;
+
+	if (soapconn->written_len < total_len)
+		return;
+
+	purple_input_remove(soapconn->output_handler);
+	soapconn->output_handler = -1;
+
+	/*clear the write buff*/
+	msn_soap_free_write_buf(soapconn);
+
+	/* Write finish!
+	 * callback for write done
+	 */
+	if(soapconn->written_cb != NULL){
+		soapconn->written_cb(soapconn, source, 0);
+	}
+	/*maybe we need to read the input?*/
+	if (soapconn->input_handler == 0) {
+		soapconn->input_handler = purple_input_add(soapconn->gsc->fd,
+			PURPLE_INPUT_READ, msn_soap_read_cb, soapconn);
+	}
+//	msn_soap_read_cb(soapconn,source,0);
+}
+
+/*write the buffer to SOAP connection*/
+void
+msn_soap_write(MsnSoapConn * soapconn, char *write_buf, PurpleInputFunction written_cb)
+{
+	soapconn->write_buf = write_buf;
+	soapconn->written_len = 0;
+	soapconn->written_cb = written_cb;
+	
+	msn_soap_free_read_buf(soapconn);
+
+	/*clear the read buffer first*/
+	/*start the write*/
+	soapconn->output_handler = purple_input_add(soapconn->gsc->fd, PURPLE_INPUT_WRITE,
+													msn_soap_write_cb, soapconn);
+	msn_soap_write_cb(soapconn, soapconn->gsc->fd, PURPLE_INPUT_WRITE);
+}
+
+/* New a soap request*/
+MsnSoapReq *
+msn_soap_request_new(const char *host,const char *post_url,const char *soap_action,
+				const char *body, const gpointer data_cb,
+				PurpleInputFunction read_cb,PurpleInputFunction written_cb)
+{
+	MsnSoapReq *request;
+
+	request = g_new0(MsnSoapReq, 1);
+	request->id = 0;
+
+	request->login_host = g_strdup(host);
+	request->login_path = g_strdup(post_url);
+	request->soap_action		= g_strdup(soap_action);
+	request->body		= g_strdup(body);
+	request->data_cb 	= data_cb;
+	request->read_cb	= read_cb;
+	request->written_cb	= written_cb;
+
+	return request;
+}
+
+/*free a soap request*/
+void
+msn_soap_request_free(MsnSoapReq *request)
+{
+	g_return_if_fail(request != NULL);
+
+	g_free(request->login_host);
+	g_free(request->login_path);
+	g_free(request->soap_action);
+	g_free(request->body);
+	request->read_cb	= NULL;
+	request->written_cb	= NULL;
+
+	g_free(request);
+}
+
+/*post the soap request queue's head request*/
+void
+msn_soap_post_head_request(MsnSoapConn *soapconn)
+{
+	purple_debug_info("MSN SOAP", "Posting new request from head of the queue\n");
+	
+	g_return_if_fail(soapconn->soap_queue != NULL);
+
+	if(!g_queue_is_empty(soapconn->soap_queue)){
+		MsnSoapReq *request;
+		if((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){
+			msn_soap_post_request(soapconn,request);
+		}
+	} else {
+		purple_debug_info("MSN SOAP", "No requests to process found.\n");
+		msn_soap_set_process_step(soapconn,MSN_SOAP_CONNECTED_IDLE);
+	}
+}
+
+/*post the soap request ,
+ * if not connected, Connected first.
+ */
+void
+msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request,
+				MsnSoapConnectInitFunction msn_soap_init_func)
+{
+	if (request != NULL) {
+		g_queue_push_tail(soapconn->soap_queue, request);
+	}
+	if (!msn_soap_connected(soapconn) && (soapconn->step == MSN_SOAP_UNCONNECTED)
+					&&(!g_queue_is_empty(soapconn->soap_queue))) {
+		/*not connected?and we have something to process connect it first*/
+		purple_debug_misc("MSN SOAP","No connection to SOAP server. Connecting...\n");
+		msn_soap_init_func(soapconn);
+		msn_soap_connect(soapconn);
+		return;
+	}
+	purple_debug_misc("MSN SOAP","Connected to SOAP server\n");
+
+	/*if connected, what we only needed to do is to queue the request, 
+	 * when SOAP request in the queue processed done, will do this command.
+	 * we just waiting...
+	 * If we send the request this time,error may occure
+	 */
+	if (soapconn->step == MSN_SOAP_CONNECTED_IDLE){
+		msn_soap_post_head_request(soapconn);
+	}
+}
+
+/*Post the soap request action*/
+void
+msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request)
+{
+	char * soap_head = NULL;
+	char * request_str = NULL;
+#ifdef MSN_SOAP_DEBUG
+#if !defined(_WIN32)
+	xmlnode * node;
+#endif
+	purple_debug_misc("MSN SOAP","msn_soap_post_request()\n");
+#endif
+
+	msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING);
+	soap_head = g_strdup_printf(
+					"POST %s HTTP/1.1\r\n"
+					"SOAPAction: %s\r\n"
+					"Content-Type:text/xml; charset=utf-8\r\n"
+					"Cookie: MSPAuth=%s\r\n"
+					"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
+					"Accept: */*\r\n"
+					"Host: %s\r\n"
+					"Content-Length: %" G_GSIZE_FORMAT "\r\n"
+					"Connection: Keep-Alive\r\n"
+					"Cache-Control: no-cache\r\n\r\n",
+					request->login_path,
+					request->soap_action,
+					soapconn->session->passport_info.mspauth,
+					request->login_host,
+					strlen(request->body)
+					);
+	request_str = g_strdup_printf("%s%s", soap_head, request->body);
+
+#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32)
+	node = xmlnode_from_str(request->body, -1);
+	if (node != NULL) {
+		char *formattedstr = xmlnode_to_formatted_str(node, NULL);
+		purple_debug_info("MSN SOAP","Posting request to SOAP server:\n%s%s\n",soap_head, formattedstr);
+		g_free(formattedstr);
+		xmlnode_free(node);
+	}
+	else
+		purple_debug_info("MSN SOAP","Failed to parse SOAP request being sent:\n%s\n", request_str);
+#endif
+	
+	g_free(soap_head);
+	/*free read buffer*/
+	// msn_soap_free_read_buf(soapconn);
+	/*post it to server*/
+	soapconn->data_cb = request->data_cb;
+	msn_soap_write(soapconn, request_str, request->written_cb);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/soap.h	Sun Sep 16 18:06:22 2007 +0000
@@ -0,0 +1,155 @@
+/**
+ * @file soap.h
+ * 	header file for SOAP connection related process
+ *	Author
+ * 		MaYuan<mayuan2006@gmail.com>
+ * 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_SOAP_H_
+#define _MSN_SOAP_H_
+
+#define MSN_SOAP_READ_BUFF_SIZE		8192
+
+/* define this to debug the communications with the SOAP server */
+/* #define MSN_SOAP_DEBUG  */
+
+
+typedef enum
+{
+	MSN_SOAP_UNCONNECTED,
+	MSN_SOAP_CONNECTING,
+	MSN_SOAP_CONNECTED,
+	MSN_SOAP_PROCESSING,
+	MSN_SOAP_CONNECTED_IDLE
+}MsnSoapStep;
+
+/*MSN SoapRequest structure*/
+typedef struct _MsnSoapReq MsnSoapReq;
+
+/*MSN Https connection structure*/
+typedef struct _MsnSoapConn MsnSoapConn;
+
+typedef void (*MsnSoapConnectInitFunction)(MsnSoapConn *);
+
+
+struct _MsnSoapReq{
+	/*request sequence*/
+	int	 id;
+
+	char *login_host;
+	char *login_path;
+	char *soap_action;
+
+	char *body;
+	
+	gpointer data_cb;
+	PurpleInputFunction read_cb;
+	PurpleInputFunction written_cb;
+};
+
+struct _MsnSoapConn{
+	MsnSession *session;
+	gpointer parent;
+
+	char *login_host;
+	char *login_path;
+	char *soap_action;
+
+	MsnSoapStep step;
+	/*ssl connection?*/
+	guint	ssl_conn;
+	/*normal connection*/
+	guint	fd;
+	/*SSL connection*/
+	PurpleSslConnection *gsc;
+	/*ssl connection callback*/
+	PurpleSslInputFunction	connect_cb;
+	/*ssl error callback*/
+	PurpleSslErrorFunction	error_cb;
+
+	/*read handler*/
+	guint input_handler;
+	/*write handler*/
+	guint output_handler;
+
+	/*Queue of SOAP request to send*/
+	int soap_id;
+	GQueue *soap_queue;
+
+	/*write buffer*/
+	char *write_buf;
+	gsize written_len;
+	PurpleInputFunction written_cb;
+
+	/*read buffer*/
+	char *read_buf;
+	gsize read_len;
+	gsize need_to_read;
+	PurpleInputFunction read_cb;
+
+	gpointer data_cb;
+
+	/*HTTP reply body part*/
+	char *body;
+	int body_len;
+};
+
+
+/*Function Prototype*/
+/*Soap Request Function */
+MsnSoapReq *msn_soap_request_new(const char *host, const char *post_url,
+				 const char *soap_action, const char *body,
+				 const gpointer data_cb,
+				 PurpleInputFunction read_cb,
+				 PurpleInputFunction written_cb);
+
+void msn_soap_request_free(MsnSoapReq *request);
+void msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request);
+void msn_soap_post_head_request(MsnSoapConn *soapconn);
+
+/*new a soap conneciton */
+MsnSoapConn *msn_soap_new(MsnSession *session,gpointer data,int sslconn);
+
+/*destroy */
+void msn_soap_destroy(MsnSoapConn *soapconn);
+
+/*init a soap conneciton */
+void msn_soap_init(MsnSoapConn *soapconn,char * host,int ssl,PurpleSslInputFunction connect_cb,PurpleSslErrorFunction error_cb);
+void msn_soap_connect(MsnSoapConn *soapconn);
+void msn_soap_close(MsnSoapConn *soapconn);
+
+/*write to soap*/
+void msn_soap_write(MsnSoapConn * soapconn, char *write_buf, PurpleInputFunction written_cb);
+void msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request,MsnSoapConnectInitFunction msn_soap_init_func);
+
+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 request*/
+void msn_soap_clean_unhandled_request(MsnSoapConn *soapconn);
+
+/*check if the soap connection is connected*/
+int msn_soap_connected(MsnSoapConn *soapconn);
+
+#endif/*_MSN_SOAP_H_*/
+
--- a/libpurple/protocols/msn/state.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/state.c	Sun Sep 16 18:06:22 2007 +0000
@@ -38,6 +38,197 @@
 	N_("Available")
 };
 
+/* Local Function Prototype*/
+static char *msn_build_psm(const char *psmstr,const char *mediastr,
+						   const char *guidstr);
+
+/*
+ * WLM media PSM info build prcedure
+ *
+ * Result can like:
+ *	<CurrentMedia>\0Music\01\0{0} - {1}\0Song Title\0Song Artist\0Song Album\0\0</CurrentMedia>\
+ *	<CurrentMedia>\0Games\01\0Playing {0}\0Game Name\0</CurrentMedia>\
+ *	<CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia>"
+ */
+static char *
+msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr)
+{
+	xmlnode *dataNode,*psmNode,*mediaNode,*guidNode;
+	char *result;
+	int length;
+
+	dataNode = xmlnode_new("Data");
+
+	psmNode = xmlnode_new("PSM");
+	if(psmstr != NULL){
+		xmlnode_insert_data(psmNode,psmstr,strlen(psmstr));
+	}
+	xmlnode_insert_child(dataNode,psmNode);
+
+	mediaNode = xmlnode_new("CurrentMedia");
+	if(mediastr != NULL){
+		xmlnode_insert_data(psmNode,mediastr,strlen(mediastr));
+	}
+	xmlnode_insert_child(dataNode,mediaNode);
+
+	guidNode = xmlnode_new("MachineGuid");
+	if(guidstr != NULL){
+		xmlnode_insert_data(guidNode,guidstr,strlen(guidstr));
+	}
+	xmlnode_insert_child(dataNode,guidNode);
+
+	result = xmlnode_to_str(dataNode,&length);
+	xmlnode_free(dataNode);
+	return result;
+}
+
+/* parse CurrentMedia string */
+char *
+msn_parse_currentmedia(const char *cmedia)
+{
+	char **cmedia_array;
+	GString *buffer = NULL;
+	int strings;
+
+	if ((cmedia == NULL) || (*cmedia == '\0')) {
+		purple_debug_info("msn", "No currentmedia string\n");
+		return NULL;
+	}
+
+	purple_debug_info("msn", "Parsing currentmedia string: \"%s\"\n", cmedia);
+
+	cmedia_array = g_strsplit(cmedia, "\\0", 0);
+
+	strings = 0;
+	/* Yes, we want to skip the first element here, as it is empty due to
+	 * the cmedia string starting with \0 -- see the examples below.
+	while (cmedia_array[++strings] != NULL);
+
+	/* The cmedia_array[2] field contains a 1 if enabled. */
+	if ((strings > 3) && (!strcmp(cmedia_array[2], "1"))) {
+		char *inptr = cmedia_array[3];
+
+		buffer = g_string_new(NULL);
+
+		while (*inptr != '\0') {
+			if ((*inptr == '{') && ((*(inptr + 1) != '\0') && (*(inptr+2) == '}')) {
+				char *tmpptr;
+				int tmp;
+
+				errno = 0;
+				tmp = strtol(inptr + 1, &tmpptr, 10);
+
+				if (errno == 0 && tmpptr != inptr + 1 &&
+				    tmp + 4 < strings) {
+					/* Replace {?} tag with appropriate text only when successful.
+					 * Skip otherwise. */
+					buffer = g_string_append(buffer, cmedia_array[tmp + 4]);
+				}
+				inptr += 3; /* Skip to the next char after '}' */
+			} else {
+				buffer = g_string_append_c(buffer, *inptr++);
+			}
+		}
+		purple_debug_info("msn", "Parsed currentmedia string, result: \"%s\"\n",
+		                  buffer->str);
+	} else {
+		purple_debug_info("msn", "Current media marked disabled, not parsing.\n");
+	}
+
+	g_strfreev(cmedia_array);
+	return buffer ? g_string_free(buffer, FALSE) : NULL;
+}
+
+/* get the CurrentMedia info from the XML string */
+char *
+msn_get_currentmedia(char *xml_str, gsize len)
+{
+	xmlnode *payloadNode, *currentmediaNode;
+	char *currentmedia;
+	
+	purple_debug_info("msn","msn get CurrentMedia\n");
+	payloadNode = xmlnode_from_str(xml_str, len);
+	if (!payloadNode){
+		purple_debug_error("msn","PSM XML parse Error!\n");
+		return NULL;
+	}
+	currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia");
+	if (currentmediaNode == NULL){
+		purple_debug_info("msn","No CurrentMedia Node");
+		xmlnode_free(payloadNode);
+		return NULL;
+	}
+	currentmedia = xmlnode_get_data(currentmediaNode);
+
+	xmlnode_free(payloadNode);
+
+	return currentmedia;
+}
+
+/*get the PSM info from the XML string*/
+char *
+msn_get_psm(char *xml_str, gsize len)
+{
+	xmlnode *payloadNode, *psmNode;
+	char *psm;
+	
+	purple_debug_info("MSNP14","msn get PSM\n");
+	payloadNode = xmlnode_from_str(xml_str, len);
+	if (!payloadNode){
+		purple_debug_error("MSNP14","PSM XML parse Error!\n");
+		return NULL;
+	}
+	psmNode = xmlnode_get_child(payloadNode, "PSM");
+	if (psmNode == NULL){
+		purple_debug_info("MSNP14","No PSM status Node");
+		xmlnode_free(payloadNode);
+		return NULL;
+	}
+	psm = xmlnode_get_data(psmNode);
+
+	xmlnode_free(payloadNode);
+
+	return psm;
+}
+
+/* set the MSN's PSM info,Currently Read from the status Line 
+ * Thanks for Cris Code
+ */
+void
+msn_set_psm(MsnSession *session)
+{
+	PurpleAccount *account = session->account;
+	PurplePresence *presence;
+	PurpleStatus *status;
+	MsnCmdProc *cmdproc;
+	MsnTransaction *trans;
+	char *payload;
+	const char *statusline;
+	gchar *unescapedstatusline;
+
+	g_return_if_fail(session != NULL);
+	g_return_if_fail(session->notification != NULL);
+
+	cmdproc = session->notification->cmdproc;
+	/*prepare PSM info*/
+	if(session->psm){
+		g_free(session->psm);
+	}
+	/*Get the PSM string from Purple's Status Line*/
+	presence = purple_account_get_presence(account);
+	status = purple_presence_get_active_status(presence);
+	statusline = purple_status_get_attr_string(status, "message");
+	unescapedstatusline = purple_unescape_html(statusline);
+	session->psm = msn_build_psm(unescapedstatusline, NULL, NULL);
+	g_free(unescapedstatusline);
+	payload = session->psm;
+
+	purple_debug_misc("MSNP14","Sending UUX command with payload: %s\n",payload);
+	trans = msn_transaction_new(cmdproc, "UUX","%d",strlen(payload));
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+	msn_cmdproc_send_trans(cmdproc, trans);
+}
+
 void
 msn_change_status(MsnSession *session)
 {
@@ -79,6 +270,7 @@
 
 		g_free(msnobj_str);
 	}
+	msn_set_psm(session);
 }
 
 const char *
--- a/libpurple/protocols/msn/state.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/state.h	Sun Sep 16 18:06:22 2007 +0000
@@ -59,6 +59,17 @@
 
 const char *msn_state_get_text(MsnAwayType state);
 
+void msn_set_psm(MsnSession *session);
+
+/* Parse CurrentMedia string */
+char * msn_parse_currentmedia(const char *cmedia);
+
+/* Get the CurrentMedia info from the XML string */
+char * msn_get_currentmedia(char *xml_str,gsize len);
+
+/*get the PSM info from the XML string*/
+char * msn_get_psm(char *xml_str,gsize len);
+
 MsnAwayType msn_state_from_account(PurpleAccount *account);
 
 #endif /* _MSN_STATE_H_ */
--- a/libpurple/protocols/msn/switchboard.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Sun Sep 16 18:06:22 2007 +0000
@@ -25,7 +25,7 @@
 #include "prefs.h"
 #include "switchboard.h"
 #include "notification.h"
-#include "msn-utils.h"
+#include "msnutils.h"
 
 #include "error.h"
 
@@ -534,6 +534,7 @@
 	payload = msn_message_gen_payload(msg, &payload_len);
 
 #ifdef MSN_DEBUG_SB
+	purple_debug_info("MSNP14","SB length:{%d}",payload_len);
 	msn_message_show_readable(msg, "SB SEND", FALSE);
 #endif
 
@@ -621,6 +622,7 @@
 	g_return_if_fail(swboard != NULL);
 	g_return_if_fail(msg     != NULL);
 
+	purple_debug_info("MSNP14","switchboard send msg..\n");
 	if (msn_switchboard_can_send(swboard))
 		release_msg(swboard, msg);
 	else if (queue)
@@ -727,7 +729,8 @@
 
 	msg = msn_message_new_from_cmd(cmdproc->session, cmd);
 
-	msn_message_parse_payload(msg, payload, len);
+	msn_message_parse_payload(msg, payload, len,
+					MSG_LINE_DEM,MSG_BODY_DEM);
 #ifdef MSN_DEBUG_SB
 	msn_message_show_readable(msg, "SB RECV", FALSE);
 #endif
@@ -749,6 +752,14 @@
 }
 
 static void
+ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_misc("MSNP14","get UBM...\n");
+	cmdproc->servconn->payload_len = atoi(cmd->params[4]);
+	cmdproc->last_cmd->payload_cb = msg_cmd_post;
+}
+
+static void
 nak_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnMessage *msg;
@@ -1095,6 +1106,8 @@
 cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
 {
 	int reason = MSN_SB_ERROR_UNKNOWN;
+	MsnMessage *msg;
+	MsnSwitchBoard *swboard = trans->data;
 
 	if (error == 215)
 	{
@@ -1107,7 +1120,19 @@
 	}
 
 	purple_debug_warning("msn", "cal_error: command %s gave error %i\n", trans->command, error);
+	purple_debug_warning("msn", "Will Use Offline Message to sendit\n");
 
+//	cal_error_helper(trans, reason);
+	/*offline Message send Process*/
+
+	while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL){
+		purple_debug_warning("MSNP14","offline msg to send:{%s}\n",msg->body);
+		/* The messages could not be sent due to a switchboard error */
+		swboard->error = MSN_SB_ERROR_USER_OFFLINE;
+		msg_error_helper(swboard->cmdproc, msg,
+							 MSN_MSG_ERROR_SB);
+		msn_message_unref(msg);
+	}
 	cal_error_helper(trans, reason);
 }
 
@@ -1151,6 +1176,7 @@
 		/* The conversation window was closed. */
 		return;
 
+	purple_debug_info("MSNP14","Switchboard:auth:{%s} socket:{%s}\n",cmd->params[4],cmd->params[2]);
 	msn_switchboard_set_auth_key(swboard, cmd->params[4]);
 
 	msn_parse_socket(cmd->params[2], &host, &port);
@@ -1263,6 +1289,7 @@
 	msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
 
 	msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "UBM", ubm_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
--- a/libpurple/protocols/msn/sync.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/sync.c	Sun Sep 16 18:06:22 2007 +0000
@@ -90,9 +90,9 @@
 {
 	MsnSession *session = cmdproc->session;
 	const char *name;
-	int group_id;
+	const char *group_id;
 
-	group_id = atoi(cmd->params[0]);
+	group_id = cmd->params[0];
 	name = purple_url_decode(cmd->params[1]);
 
 	msn_group_new(session->userlist, group_id, name);
@@ -156,10 +156,10 @@
 
 		for (c = tokens; *c != NULL; c++)
 		{
-			int id;
+			char *id;
 
-			id = atoi(*c);
-			group_ids = g_slist_append(group_ids, GINT_TO_POINTER(id));
+			id = *c;
+			group_ids = g_slist_append(group_ids, g_strdup(id));
 		}
 
 		g_strfreev(tokens);
--- a/libpurple/protocols/msn/user.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Sun Sep 16 18:06:22 2007 +0000
@@ -25,6 +25,7 @@
 #include "user.h"
 #include "slp.h"
 
+/*new a user object*/
 MsnUser *
 msn_user_new(MsnUserList *userlist, const char *passport,
 			 const char *store_name)
@@ -50,6 +51,7 @@
 	return user;
 }
 
+/*destroy a user object*/
 void
 msn_user_destroy(MsnUser *user)
 {
@@ -59,7 +61,14 @@
 		g_hash_table_destroy(user->clientcaps);
 
 	if (user->group_ids != NULL)
+	{
+		GList *l;
+		for (l = user->group_ids; l != NULL; l = l->next)
+		{
+			g_free(l->data);
+		}
 		g_list_free(user->group_ids);
+	}
 
 	if (user->msnobj != NULL)
 		msn_object_destroy(user->msnobj);
@@ -67,6 +76,7 @@
 	g_free(user->passport);
 	g_free(user->friendly_name);
 	g_free(user->store_name);
+	g_free(user->uid);
 	g_free(user->phone.home);
 	g_free(user->phone.work);
 	g_free(user->phone.mobile);
@@ -81,7 +91,18 @@
 
 	account = user->userlist->session->account;
 
-	if (user->status != NULL) {
+	if (user->statusline != NULL && user->currentmedia != NULL) {
+		purple_prpl_got_user_status(account, user->passport, user->status,
+		                          "message", user->statusline,
+		                          "currentmedia", user->currentmedia, NULL);
+	} else if (user->currentmedia != NULL) {
+		purple_prpl_got_user_status(account, user->passport, user->status, "currentmedia",
+		                          user->currentmedia, NULL);
+	} else if (user->statusline != NULL) {
+		//char *status = g_strdup_printf("%s - %s", user->status, user->statusline);
+		purple_prpl_got_user_status(account, user->passport, user->status,
+		                          "message", user->statusline, NULL);
+	} else if (user->status != NULL) {
 		if (!strcmp(user->status, "offline") && user->mobile) {
 			purple_prpl_got_user_status(account, user->passport, "offline", NULL);
 			purple_prpl_got_user_status(account, user->passport, "mobile", NULL);
@@ -142,12 +163,66 @@
 }
 
 void
+msn_user_set_statusline(MsnUser *user, const char *statusline)
+{
+	g_return_if_fail(user != NULL);
+
+	g_free(user->statusline);
+	user->statusline = g_strdup(statusline);
+}
+
+void
+msn_user_set_currentmedia(MsnUser *user, const char *currentmedia)
+{
+	g_return_if_fail(user != NULL);
+
+	g_free(user->currentmedia);
+	user->currentmedia = g_strdup(currentmedia);
+}
+
+void
 msn_user_set_store_name(MsnUser *user, const char *name)
 {
 	g_return_if_fail(user != NULL);
 
-	g_free(user->store_name);
-	user->store_name = g_strdup(name);
+	if (name != NULL)
+	{
+		g_free(user->store_name);
+		user->store_name = g_strdup(name);
+	}
+}
+
+void
+msn_user_set_uid(MsnUser *user, const char *uid)
+{
+	g_return_if_fail(user != NULL);
+
+	g_free(user->uid);
+	user->uid = g_strdup(uid);
+}
+
+void
+msn_user_set_type(MsnUser *user, MsnUserType type)
+{
+	g_return_if_fail(user != NULL);
+
+	user->type = type;
+}
+
+void
+msn_user_set_op(MsnUser *user, int list_op)
+{
+	g_return_if_fail(user != NULL);
+
+	user->list_op |= list_op;
+}
+
+void
+msn_user_unset_op(MsnUser *user, int list_op)
+{
+	g_return_if_fail(user != NULL);
+	
+	user->list_op &= ~list_op;
 }
 
 void
@@ -218,54 +293,97 @@
 	}
 }
 
+/*add group id to User object*/
 void
-msn_user_add_group_id(MsnUser *user, int id)
+msn_user_add_group_id(MsnUser *user, const char* id)
 {
 	MsnUserList *userlist;
 	PurpleAccount *account;
 	PurpleBuddy *b;
 	PurpleGroup *g;
 	const char *passport;
+	char *group_id;
 	const char *group_name;
 
 	g_return_if_fail(user != NULL);
-	g_return_if_fail(id >= 0);
+	g_return_if_fail(id != NULL);
 
-	user->group_ids = g_list_append(user->group_ids, GINT_TO_POINTER(id));
+	group_id = g_strdup(id);
+	user->group_ids = g_list_append(user->group_ids, group_id);
 
 	userlist = user->userlist;
 	account = userlist->session->account;
 	passport = msn_user_get_passport(user);
 
-	group_name = msn_userlist_find_group_name(userlist, id);
+	group_name = msn_userlist_find_group_name(userlist, group_id);
+
+	purple_debug_info("User","group id:%s,name:%s,user:%s\n", group_id, group_name, passport);
 
 	g = purple_find_group(group_name);
 
-	if ((id == 0) && (g == NULL))
+	if ((id == NULL) && (g == NULL))
 	{
 		g = purple_group_new(group_name);
 		purple_blist_add_group(g, NULL);
 	}
 
 	b = purple_find_buddy_in_group(account, passport, g);
-
 	if (b == NULL)
 	{
 		b = purple_buddy_new(account, passport, NULL);
-
 		purple_blist_add_buddy(b, NULL, g, NULL);
 	}
+	b->proto_data = user;
+	/*Update the blist Node info*/
+//	purple_blist_node_set_string(&(b->node), "", "");
+}
 
-	b->proto_data = user;
+/*check if the msn user is online*/
+gboolean
+msn_user_is_online(PurpleAccount *account, const char *name)
+{
+	PurpleBuddy *buddy;
+
+	buddy =purple_find_buddy(account,name);
+	return PURPLE_BUDDY_IS_ONLINE(buddy);
+}
+
+gboolean
+msn_user_is_yahoo(PurpleAccount *account, const char *name)
+{
+	MsnSession *session = NULL;
+	MsnUser *user;
+	PurpleConnection *gc;
+
+	gc = purple_account_get_connection(account);
+	if (gc != NULL)
+		session = gc->proto_data;
+
+	if ((session != NULL) && (session->protocol_ver == WLM_PROT_VER))
+		return FALSE;
+
+	if ((session != NULL) && (user = msn_userlist_find_user(session->userlist, name)) != NULL)
+	{
+		return (user->type == MSN_USER_TYPE_YAHOO);
+	}
+	return (strstr(name,"@yahoo.") != NULL);
 }
 
 void
-msn_user_remove_group_id(MsnUser *user, int id)
+msn_user_remove_group_id(MsnUser *user, const char *id)
 {
+	GList *l;
+
 	g_return_if_fail(user != NULL);
-	g_return_if_fail(id >= 0);
+	g_return_if_fail(id != NULL);
+
+	l = g_list_find_custom(user->group_ids, id, (GCompareFunc)strcmp);
 
-	user->group_ids = g_list_remove(user->group_ids, GINT_TO_POINTER(id));
+	if (l == NULL)
+		return;
+
+	g_free(l->data);
+	user->group_ids = g_list_remove_link(user->group_ids, l);
 }
 
 void
--- a/libpurple/protocols/msn/user.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/user.h	Sun Sep 16 18:06:22 2007 +0000
@@ -31,6 +31,17 @@
 
 #include "userlist.h"
 
+typedef enum
+{
+	MSN_USER_TYPE_UNKNOWN  = 0x00,
+	MSN_USER_TYPE_PASSPORT = 0x01,
+	MSN_USER_TYPE_UNKNOWN1 = 0x02,
+	MSN_USER_TYPE_MOBILE   = 0x04,
+	MSN_USER_TYPE_UNKNOWN2 = 0x08,
+	MSN_USER_TYPE_UNKNOWN3 = 0x10,
+	MSN_USER_TYPE_YAHOO    = 0x20
+} MsnUserType;
+
 /**
  * A user.
  */
@@ -45,7 +56,12 @@
 	char *store_name;       /**< The name stored in the server. */
 	char *friendly_name;    /**< The friendly name.             */
 
+	char * uid;				/*< User Id							*/
+
 	const char *status;     /**< The state of the user.         */
+	char *statusline;       /**< The state of the user.         */	
+	char *currentmedia;     /**< The current media of the user. */
+
 	gboolean idle;          /**< The idle state of the user.    */
 
 	struct
@@ -65,7 +81,12 @@
 
 	GHashTable *clientcaps; /**< The client's capabilities.     */
 
-	int list_op;
+	MsnUserType type;       /**< The user type                  */
+
+	int list_op;            /**< Which lists the user is in     */
+
+	guint membership_id[5];	/**< The membershipId sent by the contacts server,
+				     indexed by the list it belongs to		*/
 };
 
 /**************************************************************************/
@@ -102,6 +123,22 @@
  */
 void msn_user_update(MsnUser *user);
 
+ /**
+  *  Sets the new statusline of user.
+  * 
+  *  @param user The user.
+  *  @param state The statusline string.
+  */
+void msn_user_set_statusline(MsnUser *user, const char *statusline);
+
+ /**
+  *  Sets the current media of user.
+  * 
+  *  @param user The user.
+  *  @param state The statusline string.
+  */
+void msn_user_set_currentmedia(MsnUser *user, const char *currentmedia);
+
 /**
  * Sets the new state of user.
  *
@@ -156,7 +193,7 @@
  * @param user The user.
  * @param id   The group ID.
  */
-void msn_user_add_group_id(MsnUser *user, int id);
+void msn_user_add_group_id(MsnUser *user, const char * id);
 
 /**
  * Removes the group ID from a user.
@@ -164,7 +201,7 @@
  * @param user The user.
  * @param id   The group ID.
  */
-void msn_user_remove_group_id(MsnUser *user, int id);
+void msn_user_remove_group_id(MsnUser *user, const char * id);
 
 /**
  * Sets the home phone number for a user.
@@ -182,6 +219,9 @@
  */
 void msn_user_set_work_phone(MsnUser *user, const char *number);
 
+void msn_user_set_uid(MsnUser *user, const char *uid);
+void msn_user_set_type(MsnUser *user, MsnUserType type);
+
 /**
  * Sets the mobile phone number for a user.
  *
@@ -279,6 +319,22 @@
  */
 GHashTable *msn_user_get_client_caps(const MsnUser *user);
 
+/**
+ * check to see if user is online
+ */
+gboolean
+msn_user_is_online(PurpleAccount *account, const char *name);
+
+/**
+ * check to see if user is Yahoo User
+ */
+gboolean
+msn_user_is_yahoo(PurpleAccount *account ,const char *name);
+
+void msn_user_set_op(MsnUser *user, int list_op);
+void msn_user_unset_op(MsnUser *user, int list_op);
+
 /*@}*/
 
+
 #endif /* _MSN_USER_H_ */
--- a/libpurple/protocols/msn/userlist.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/userlist.c	Sun Sep 16 18:06:22 2007 +0000
@@ -43,8 +43,15 @@
 	MsnPermitAdd *pa = data;
 	MsnSession *session = pa->gc->proto_data;
 	MsnUserList *userlist = session->userlist;
+	MsnUser *user = msn_userlist_find_add_user(userlist, pa->who, pa->who);
+	
+	purple_debug_misc("MSN Userlist", "Accepted the new buddy: %s\n", pa->who);
 
-	msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_AL, NULL);
+	msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL);
+
+	if (msn_userlist_user_is_in_list(user, MSN_LIST_FL)) {
+		msn_del_contact_from_list(session->contact, NULL, pa->who, MSN_LIST_PL);
+	}
 
 	g_free(pa->who);
 	g_free(pa->friendly);
@@ -55,10 +62,20 @@
 msn_cancel_add_cb(gpointer data)
 {
 	MsnPermitAdd *pa = data;
-	MsnSession *session = pa->gc->proto_data;
-	MsnUserList *userlist = session->userlist;
+
+	purple_debug_misc("MSN Userlist", "Deniedthe new buddy: %s\n", pa->who);
 
-	msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_BL, NULL);
+	if (g_list_find(purple_connections_get_all(), pa->gc) != NULL)
+	{
+		MsnSession *session = pa->gc->proto_data;
+		MsnUserList *userlist = session->userlist;
+		MsnCallbackState *state = msn_callback_state_new();
+	
+		msn_callback_state_set_action(state, MSN_DENIED_BUDDY);
+
+		msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_BL);
+		msn_del_contact_from_list(session->contact, state, pa->who, MSN_LIST_PL);
+	}
 
 	g_free(pa->who);
 	g_free(pa->friendly);
@@ -78,47 +95,42 @@
 	purple_account_request_authorization(purple_connection_get_account(gc), passport, NULL, friendly, NULL,
 					   purple_find_buddy(purple_connection_get_account(gc), passport) != NULL,
 					   msn_accept_add_cb, msn_cancel_add_cb, pa);
+
 }
 
 /**************************************************************************
  * Utility functions
  **************************************************************************/
 
-static gboolean
-user_is_in_group(MsnUser *user, int group_id)
+gboolean
+msn_userlist_user_is_in_group(MsnUser *user, const char * group_id)
 {
 	if (user == NULL)
 		return FALSE;
 
-	if (group_id < 0)
+	if (group_id == NULL)
 		return FALSE;
 
-	if (g_list_find(user->group_ids, GINT_TO_POINTER(group_id)))
+	if (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp))
 		return TRUE;
 
 	return FALSE;
 }
 
-static gboolean
-user_is_there(MsnUser *user, int list_id, int group_id)
+gboolean
+msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id)
 {
 	int list_op;
 
 	if (user == NULL)
 		return FALSE;
-
+	
 	list_op = 1 << list_id;
 
-	if (!(user->list_op & list_op))
+	if (user->list_op & list_op)
+		return TRUE;
+	else
 		return FALSE;
-
-	if (list_id == MSN_LIST_FL)
-	{
-		if (group_id >= 0)
-			return user_is_in_group(user, group_id);
-	}
-
-	return TRUE;
 }
 
 static const char*
@@ -149,31 +161,6 @@
 	return store_name;
 }
 
-static void
-msn_request_add_group(MsnUserList *userlist, const char *who,
-					  const char *old_group_name, const char *new_group_name)
-{
-	MsnCmdProc *cmdproc;
-	MsnTransaction *trans;
-	MsnMoveBuddy *data;
-
-	cmdproc = userlist->session->notification->cmdproc;
-	data = g_new0(MsnMoveBuddy, 1);
-
-	data->who = g_strdup(who);
-
-	if (old_group_name)
-		data->old_group_name = g_strdup(old_group_name);
-
-	trans = msn_transaction_new(cmdproc, "ADG", "%s %d",
-								purple_url_encode(new_group_name),
-								0);
-
-	msn_transaction_set_data(trans, data);
-
-	msn_cmdproc_send_trans(cmdproc, trans);
-}
-
 /**************************************************************************
  * Server functions
  **************************************************************************/
@@ -193,14 +180,16 @@
 	return -1;
 }
 
+/* this function msn_got_add_user isn't called anywhere */
 void
 msn_got_add_user(MsnSession *session, MsnUser *user,
-				 MsnListId list_id, int group_id)
+				 MsnListId list_id, const char * group_id)
 {
 	PurpleAccount *account;
 	const char *passport;
 	const char *friendly;
 
+	purple_debug_info("MSNP14","got add user...\n");
 	account = session->account;
 
 	passport = msn_user_get_passport(user);
@@ -214,7 +203,7 @@
 
 		serv_got_alias(gc, passport, friendly);
 
-		if (group_id >= 0)
+		if (group_id != NULL)
 		{
 			msn_user_add_group_id(user, group_id);
 		}
@@ -263,7 +252,7 @@
 			 *       looked at this.  Maybe we should use the store
 			 *       name instead? --KingAnt
 			 */
-			got_new_entry(gc, passport, friendly);
+//			got_new_entry(gc, passport, friendly);
 		}
 	}
 
@@ -273,7 +262,7 @@
 
 void
 msn_got_rem_user(MsnSession *session, MsnUser *user,
-				 MsnListId list_id, int group_id)
+				 MsnListId list_id, const char * group_id)
 {
 	PurpleAccount *account;
 	const char *passport;
@@ -285,7 +274,7 @@
 	if (list_id == MSN_LIST_FL)
 	{
 		/* TODO: When is the user totally removed? */
-		if (group_id >= 0)
+		if (group_id != NULL)
 		{
 			msn_user_remove_group_id(user, group_id);
 			return;
@@ -333,7 +322,6 @@
 	{
 		purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n",
 						passport);
-
 	}
 }
 
@@ -351,14 +339,16 @@
 
 	passport = msn_user_get_passport(user);
 	store = msn_user_get_store_name(user);
+	
+	msn_user_set_op(user, list_op);
 
 	if (list_op & MSN_LIST_FL_OP)
 	{
 		GSList *c;
 		for (c = group_ids; c != NULL; c = g_slist_next(c))
 		{
-			int group_id;
-			group_id = GPOINTER_TO_INT(c->data);
+			char *group_id;
+			group_id = c->data;
 			msn_user_add_group_id(user, group_id);
 		}
 
@@ -393,11 +383,14 @@
 
 		if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
 		{
-			got_new_entry(gc, passport, store);
+//			got_new_entry(gc, passport, store);
 		}
 	}
 
-	user->list_op = list_op;
+	if (list_op & MSN_LIST_PL_OP)
+	{
+		got_new_entry(gc, passport, store);
+	}
 }
 
 /**************************************************************************
@@ -427,18 +420,18 @@
 {
 	GList *l;
 
+	/*destroy userlist*/
 	for (l = userlist->users; l != NULL; l = l->next)
 	{
 		msn_user_destroy(l->data);
 	}
-
 	g_list_free(userlist->users);
 
+	/*destroy group list*/
 	for (l = userlist->groups; l != NULL; l = l->next)
 	{
 		msn_group_destroy(l->data);
 	}
-
 	g_list_free(userlist->groups);
 
 	g_queue_free(userlist->buddy_icon_requests);
@@ -449,6 +442,22 @@
 	g_free(userlist);
 }
 
+MsnUser *
+msn_userlist_find_add_user(MsnUserList *userlist,const char *passport,const char *userName)
+{
+	MsnUser *user;
+
+	user = msn_userlist_find_user(userlist, passport);
+	if (user == NULL)
+	{
+		user = msn_user_new(userlist, passport, userName);
+		msn_userlist_add_user(userlist, user);
+	} else {
+		msn_user_set_store_name(user, userName);
+	}
+	return user;
+}
+
 void
 msn_userlist_add_user(MsnUserList *userlist, MsnUser *user)
 {
@@ -472,10 +481,13 @@
 	{
 		MsnUser *user = (MsnUser *)l->data;
 
+//		purple_debug_info("MsnUserList","user passport:%s,passport:%s\n",user->passport,passport);
 		g_return_val_if_fail(user->passport != NULL, NULL);
 
-		if (!strcmp(passport, user->passport))
+		if (!g_strcasecmp(passport, user->passport)){
+//			purple_debug_info("MsnUserList","return:%p\n",user);
 			return user;
+		}
 	}
 
 	return NULL;
@@ -494,18 +506,18 @@
 }
 
 MsnGroup *
-msn_userlist_find_group_with_id(MsnUserList *userlist, int id)
+msn_userlist_find_group_with_id(MsnUserList *userlist, const char * id)
 {
 	GList *l;
 
 	g_return_val_if_fail(userlist != NULL, NULL);
-	g_return_val_if_fail(id       >= 0,    NULL);
+	g_return_val_if_fail(id       != NULL, NULL);
 
 	for (l = userlist->groups; l != NULL; l = l->next)
 	{
 		MsnGroup *group = l->data;
 
-		if (group->id == id)
+		if (!g_strcasecmp(group->id,id))
 			return group;
 	}
 
@@ -524,14 +536,14 @@
 	{
 		MsnGroup *group = l->data;
 
-		if ((group->name != NULL) && !g_ascii_strcasecmp(name, group->name))
+		if ((group->name != NULL) && !g_strcasecmp(name, group->name))
 			return group;
 	}
 
 	return NULL;
 }
 
-int
+const char *
 msn_userlist_find_group_id(MsnUserList *userlist, const char *group_name)
 {
 	MsnGroup *group;
@@ -541,11 +553,11 @@
 	if (group != NULL)
 		return msn_group_get_id(group);
 	else
-		return -1;
+		return NULL;
 }
 
 const char *
-msn_userlist_find_group_name(MsnUserList *userlist, int group_id)
+msn_userlist_find_group_name(MsnUserList *userlist, const char * group_id)
 {
 	MsnGroup *group;
 
@@ -558,7 +570,7 @@
 }
 
 void
-msn_userlist_rename_group_id(MsnUserList *userlist, int group_id,
+msn_userlist_rename_group_id(MsnUserList *userlist, const char * group_id,
 							 const char *new_name)
 {
 	MsnGroup *group;
@@ -570,7 +582,7 @@
 }
 
 void
-msn_userlist_remove_group_id(MsnUserList *userlist, int group_id)
+msn_userlist_remove_group_id(MsnUserList *userlist, const char * group_id)
 {
 	MsnGroup *group;
 
@@ -584,116 +596,293 @@
 }
 
 void
-msn_userlist_rem_buddy(MsnUserList *userlist,
-					   const char *who, int list_id, const char *group_name)
+msn_userlist_rem_buddy(MsnUserList *userlist, const char *who)
+{
+	MsnUser *user = NULL;
+	
+	g_return_if_fail(userlist != NULL);
+	g_return_if_fail(userlist->session != NULL);
+	g_return_if_fail(userlist->session->contact != NULL);
+	g_return_if_fail(who != NULL);
+	
+	user = msn_userlist_find_user(userlist, who);
+
+	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_FL);
+
+	/* delete the contact from address book via soap action */
+	if (user != NULL) {
+		msn_delete_contact(userlist->session->contact, user->uid);
+	}
+}
+
+void
+msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
+				 MsnListId list_id)
 {
 	MsnUser *user;
-	int group_id;
-	const char *list;
+	const gchar *list;
+	MsnListOp list_op = 1 << list_id;
 
 	user = msn_userlist_find_user(userlist, who);
-	group_id = -1;
-
-	if (group_name != NULL)
-	{
-		group_id = msn_userlist_find_group_id(userlist, group_name);
-
-		if (group_id < 0)
-		{
-			/* Whoa, there is no such group. */
-			purple_debug_error("msn", "Group doesn't exist: %s\n", group_name);
-			return;
-		}
-	}
-
-	/* First we're going to check if not there. */
-	if (!(user_is_there(user, list_id, group_id)))
-	{
+	
+	g_return_if_fail(user != NULL);
+	
+	if ( !msn_userlist_user_is_in_list(user, list_id)) {
 		list = lists[list_id];
-		purple_debug_error("msn", "User '%s' is not there: %s\n",
-						 who, list);
+		purple_debug_info("MSN Userlist", "User %s is not in list %s, not removing.\n", who, list);
 		return;
 	}
 
-	/* Then request the rem to the server. */
-	list = lists[list_id];
+	msn_user_unset_op(user, list_op);
 
-	msn_notification_rem_buddy(userlist->session->notification, list, who, group_id);
+	msn_notification_rem_buddy_from_list(userlist->session->notification, list_id, who);
 }
 
+/*add buddy*/
 void
-msn_userlist_add_buddy(MsnUserList *userlist,
-					   const char *who, int list_id,
-					   const char *group_name)
+msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group_name)
 {
 	MsnUser *user;
-	int group_id;
-	const char *list;
-	const char *store_name;
+	MsnCallbackState *state = NULL;
+	const char *group_id = NULL, *new_group_name;
+	
+	new_group_name = group_name == NULL ? MSN_INDIVIDUALS_GROUP_NAME : group_name;
 
-	group_id = -1;
+	
+	g_return_if_fail(userlist != NULL);
+	g_return_if_fail(userlist->session != NULL);
+
+	
+	purple_debug_info("MSN Userlist", "Add user: %s to group: %s\n", who, new_group_name);
+
+	state = msn_callback_state_new();
+	msn_callback_state_set_who(state, who);
+	msn_callback_state_set_new_group_name(state, new_group_name);
 
 	if (!purple_email_is_valid(who))
 	{
 		/* only notify the user about problems adding to the friends list
 		 * maybe we should do something else for other lists, but it probably
 		 * won't cause too many problems if we just ignore it */
-		if (list_id == MSN_LIST_FL)
-		{
-			char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
-			purple_notify_error(NULL, NULL, str,
-							  _("The screen name specified is invalid."));
-			g_free(str);
-		}
+		
+		char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
+		
+		purple_notify_error(NULL, NULL, str,
+				  _("The screen name specified is invalid."));
+		g_free(str);
 
 		return;
 	}
 
-	if (group_name != NULL)
+	group_id = msn_userlist_find_group_id(userlist, new_group_name);
+
+	if (group_id == NULL)
 	{
-		group_id = msn_userlist_find_group_id(userlist, group_name);
+		/* Whoa, we must add that group first. */
+		purple_debug_info("MSN Userlist", "Adding user %s to a new group, creating group %s first\n", who, new_group_name);
+		
+		msn_callback_state_set_action(state, MSN_ADD_BUDDY);
 
-		if (group_id < 0)
-		{
-			/* Whoa, we must add that group first. */
-			msn_request_add_group(userlist, who, NULL, group_name);
+		msn_add_group(userlist->session, state, new_group_name);
+		return;
+	} else {
+		msn_callback_state_set_guid(state, group_id);
+	}
+	
+	/* XXX: adding user here may not be correct (should add them in the
+ 	 * ACK to the ADL command), but for now we need to make sure they exist  
+	 * early enough that the ILN command doesn't screw us up */
+
+	user = msn_userlist_find_add_user(userlist, who, who);
+
+	if ( msn_userlist_user_is_in_list(user, MSN_LIST_FL) ) {
+
+		purple_debug_info("MSN Userlist", "User %s already exists\n", who);
+
+		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
+
+		if (msn_userlist_user_is_in_group(user, group_id)) {
+			purple_debug_info("MSN Userlist", "User %s is already in group %s, returning\n", who, new_group_name);
 			return;
 		}
 	}
+			
+	purple_debug_info("MSN Userlist", "Adding user: %s to group id: %s\n", who, group_id);
 
-	user = msn_userlist_find_user(userlist, who);
+	msn_callback_state_set_action(state, MSN_ADD_BUDDY);
+
+	/* Add contact in the Contact server with a SOAP request and if
+	   successful, send ADL with MSN_LIST_AL and MSN_LIST_FL and a FQY */
+	msn_add_contact_to_group(userlist->session->contact, state, who, group_id);
+}
 
+void
+msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who, 
+							MsnListId list_id)
+{
+	MsnUser *user = NULL;
+	const gchar *list;
+	MsnListOp list_op = 1 << list_id;
+
+	g_return_if_fail(userlist != NULL);
+	
+	user = msn_userlist_find_add_user(userlist, who, who);
+	
 	/* First we're going to check if it's already there. */
-	if (user_is_there(user, list_id, group_id))
+	if (msn_userlist_user_is_in_list(user, list_id))
 	{
 		list = lists[list_id];
-		purple_debug_error("msn", "User '%s' is already there: %s\n", who, list);
+		purple_debug_info("MSN Userlist", "User '%s' is already in list: %s\n", who, list);
 		return;
 	}
+	
+	//store_name = (user != NULL) ? get_store_name(user) : who;
+	
+	//purple_debug_info("MSN Userlist", "store_name = %s\n", store_name);
+	
+	/* XXX: see XXX above, this should really be done when we get the response from
+		the server */
+	
+	msn_user_set_op(user, list_op);
+	
+	msn_notification_add_buddy_to_list(userlist->session->notification, list_id, who);
+}
+
+gboolean
+msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who,
+				const char *group_name)
+{
+	MsnUser *user;
+	gchar * group_id;
+	
+	g_return_val_if_fail(userlist != NULL, FALSE);
+	g_return_val_if_fail(group_name != NULL, FALSE);
+	g_return_val_if_fail(who != NULL, FALSE);
+
+	purple_debug_info("MSN Userlist","Adding buddy with passport %s to group %s\n", who, group_name);
+
+	if ( (group_id = (gchar *)msn_userlist_find_group_id(userlist, group_name)) == NULL) {
+		purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name);
+		return FALSE;
+	}
 
-	store_name = (user != NULL) ? get_store_name(user) : who;
+	if ( (user = msn_userlist_find_user(userlist, who)) == NULL) {
+		purple_debug_error("MSN Userlist", "User %s not found!", who);
+		return FALSE;
+	}
+	
+	msn_user_add_group_id(user, group_id);
+
+	return TRUE;
+}
+
+
+gboolean
+msn_userlist_rem_buddy_from_group(MsnUserList *userlist, const char *who,
+				const char *group_name)
+{
+	const gchar * group_id;
+	MsnUser *user;
 
-	/* Then request the add to the server. */
-	list = lists[list_id];
+	g_return_val_if_fail(userlist != NULL, FALSE);
+	g_return_val_if_fail(group_name != NULL, FALSE);
+	g_return_val_if_fail(who != NULL, FALSE);
+	
+	purple_debug_info("MSN Userlist","Removing buddy with passport %s from group %s\n", who, group_name);
 
-	msn_notification_add_buddy(userlist->session->notification, list, who,
-							   store_name, group_id);
+	if ( (group_id = msn_userlist_find_group_id(userlist, group_name)) == NULL) {
+		purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name);
+		return FALSE;
+	}
+
+	if ( (user = msn_userlist_find_user(userlist, who)) == NULL) {
+		purple_debug_error("MSN Userlist", "User %s not found!", who);
+		return FALSE;
+	}
+
+	msn_user_remove_group_id(user, group_id);
+
+	return TRUE;
 }
 
 void
 msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
-						const char *old_group_name, const char *new_group_name)
+			const char *old_group_name, const char *new_group_name)
 {
-	int new_group_id;
+	const char *new_group_id;
+	MsnCallbackState *state;
+	
+	g_return_if_fail(userlist != NULL);
+	g_return_if_fail(userlist->session != NULL);
+	g_return_if_fail(userlist->session->contact != NULL);
+
+	state = msn_callback_state_new();
+	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);
+	msn_callback_state_set_new_group_name(state, new_group_name);
 
 	new_group_id = msn_userlist_find_group_id(userlist, new_group_name);
 
-	if (new_group_id < 0)
-	{
-		msn_request_add_group(userlist, who, old_group_name, new_group_name);
+	if (new_group_id == NULL)
+	{		
+		msn_add_group(userlist->session, state, new_group_name);
 		return;
 	}
+	
+	/* add the contact to the new group, and remove it from the old one in
+	 * the callback
+	*/
+	msn_add_contact_to_group(userlist->session->contact, state, who, new_group_id);
+}
 
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_FL, new_group_name);
-	msn_userlist_rem_buddy(userlist, who, MSN_LIST_FL, old_group_name);
+/*load userlist from the Blist file cache*/
+void
+msn_userlist_load(MsnSession *session)
+{
+	PurpleBlistNode *gnode, *cnode, *bnode;
+	PurpleConnection *gc = purple_account_get_connection(session->account);
+	GSList *l;
+	MsnUser * user;
+
+	g_return_if_fail(gc != NULL);
+
+	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next)
+	{
+		if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+			continue;
+		for (cnode = gnode->child; cnode; cnode = cnode->next)
+		{
+			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+				continue;
+			for (bnode = cnode->child; bnode; bnode = bnode->next)
+			{
+				PurpleBuddy *b;
+				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+					continue;
+				b = (PurpleBuddy *)bnode;
+				if (b->account == gc->account)
+				{
+					user = msn_userlist_find_add_user(session->userlist,
+						b->name,NULL);
+					b->proto_data = user;
+					msn_user_set_op(user, MSN_LIST_FL_OP);
+				}
+			}
+		}
+	}
+	for (l = session->account->permit; l != NULL; l = l->next)
+	{
+		user = msn_userlist_find_add_user(session->userlist,
+						(char *)l->data,NULL);
+		msn_user_set_op(user, MSN_LIST_AL_OP);
+	}
+	for (l = session->account->deny; l != NULL; l = l->next)
+	{
+		user = msn_userlist_find_add_user(session->userlist,
+						(char *)l->data,NULL);
+		msn_user_set_op(user, MSN_LIST_BL_OP);
+	}
+	
 }
+
--- a/libpurple/protocols/msn/userlist.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/protocols/msn/userlist.h	Sun Sep 16 18:06:22 2007 +0000
@@ -35,16 +35,11 @@
 	MSN_LIST_FL,
 	MSN_LIST_AL,
 	MSN_LIST_BL,
-	MSN_LIST_RL
+	MSN_LIST_RL,
+	MSN_LIST_PL
 
 } MsnListId;
 
-typedef struct
-{
-	char *who;
-	char *old_group_name;
-
-} MsnMoveBuddy;
 
 struct _MsnUserList
 {
@@ -64,40 +59,56 @@
 
 };
 
+gboolean msn_userlist_user_is_in_group(MsnUser *user, const char * group_id);
+gboolean msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id);
 MsnListId msn_get_list_id(const char *list);
 
 void msn_got_add_user(MsnSession *session, MsnUser *user,
-					  MsnListId list_id, int group_id);
+					  MsnListId list_id, const char *group_id);
 void msn_got_rem_user(MsnSession *session, MsnUser *user,
-					  MsnListId list_id, int group_id);
+					  MsnListId list_id, const char *group_id);
 void msn_got_lst_user(MsnSession *session, MsnUser *user,
 					  int list_op, GSList *group_ids);
 
 MsnUserList *msn_userlist_new(MsnSession *session);
 void msn_userlist_destroy(MsnUserList *userlist);
+
 void msn_userlist_add_user(MsnUserList *userlist, MsnUser *user);
 void msn_userlist_remove_user(MsnUserList *userlist, MsnUser *user);
-MsnUser *msn_userlist_find_user(MsnUserList *userlist,
-								const char *passport);
+
+MsnUser * msn_userlist_find_user(MsnUserList *userlist, const char *passport);
+MsnUser * msn_userlist_find_add_user(MsnUserList *userlist,
+				const char *passport, const char *userName);
+
 void msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group);
 void msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group);
-MsnGroup *msn_userlist_find_group_with_id(MsnUserList *userlist, int id);
-MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist,
-											const char *name);
-int msn_userlist_find_group_id(MsnUserList *userlist,
-							   const char *group_name);
-const char *msn_userlist_find_group_name(MsnUserList *userlist,
-										 int group_id);
-void msn_userlist_rename_group_id(MsnUserList *userlist, int group_id,
-								  const char *new_name);
-void msn_userlist_remove_group_id(MsnUserList *userlist, int group_id);
+MsnGroup *msn_userlist_find_group_with_id(MsnUserList *userlist, const char *id);
+MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist, const char *name);
+const char * msn_userlist_find_group_id(MsnUserList *userlist,
+					const char *group_name);
+const char *msn_userlist_find_group_name(MsnUserList *userlist, const char *group_id);
+void msn_userlist_rename_group_id(MsnUserList *userlist, const char *group_id,
+				  const char *new_name);
+void msn_userlist_remove_group_id(MsnUserList *userlist, const char *group_id);
 
-void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who,
-							int list_id, const char *group_name);
-void msn_userlist_add_buddy(MsnUserList *userlist, const char *who,
-							int list_id, const char *group_name);
+void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who);
+void msn_userlist_add_buddy(MsnUserList *userlist, 
+			    const char *who, const char *group_name);
 void msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
-							 const char *old_group_name,
-							 const char *new_group_name);
+						    const char *old_group_name,
+						    const char *new_group_name);
+
+gboolean msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who,
+					 const char *group_name);
+gboolean msn_userlist_rem_buddy_from_group(MsnUserList *userlist,
+					   const char *who,
+					   const char *group_name);
+
+void msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who, 
+				    MsnListId list_id);
+void msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
+				      MsnListId list_id);
+
+void msn_userlist_load(MsnSession *session);
 
 #endif /* _MSN_USERLIST_H_ */
--- a/libpurple/util.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/util.c	Sun Sep 16 18:06:22 2007 +0000
@@ -514,23 +514,6 @@
  * Date/Time Functions
  **************************************************************************/
 
-#ifdef _WIN32
-static long win32_get_tz_offset() {
-	TIME_ZONE_INFORMATION tzi;
-	DWORD ret;
-	long off = -1;
-
-	if ((ret = GetTimeZoneInformation(&tzi)) != TIME_ZONE_ID_INVALID)
-	{
-		off = -(tzi.Bias * 60);
-		if (ret == TIME_ZONE_ID_DAYLIGHT)
-			off -= tzi.DaylightBias * 60;
-	}
-
-	return off;
-}
-#endif
-
 const char *purple_get_tzoff_str(const struct tm *tm, gboolean iso)
 {
 	static char buf[7];
@@ -545,7 +528,7 @@
 		g_return_val_if_reached("");
 
 #ifdef _WIN32
-	if ((off = win32_get_tz_offset()) == -1)
+	if ((off = wpurple_get_tz_offset()) == -1)
 		return "";
 #else
 # ifdef HAVE_TM_GMTOFF
@@ -853,7 +836,7 @@
 #endif
 
 #ifdef _WIN32
-				if ((sys_tzoff = win32_get_tz_offset()) == -1)
+				if ((sys_tzoff = wpurple_get_tz_offset()) == -1)
 					tzoff = PURPLE_NO_TZ_OFF;
 				else
 					tzoff += sys_tzoff;
--- a/libpurple/win32/win32dep.c	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/win32/win32dep.c	Sun Sep 16 18:06:22 2007 +0000
@@ -586,6 +586,22 @@
 	libpurpledll_hInstance = NULL;
 }
 
+long
+wpurple_get_tz_offset() {
+	TIME_ZONE_INFORMATION tzi;
+	DWORD ret;
+	long off = -1;
+
+	if ((ret = GetTimeZoneInformation(&tzi)) != TIME_ZONE_ID_INVALID)
+	{
+		off = -(tzi.Bias * 60);
+		if (ret == TIME_ZONE_ID_DAYLIGHT)
+			off -= tzi.DaylightBias * 60;
+	}
+
+	return off;
+}
+
 /* DLL initializer */
 /* suppress gcc "no previous prototype" warning */
 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
--- a/libpurple/win32/win32dep.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/win32/win32dep.h	Sun Sep 16 18:06:22 2007 +0000
@@ -60,6 +60,7 @@
 void wpurple_init(void);
 void wpurple_cleanup(void);
 
+long wpurple_get_tz_offset(void);
 
 /*
  *  MACROS
--- a/libpurple/xmlnode.h	Tue Sep 11 13:48:19 2007 +0000
+++ b/libpurple/xmlnode.h	Sun Sep 16 18:06:22 2007 +0000
@@ -128,7 +128,7 @@
  *
  * @param node The node to get data from.
  *
- * @return The data from the node.  This data is in raw escaped format.
+ * @return The data from the node or NULL. This data is in raw escaped format.
  *         You must g_free this string when finished using it.
  */
 char *xmlnode_get_data(xmlnode *node);