changeset 20449:4403cecc7cd6

propagate from branch 'im.pidgin.pidgin' (head 850e223e9597fdc482eebe75c00efda941b3bdb6) to branch 'im.pidgin.cpw.khc.msnp14' (head 92f5b4953a29318b630e01b296e54d1037fe96d3)
author Stu Tomlinson <stu@nosnilmot.com>
date Sun, 03 Jun 2007 00:40:27 +0000
parents 98f27c7f0ef5 (current diff) c1ff701bcb0c (diff)
children bcdeb02d9fcc
files COPYRIGHT libpurple/protocols/msn/msn-utils.c libpurple/protocols/msn/msn-utils.h libpurple/protocols/msn/msn.c libpurple/protocols/msn/msn.h libpurple/protocols/msn/nexus.c libpurple/protocols/msn/notification.c libpurple/protocols/msn/session.c libpurple/protocols/msn/switchboard.c libpurple/protocols/msn/switchboard.h libpurple/protocols/msn/sync.c pidgin/pixmaps/emotes/default/22/theme pidgin/pixmaps/emotes/none/theme
diffstat 44 files changed, 5447 insertions(+), 1099 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Sat Jun 02 19:05:28 2007 +0000
+++ b/COPYRIGHT	Sun Jun 03 00:40:27 2007 +0000
@@ -264,6 +264,7 @@
 Ted Percival
 Eduardo Pérez
 Matt Perry
+Sebastián E. Peyrott
 Celso Pinto
 Joao Luís Marques Pinto
 Aleksander Piotrowski
--- a/libpurple/internal.h	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/internal.h	Sun Jun 03 00:40:27 2007 +0000
@@ -75,6 +75,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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/Makefile.am	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/Makefile.mingw	Sun Jun 03 00:40:27 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 Jun 03 00:40:27 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/command.c	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/command.c	Sun Jun 03 00:40:27 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("MaYuan","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 Jun 03 00:40:27 2007 +0000
@@ -0,0 +1,911 @@
+/**
+ * @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 "soap.h"
+#include "contact.h"
+#include "xmlnode.h"
+#include "group.h"
+
+/*define This to debug the Contact Server*/
+#undef  MSN_CONTACT_SOAP_DEBUG
+
+/*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);
+}
+
+/*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,NULL);
+}
+
+/*get MSN member role utility*/
+static int
+msn_get_memberrole(char * role)
+{
+	if(!strcmp(role,"Allow")){
+		return MSN_LIST_AL_OP;
+	}else if(!strcmp(role,"Block")){
+		return MSN_LIST_BL_OP;
+	}else if(!strcmp(role,"Reverse")){
+		return MSN_LIST_RL_OP;
+	}else if(!strcmp(role,"Pending")){
+		return MSN_LIST_PL_OP;
+	}
+	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;
+}
+
+/*parse contact list*/
+static void
+msn_parse_contact_list(MsnContact * contact)
+{
+	MsnSession * session;
+	int list_op =0;
+	char * passport;
+	xmlnode * node,*body,*response,*result,*services;
+	xmlnode *service,*memberships;
+	xmlnode *LastChangeNode;
+	xmlnode *membershipnode,*members,*member,*passportNode;
+	char *LastChangeStr;
+
+	session = contact->session;
+	purple_debug_misc("MSNCL","parse contact list:{%s}\nsize:%d\n",contact->soapconn->body,contact->soapconn->body_len);
+	node = 	xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len);
+
+	if(node == NULL){
+		purple_debug_misc("MSNCL","parse contact from str err!\n");
+		return;
+	}
+	purple_debug_misc("MSNCL","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("MSNCL","body{%p},name:%s\n",body,body->name);
+	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>
+		 */
+		msn_get_contact_list(contact, NULL);
+		return;
+	}
+
+	purple_debug_misc("MSNCL","response{%p},name:%s\n",response,response->name);
+	result =xmlnode_get_child(response,"FindMembershipResult");
+	if(result == NULL){
+		purple_debug_misc("MSNCL","receive No Update!\n");
+		return;
+	}
+	purple_debug_misc("MSNCL","result{%p},name:%s\n",result,result->name);
+	services =xmlnode_get_child(result,"Services");
+	purple_debug_misc("MSNCL","services{%p},name:%s\n",services,services->name);
+	service =xmlnode_get_child(services,"Service");
+	purple_debug_misc("MSNCL","service{%p},name:%s\n",service,service->name);
+	
+	/*Last Change Node*/
+	LastChangeNode = xmlnode_get_child(service,"LastChange");
+	LastChangeStr = xmlnode_get_data(LastChangeNode);
+	purple_debug_misc("MSNCL","LastChangeNode0 %s\n",LastChangeStr);	
+	purple_account_set_string(session->account, "CLLastChange",LastChangeStr);
+	purple_debug_misc("MSNCL","LastChangeNode %s\n",LastChangeStr);
+	
+	memberships =xmlnode_get_child(service,"Memberships");
+	if (memberships == NULL) {
+		xmlnode_free(node);
+		return;
+	}
+	purple_debug_misc("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_op = msn_get_memberrole(role);
+		purple_debug_misc("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;
+			xmlnode * typeNode;
+			char * type;
+
+			purple_debug_misc("MSNCL","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_misc("MSNCL","Passport name:%s,type:%s\n",passport,type);
+				g_free(type);
+
+				user = msn_userlist_find_add_user(session->userlist,passport,NULL);
+				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);
+				msn_got_lst_user(session,user,list_op,NULL);
+				g_free(passport);
+			}
+		}
+	}
+
+	xmlnode_free(node);
+}
+
+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;
+
+	contact = soapconn->parent;
+	g_return_if_fail(contact != NULL);
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+#ifdef  MSN_CONTACT_SOAP_DEBUG
+	purple_debug_misc("msn", "soap contact server Reply: {%s}\n", soapconn->read_buf);
+#endif
+	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);
+
+#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, abLastChange, dynamicItemLastChange);
+#else
+	msn_get_address_book(contact, NULL, NULL);
+#endif
+}
+
+static void
+msn_get_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","finish contact written\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 char *update_time)
+{
+	MsnSoapReq *soap_request;
+	char *body = NULL;
+	char * update_str;
+	
+	purple_debug_info("MaYuan","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, update_str);
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_GET_CONTACT_POST_URL,MSN_GET_CONTACT_SOAP_ACTION,
+					body,
+					msn_get_contact_list_cb,
+					msn_get_contact_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	g_free(update_str);
+	g_free(body);
+}
+
+static void
+msn_parse_addressbook_groups(MsnContact *contact, xmlnode *node)
+{
+	MsnSession *session = contact->session;
+	xmlnode *group;
+
+	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 */
+			continue;
+		}
+
+		purple_debug_misc("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,*Name,*uid,*type;
+
+		passport = 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);
+			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*/
+				continue;
+			}
+			for(contactEmailNode = xmlnode_get_child(emailsNode,"ContactEmail");contactEmailNode;
+					contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){
+				messengerEnabledNode = xmlnode_get_child(contactEmailNode,"isMessengerEnabled");
+				if(messengerEnabledNode == NULL){
+					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;
+					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){
+			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;
+
+	session = contact->session;
+	purple_debug_misc("xml","parse addressbook:{%s}\nsize:%d\n",contact->soapconn->body,contact->soapconn->body_len);
+	node = xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len);
+
+	if(node == NULL){
+		purple_debug_misc("xml","parse from str err!\n");
+		return FALSE;
+	}
+	purple_debug_misc("xml","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("xml","body{%p},name:%s\n",body,body->name);
+	response = xmlnode_get_child(body,"ABFindAllResponse");
+
+	if (response == NULL) {
+		return FALSE;
+	}
+
+	purple_debug_misc("xml","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");
+		return TRUE;
+	}
+	purple_debug_misc("xml","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);
+	}
+
+	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;
+
+	contact = soapconn->parent;
+	g_return_if_fail(contact != NULL);
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+//	purple_debug_misc("msn", "soap contact server Reply: {%s}\n", soapconn->read_buf);
+	if (msn_parse_addressbook(contact)) {
+		msn_notification_dump_contact(session);
+		msn_set_psm(session);
+		msn_session_finish_login(session);
+	} else {
+		msn_get_address_book(contact, NULL, NULL);
+	}
+
+	/*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_info("MaYuan","finish contact written\n");
+	soapconn->read_cb = msn_get_address_cb;
+}
+
+/*get the address book*/
+void
+msn_get_address_book(MsnContact *contact, const char *LastChanged, const char *dynamicItemLastChange)
+{
+	MsnSoapReq *soap_request;
+	char *body = NULL;
+	char *ab_update_str,*update_str;
+
+	purple_debug_info("MaYuan","msn_get_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,update_str);
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_ADDRESS_BOOK_POST_URL,MSN_GET_ADDRESS_SOAP_ACTION,
+					body,
+					msn_get_address_cb,
+					msn_address_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	g_free(update_str);
+	g_free(body);
+}
+
+static void
+msn_add_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	purple_debug_info("MaYuan","add contact read done\n");
+}
+
+static void
+msn_add_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","finish add contact  written\n");
+	soapconn->read_cb = msn_add_contact_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+/*add a Contact */
+void
+msn_add_contact(MsnContact *contact,const char *passport,const char *groupId)
+{
+	MsnSoapReq *soap_request;
+	char *body = NULL;
+	char *contact_xml = NULL;
+	char *soap_action;
+
+	purple_debug_info("MaYuan","msn add a contact...\n");
+	contact_xml = g_strdup_printf(MSN_CONTACT_XML,passport);
+	if(groupId == NULL){
+		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);
+	}else{
+		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,
+					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_delete_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	purple_debug_info("MaYuan","delete contact read done\n");
+}
+
+static void
+msn_delete_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","delete contact written\n");
+	soapconn->read_cb = msn_delete_contact_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+/*delete a Contact*/
+void
+msn_delete_contact(MsnContact *contact,const char *contactId)
+{	
+	char *body = NULL;
+	char *contact_xml = NULL ;
+	MsnSoapReq *soap_request;
+
+	g_return_if_fail(contactId != NULL);
+	purple_debug_info("MaYuan","msn delete a contact,contactId:{%s}...\n",contactId);
+	contact_xml = g_strdup_printf(MSN_CONTACTS_DEL_XML,contactId);
+	body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE,contact_xml);
+	g_free(contact_xml);
+	/*build SOAP and POST it*/
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_ADDRESS_BOOK_POST_URL,MSN_CONTACT_DEL_SOAP_ACTION,
+					body,
+					msn_delete_contact_read_cb,
+					msn_delete_contact_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+	g_free(body);
+}
+
+#if 0
+static void
+msn_update_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	purple_debug_info("MaYuan","update contact read done\n");
+}
+
+static void
+msn_update_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","update contact written\n");
+	soapconn->read_cb = msn_update_contact_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+/*update a contact's Nickname*/
+void
+msn_update_contact(MsnContact *contact,const char* nickname)
+{
+	MsnSoapReq *soap_request;
+	char *body = NULL;
+
+	purple_debug_info("MaYuan","msn unblock a contact...\n");
+
+	body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE,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,
+					msn_update_contact_read_cb,
+					msn_update_contact_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+	g_free(body);
+}
+#endif
+
+static void
+msn_block_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	purple_debug_info("MaYuan","block read done\n");
+}
+
+static void
+msn_block_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","finish unblock written\n");
+	soapconn->read_cb = msn_block_read_cb;
+}
+
+/*block a Contact*/
+void
+msn_block_contact(MsnContact *contact,const char* membership_id)
+{
+	MsnSoapReq *soap_request;
+	char *body = NULL;
+
+	purple_debug_info("MaYuan","msn block a contact...\n");
+	body = g_strdup_printf(MSN_CONTACT_DELECT_FROM_ALLOW_TEMPLATE,membership_id);
+	/*build SOAP and POST it*/
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_SHARE_POST_URL,MSN_CONTACT_BLOCK_SOAP_ACTION,
+					body,
+					msn_block_read_cb,
+					msn_block_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+	g_free(body);
+}
+
+static void
+msn_unblock_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	purple_debug_info("MaYuan","unblock read done\n");
+}
+
+static void
+msn_unblock_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","finish unblock written\n");
+	soapconn->read_cb = msn_unblock_read_cb;
+}
+
+/*unblock a contact*/
+void
+msn_unblock_contact(MsnContact *contact,const char* passport)
+{
+	MsnSoapReq *soap_request;
+	char *body = NULL;
+
+	purple_debug_info("MaYuan","msn unblock a contact...\n");
+
+	body = g_strdup_printf(MSN_UNBLOCK_CONTACT_TEMPLATE,passport);
+	/*build SOAP and POST it*/
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_SHARE_POST_URL,MSN_CONTACT_UNBLOCK_SOAP_ACTION,
+					body,
+					msn_unblock_read_cb,
+					msn_unblock_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("MaYuan","Gleams read done\n");
+}
+
+static void
+msn_gleams_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","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("MaYuan","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,
+					msn_gleams_read_cb,
+					msn_gleams_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+}
+#endif
+
+/***************************************************************
+ * Group Operation
+ ***************************************************************/
+static void
+msn_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	purple_debug_info("MaYuan","Group read \n");
+}
+
+static void
+msn_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","finish Group written\n");
+	soapconn->read_cb = msn_group_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+/*add group*/
+void msn_add_group(MsnSession *session,const char* group_name)
+{
+	MsnSoapReq *soap_request;
+	MsnContact *contact ;
+	char *body = NULL;
+
+	g_return_if_fail(session != NULL);
+	contact = session->contact;
+	purple_debug_info("MaYuan","msn add group...\n");
+
+	body = g_strdup_printf(MSN_GROUP_ADD_TEMPLATE,group_name);
+	/*build SOAP and POST it*/
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_ADDRESS_BOOK_POST_URL,MSN_GROUP_ADD_SOAP_ACTION,
+					body,
+					msn_group_read_cb,
+					msn_group_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+}
+
+/*delete a group*/
+void msn_del_group(MsnSession *session,const char *guid)
+{
+	MsnSoapReq *soap_request;
+	MsnContact *contact;
+	char *body = NULL;
+
+	g_return_if_fail(session != NULL);
+	/*if group uid we need to del is NULL, 
+	 * we need to delete nothing
+	 */
+	g_return_if_fail(guid != NULL);
+	contact = session->contact;
+	purple_debug_info("MaYuan","msn del group...\n");
+
+	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,
+					msn_group_read_cb,
+					msn_group_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+	g_free(body);
+}
+
+void
+msn_contact_connect_init(MsnSoapConn *soapconn)
+{
+	/*  Authenticate via Windows Live ID. */
+	purple_debug_info("MaYuan","msn_contact_connect...\n");
+
+	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 Jun 03 00:40:27 2007 +0000
@@ -0,0 +1,236 @@
+/**
+ * @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 soap request template*/
+#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\">Initial</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"
+/*get addressbook soap request template*/
+#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>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>"\
+			"%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 Add/del SOAP Action
+ *******************************************************/
+/*add conatct soap request*/
+#define MSN_CONTACT_ADD_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactAdd"
+#define MSN_CONTACT_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><contactType>LivePending</contactType><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>"
+
+/*Contact Group Add*/
+#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 contact from contact list soap request template*/
+#define MSN_CONTACT_DEL_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactDelete"
+#define MSN_CONTACTS_DEL_XML		"<Contact><contactId>%s</contactId></Contact>"
+#define MSN_DEL_CONTACT_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts></ABContactDelete></soap:Body></soap:Envelope>"
+
+#define MSN_MEMBER_TEMPLATE		"<Member xsi:type=\"PassportMember\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><Type>Passport</Type><State>Accepted</State><PassportName>%s</PassportName></Member>"
+
+/*******************************************************
+ * Contact Block/Unblock SOAP Action
+ *******************************************************/
+/*block means delete from allow list and add contact to block list*/
+#define MSN_SHARE_POST_URL		"/abservice/SharingService.asmx"
+#define MSN_CONTACT_BLOCK_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/AddMember"
+/*first delete contact from allow list*/
+#define MSN_CONTACT_DELECT_FROM_ALLOW_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>BlockUnblock</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>Allow</MemberRole><Members><Member xsi:type=\"PassportMember\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><Type>Passport</Type><MembershipId>%s</MembershipId><State>Accepted</State></Member></Members></Membership></memberships></DeleteMember></soap:Body></soap:Envelope>"
+
+#define MSN_CONTACT_ADD_TO_BLOCK_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>BlockUnblock</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>Block</MemberRole><Members>%s</Members></Membership></memberships></AddMember></soap:Body></soap:Envelope>"
+
+/*unblock means delete contact from block list*/
+#define MSN_CONTACT_UNBLOCK_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/DeleteMember"
+#define MSN_UNBLOCK_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>BlockUnblock</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>Block</MemberRole><Members>%s</Members></Membership></memberships></DeleteMember></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> "
+
+/*******************************************************
+ * Group SOAP Action
+ *******************************************************/
+/*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>"
+
+
+typedef struct _MsnContact MsnContact;
+
+struct _MsnContact
+{
+	MsnSession *session;
+
+	MsnSoapConn *soapconn;
+};
+
+/************************************************
+ * function prototype
+ ************************************************/
+MsnContact * msn_contact_new(MsnSession *session);
+void msn_contact_destroy(MsnContact *contact);
+
+void msn_contact_connect(MsnContact *contact);
+void msn_get_contact_list(MsnContact * contact, const char *update);
+void msn_get_address_book(MsnContact *contact, const char * update, const char * gupdate);
+
+/*contact SOAP Operation*/
+void msn_add_contact(MsnContact *contact,const char *passport,const char *groupId);
+void msn_delete_contact(MsnContact *contact,const char *contactId);
+
+
+/*group operation*/
+void msn_add_group(MsnSession *session,const char* group_name);
+void msn_del_group(MsnSession *session,const char *guid);
+
+/*contact Block/unblock operation*/
+void msn_block_contact(MsnContact *contact,const char* membership_id);
+void msn_unblock_contact(MsnContact *contact,const char* passport);
+
+void msn_contact_connect_init(MsnSoapConn *soapconn);
+
+#endif/* _MSN_CMDPROC_H_*/
+
--- a/libpurple/protocols/msn/dialog.c	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/dialog.c	Sun Jun 03 00:40:27 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, MSN_LIST_FL, data->group);
+	}
 
 	g_free(data->group);
 	g_free(data->who);
@@ -75,15 +80,15 @@
 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);
+		msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->group);
+	}
 
 	g_free(data->group);
 	g_free(data->who);
--- a/libpurple/protocols/msn/directconn.c	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/directconn.c	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/directconn.h	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/group.c	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/group.h	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/history.c	Sun Jun 03 00:40:27 2007 +0000
@@ -84,3 +84,4 @@
 		msn_transaction_destroy(trans);
 	}
 }
+
--- a/libpurple/protocols/msn/msg.c	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/msg.c	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/msg.h	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 && (!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	Sat Jun 02 19:05:28 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Sun Jun 03 00:40:27 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"
@@ -123,7 +123,7 @@
 	return PURPLE_CMD_RET_OK;
 }
 
-static void
+void
 msn_act_id(PurpleConnection *gc, const char *entry)
 {
 	MsnCmdProc *cmdproc;
@@ -131,6 +131,9 @@
 	PurpleAccount *account;
 	const char *alias;
 
+	char *soapbody;
+	MsnSoapReq *soap_request;
+
 	session = gc->proto_data;
 	cmdproc = session->notification->cmdproc;
 	account = purple_connection_get_account(gc);
@@ -147,9 +150,27 @@
 		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);
+
+	soapbody = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, alias);
+	/*build SOAP and POST it*/
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+										MSN_ADDRESS_BOOK_POST_URL,
+										MSN_CONTACT_UPDATE_SOAP_ACTION,
+										soapbody,
+										NULL,
+										NULL);
+
+	session->contact->soapconn->read_cb = NULL;
+	msn_soap_post(session->contact->soapconn,
+				  soap_request,
+				  msn_contact_connect_init);
+
+	g_free(soapbody);
 }
 
 static void
@@ -432,6 +453,7 @@
 	session = gc->proto_data;
 
 	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
+
 	if (xfer)
 	{
 		slplink = msn_session_get_slplink(session, who);
@@ -483,18 +505,56 @@
 	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, *name, *cmedia;
+	char *psm_str, *tmp2, *text;
 
 	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 (!purple_presence_is_available(presence) && !purple_presence_is_idle(presence)){
+		name = purple_status_get_name(status);
+	}else{
+		name = NULL;
+	}
+
+	if (cmedia != NULL) {
+		if(name) {
+			tmp2 = g_strdup_printf("%s - %s", name, cmedia);
+			text = g_markup_escape_text(tmp2, -1);
+		} else {
+			text = g_markup_escape_text(cmedia, -1);
+		}
+		return text;
+	} else if (msg != NULL) {
+		tmp2 = purple_markup_strip_html(msg);
+		if (name){
+			psm_str = g_strdup_printf("%s - %s", name, tmp2);
+			g_free(tmp2);
+		}else{
+			psm_str = tmp2;
+		}
+		text = g_markup_escape_text(psm_str, -1);
+		g_free(psm_str);
+		return text;
+	} else {
+		if (!purple_presence_is_available(presence) && !purple_presence_is_idle(presence)){
+			psm_str = g_strdup(purple_status_get_name(status));
+			text = g_markup_escape_text(psm_str, -1);
+			g_free(psm_str);
+			return text;
+		}
 	}
 
 	return NULL;
@@ -512,8 +572,24 @@
 	
 	if (purple_presence_is_online(presence))
 	{
+		const char *psm, *currentmedia;
+		char *tmp;
+
+		psm = purple_status_get_attr_string(status, "message");
+		currentmedia = purple_status_get_attr_string(status, "currentmedia");
+
 		purple_notify_user_info_add_pair(user_info, _("Status"),
 									   (purple_presence_is_idle(presence) ? _("Idle") : purple_status_get_name(status)));
+		if (psm) {
+			tmp = g_markup_escape_text(psm, -1);
+			purple_notify_user_info_add_pair(user_info, _("PSM"), 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)
@@ -538,29 +614,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,
@@ -570,11 +665,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;
 }
 
@@ -757,6 +852,7 @@
 	char *msgformat;
 	char *msgtext;
 
+	purple_debug_info("MaYuan","send IM {%s} to %s\n",message,who);
 	account = purple_connection_get_account(gc);
 
 	if (buddy) {
@@ -770,62 +866,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("MaYuan","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("MaYuan","send to Yahoo User\n");
+				uum_send_msg(session,msg);
+			}else{
+				purple_debug_info("MaYuan","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("MaYuan","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;
 }
@@ -964,6 +1089,7 @@
 	userlist = session->userlist;
 	who = msn_normalize(gc->account, buddy->name);
 
+	purple_debug_info("MaYuan","add user:{%s} to group:{%s}\n",who,group->name);
 	if (!session->logged_in)
 	{
 #if 0
@@ -1256,22 +1382,25 @@
 {
 	MsnSession *session;
 	MsnCmdProc *cmdproc;
-	int old_gid;
+	const char *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);
 
+	purple_debug_info("MaYuan","rename group:old{%s},new{%s}",old_name,enc_new_group_name);
 	old_gid = msn_userlist_find_group_id(session->userlist, old_name);
 
-	if (old_gid >= 0)
+	if (old_gid != NULL)
 	{
+		/*find a Group*/
 		msn_cmdproc_send(cmdproc, "REG", "%d %s 0", old_gid,
 						 enc_new_group_name);
 	}
 	else
 	{
+		/*not found*/
 		msn_cmdproc_send(cmdproc, "ADG", "%s 0", enc_new_group_name);
 	}
 }
@@ -1332,14 +1461,21 @@
 {
 	MsnSession *session;
 	MsnCmdProc *cmdproc;
-	int group_id;
+	const char *group_id;
 
 	session = gc->proto_data;
 	cmdproc = session->notification->cmdproc;
 
-	if ((group_id = msn_userlist_find_group_id(session->userlist, group->name)) >= 0)
+	/*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);
+		return ;
+	}
+	group_id = msn_userlist_find_group_id(session->userlist, group->name);
+	if (group_id != NULL)
+	{
+		msn_del_group(session,group_id);
 	}
 }
 
@@ -1390,12 +1526,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;
@@ -1460,7 +1595,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)
@@ -1844,6 +1979,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("Ma Yuan","photo url:{%s}\n",photo_url_text);
 
 	/* Marshall the existing state */
 	info2_data = g_malloc0(sizeof(MsnGetInfoStepTwoData));
@@ -2068,7 +2204,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 */
@@ -2124,10 +2260,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           */
@@ -2152,11 +2289,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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/msn.h	Sun Jun 03 00:40:27 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
 
@@ -85,9 +96,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
 {
@@ -128,4 +141,7 @@
 	 (MSN_CLIENT_ID_RESERVED_2 <<  8) | \
 	 (MSN_CLIENT_ID_CAPABILITIES))
 
+void msn_act_id(PurpleConnection *gc, const char *entry);
+
+
 #endif /* _MSN_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Sun Jun 03 00:40:27 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 && (!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("MaYuan","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("MaYuan","chl output{%s}\n",output);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/msnutils.h	Sun Jun 03 00:40:27 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/nexus.c	Sun Jun 03 00:40:27 2007 +0000
@@ -22,8 +22,13 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 +41,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 +53,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 +119,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 */
+	purple_debug_misc("msn", "TWN Server Reply: {%s}\n", soapconn->read_buf);
+
+	/*reply OK, we should process the SOAP body*/
+	purple_debug_info("MaYuan","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("MaYuan","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}", 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("MaYuan","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("MaYuan","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", "TWN Sending: {%s}\n", request_str);
 
-	purple_debug_misc("msn", "Sending: {%s}\n", buffer);
-
-	g_free(buffer);
 	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 +363,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 +426,13 @@
 }
 
 
+#endif
+
 /**************************************************************************
  * Connect
  **************************************************************************/
 
+#if 0 /* khc */
 static void
 nexus_connect_cb(gpointer data, PurpleSslConnection *gsc,
 				 PurpleInputCondition cond)
@@ -502,10 +461,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("MaYuan","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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/nexus.h	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Sun Jun 03 00:40:27 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,14 @@
 
 static MsnTable *cbs_table;
 
+/****************************************************************************
+ * 	Local Function Prototype
+ ****************************************************************************/
+
+static void msn_notification_fqy_yahoo(MsnSession *session, const char *passport);
+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, int list_op, MsnUserType type);
+
 /**************************************************************************
  * Main
  **************************************************************************/
@@ -102,9 +110,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 +122,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 +167,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 +186,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 +226,6 @@
 	PurpleAccount *account;
 
 	account = cmdproc->session->account;
-
 	msn_cmdproc_send(cmdproc, "USR", "TWN I %s",
 					 purple_account_get_username(account));
 }
@@ -232,14 +243,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 +263,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("MaYuan","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 +341,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 +392,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 +405,12 @@
 static void
 msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
+	purple_debug_info("MaYuan","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 +420,145 @@
 	{
 		g_return_if_fail(cmd->payload_cb != NULL);
 
+		purple_debug_info("MaYuan","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("MaYuan","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("MaYuan","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("MaYuan","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("MaYuan","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("MaYuan","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 +566,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("MaYuan","<<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 +583,219 @@
 /**************************************************************************
  * Buddy Lists
  **************************************************************************/
+/* add contact to xmlnode */
+static void
+msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport,int list_op, MsnUserType type)
+{
+	xmlnode *d_node,*c_node;
+	char **tokens;
+	char *email,*domain;
+	char *list_op_str,*type_str;
+
+	purple_debug_info("MaYuan","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("MaYuan","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("MaYuan","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("MaYuan","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("MaYuan","Send ADL{%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");
+			}
+		}
+	}
+
+
+	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*/
+static void
+msn_notification_fqy_yahoo(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)
+{
+	purple_debug_info("MaYuan","Process BLP\n");
+}
+
+static void
+adl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MaYuan","Process ADL\n");
+}
+
+static void
+adl_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
 {
 	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]);
+	PurpleAccount *account;
+	PurpleConnection *gc;
+	char *reason = NULL;
 
 	session = cmdproc->session;
+	account = session->account;
+	gc = purple_account_get_connection(account);
 
-	user = msn_userlist_find_user(session->userlist, passport);
+	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);
+}
+
+static void
+fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+			 size_t len)
+{
+	purple_debug_info("MaYuan","FQY payload{%s}\n",payload);
+	msn_notification_post_adl(cmdproc,payload,len);
+}
 
-	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
+fqy_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MaYuan","Process FQY\n");
+	cmdproc->last_cmd->payload_cb  = fqy_cmd_post;
+}
 
-	list_id = msn_get_list_id(list);
+static void
+rml_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+#if 0
+	MsnTransaction *trans;
+	char * payload;
+#endif
 
-	if (cmd->param_count >= 6)
-		group_id = atoi(cmd->params[5]);
-	else
-		group_id = -1;
+	purple_debug_info("MaYuan","Process RML\n");
+#if 0
+	trans = msn_transaction_new(cmdproc, "RML","");
 
-	msn_got_add_user(session, user, list_id, group_id);
-	msn_user_update(user);
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+
+	msn_cmdproc_send_trans(cmdproc, trans);
+#endif
 }
 
 static void
@@ -567,7 +890,7 @@
 
 	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 */
 	if (cmd->trans->data)
@@ -631,6 +954,7 @@
 	PurpleConnection *gc;
 	MsnUser *user;
 	MsnObject *msnobj;
+	int wlmclient;
 	const char *state, *passport, *friendly;
 
 	session = cmdproc->session;
@@ -639,7 +963,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);
 
@@ -647,9 +973,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);
 	}
 
@@ -681,6 +1007,7 @@
 	MsnUser *user;
 	MsnObject *msnobj;
 	int clientid;
+	int wlmclient;
 	const char *state, *passport, *friendly, *old_friendly;
 
 	session = cmdproc->session;
@@ -689,7 +1016,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);
 
@@ -702,10 +1030,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
@@ -714,7 +1041,7 @@
 		}
 	}
 
-	clientid = atoi(cmd->params[3]);
+	clientid = atoi(cmd->params[4]);
 	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE);
 
 	msn_user_set_state(user, state);
@@ -810,6 +1137,12 @@
 			msn_user_set_work_phone(session->user, NULL);
 		else if (!strcmp(type, "PHM"))
 			msn_user_set_mobile_phone(session->user, NULL);
+		else if (!strcmp(type, "MFM")) {
+			type = cmd->params[1];
+			purple_connection_set_display_name(
+				purple_account_get_connection(session->account),
+				purple_url_decode(cmd->params[2]));
+		}
 	}
 }
 
@@ -817,11 +1150,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);
@@ -830,12 +1162,12 @@
 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);
 
@@ -847,10 +1179,8 @@
 {
 	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];
@@ -862,9 +1192,9 @@
 	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);
@@ -874,10 +1204,10 @@
 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);
 }
@@ -885,12 +1215,12 @@
 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);
 
@@ -1135,6 +1465,86 @@
 	g_free(host);
 }
 
+static void
+gcf_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+			 size_t len)
+{
+	/*get the payload content*/
+	purple_debug_info("MaYuan","GCF{%s}\n",cmd->payload);
+}
+
+static void
+gcf_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MaYuan","Processing GCF... \n");
+	cmdproc->last_cmd->payload_cb  = gcf_cmd_post;
+	return;
+}
+
+static void
+sbs_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MaYuan","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;
+
+	/*get the payload content*/
+//	purple_debug_info("MaYuan","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(
+	                                 msn_get_currentmedia(cmd->payload, len));
+
+	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_info("MaYuan","UBX... \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_info("MaYuan","UUX... \n");
+}
+
 /**************************************************************************
  * Message Types
  **************************************************************************/
@@ -1144,7 +1554,9 @@
 {
 	MsnSession *session;
 	const char *value;
+	const char *clLastChange;
 
+	purple_debug_info("MaYuan","profile_msg... \n");
 	session = cmdproc->session;
 
 	if (strcmp(msg->remote_user, "Hotmail"))
@@ -1176,10 +1588,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, clLastChange);
+#else
+	/* always get the full list? */
+	msn_get_contact_list(session->contact, NULL);
+#endif
+#if 0
+	msn_contact_connect(session->contact);
+#endif
 }
 
 static void
@@ -1235,6 +1664,85 @@
 	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)
+{
+}
+
 static void
 email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
@@ -1338,46 +1846,62 @@
 void
 msn_notification_add_buddy(MsnNotification *notification, const char *list,
 						   const char *who, const char *store_name,
-						   int group_id)
+						   const char *group_id)
 {
 	MsnCmdProc *cmdproc;
+	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,1,MSN_USER_TYPE_PASSPORT);
 
-	if (group_id >= 0)
+	payload = xmlnode_to_str(adl_node,&payload_len);
+	xmlnode_free(adl_node);
+	if (msn_user_is_yahoo(notification->session->account,who))
 	{
-		msn_cmdproc_send(cmdproc, "ADD", "%s %s %s %d",
-						 list, who, store_name, group_id);
+		msn_notification_fqy_yahoo(notification->session, who);
 	}
 	else
 	{
-		msn_cmdproc_send(cmdproc, "ADD", "%s %s %s", list, who, store_name);
+		msn_notification_post_adl(notification->servconn->cmdproc,
+							payload,payload_len);
 	}
 }
 
 void
 msn_notification_rem_buddy(MsnNotification *notification, const char *list,
-						   const char *who, int group_id)
+						   const char *who, const char *group_id)
 {
 	MsnCmdProc *cmdproc;
+	MsnTransaction *trans;
+	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,1,MSN_USER_TYPE_PASSPORT);
+
+	payload = xmlnode_to_str(rml_node,&payload_len);
+	xmlnode_free(rml_node);
+
+	purple_debug_info("MaYuan","RML{%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);
 }
 
 /**************************************************************************
  * Init
  **************************************************************************/
-
 void
 msn_notification_init(void)
 {
@@ -1388,18 +1912,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, "ADL", "ILN", iln_cmd);
 	msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd);
 	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, "USR", "GCF", gcf_cmd);
 	msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd);
 	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);
@@ -1408,11 +1932,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);
@@ -1422,11 +1950,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); */
@@ -1435,12 +1967,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);
@@ -1451,3 +1995,4 @@
 {
 	msn_table_destroy(cbs_table);
 }
+
--- a/libpurple/protocols/msn/notification.h	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/notification.h	Sun Jun 03 00:40:27 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,23 @@
 };
 
 #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);
+								const char *store_name, const char *group_id);
 void msn_notification_rem_buddy(MsnNotification *notification,
 								const char *list, const char *who,
-								int group_id);
+								const char *group_id);
 MsnNotification *msn_notification_new(MsnSession *session);
 void msn_notification_destroy(MsnNotification *notification);
 gboolean msn_notification_connect(MsnNotification *notification,
 							  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 Jun 03 00:40:27 2007 +0000
@@ -0,0 +1,632 @@
+/**
+ * @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, char *msg_str);
+static void msn_oim_get_process(MsnOim *oim, 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("MaYuan","encode OIM Message...\n");	
+	oim_base64 = purple_base64_encode((const guchar *)body, strlen(body));
+	purple_debug_info("MaYuan","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, *faultstring;
+
+	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("MaYuan","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("MaYuan","faultcode Node is NULL\n");
+		goto oim_send_process_fail;
+	}
+	faultCodeStr = xmlnode_get_data(faultCodeNode);
+	purple_debug_info("MaYuan","fault code:{%s}\n",faultCodeStr);
+
+	if(!strcmp(faultCodeStr,"q0:AuthenticationFailed")){
+		/*other Fault Reason?*/
+		goto oim_send_process_fail;
+	}
+
+	faultstringNode = xmlnode_get_child(faultNode,"faultstring");
+	faultstring = xmlnode_get_data(faultstringNode);
+	purple_debug_info("MaYuan","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("MaYuan","lockkey:{%s}\n",oim->challenge);
+
+	/*repost the send*/
+	purple_debug_info("MaYuan","prepare to repost the send...\n");
+	msn_oim_send_msg(oim);
+
+oim_send_process_fail:
+	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;
+
+	g_return_if_fail(session != NULL);
+	oim = soapconn->session->oim;
+	g_return_if_fail(oim != NULL);
+
+	purple_debug_info("MaYuan","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("MaYuan","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("MaYuan","no lock key challenge,wait for SOAP Fault and Resend\n");
+		buf[0]='\0';
+	}
+	purple_debug_info("MaYuan","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,
+					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;
+
+	purple_debug_info("MaYuan","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("MaYuan","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,
+					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("MaYuan","oim get SOAP Server connected!\n");
+}
+
+/*Post the Offline Instant Message to User Conversation*/
+static void
+msn_oim_report_to_user(MsnOim *oim, 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("MaYuan","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("MaYuan","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("MaYuan","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("MaYuan","oim Date:{%s},passport{%s}\n",date,passport);
+
+	stamp = purple_str_to_time(date, TRUE, NULL, NULL, NULL);
+
+	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, char *oim_msg)
+{
+	xmlnode *oimNode,*bodyNode,*responseNode,*msgNode;
+	char *msg_data,*msg_str;
+
+	oimNode = xmlnode_from_str(oim_msg, strlen(oim_msg));
+	bodyNode = xmlnode_get_child(oimNode,"Body");
+	responseNode = xmlnode_get_child(bodyNode,"GetMessageResponse");
+	msgNode = xmlnode_get_child(responseNode,"GetMessageResult");
+	msg_data = xmlnode_get_data(msgNode);
+	msg_str = g_strdup(msg_data);
+	g_free(msg_data);
+	purple_debug_info("OIM","msg:{%s}\n",msg_str);
+	msn_oim_report_to_user(oim,msg_str);
+
+	g_free(msg_str);
+}
+
+static void
+msn_oim_get_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnOim * oim = soapconn->session->oim;
+
+	purple_debug_info("MaYuan","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, *mdNode,*mNode,*ENode,*INode,*rtNode,*nNode;
+	char *passport,*msgid,*nickname, *unread, *rTime = NULL;
+	MsnSession *session = oim->session;
+
+	node = xmlnode_from_str(xmlmsg, strlen(xmlmsg));
+
+	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);
+		}
+	}
+
+	mdNode = xmlnode_get_child(node, "MD");
+	if (mdNode == NULL) {
+		xmlnode_free(node);
+		return;
+	}
+	for(mNode = xmlnode_get_child(mdNode, "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("MaYuan","E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime); */
+
+		oim->oim_list = g_list_append(oim->oim_list,msgid);
+		msn_oim_post_single_get_msg(oim,msgid);
+		g_free(passport);
+		g_free(msgid);
+		g_free(rTime);
+		rTime = NULL;
+		g_free(nickname);
+	}
+	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("MaYuan","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,
+					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("MaYuan","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("MaYuan","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 Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/servconn.c	Sun Jun 03 00:40:27 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_msg)
 {
 	MsnServConn *servconn;
 
@@ -242,7 +242,9 @@
 		return TRUE;
 	}
 	else
+	{
 		return FALSE;
+	}
 }
 
 void
@@ -443,6 +445,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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/session.c	Sun Jun 03 00:40:27 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,18 @@
 
 	/* 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;
+		if(!g_strcasecmp(group_name, MSN_INDIVIDUALS_GROUP_NAME)
+						||	!g_strcasecmp(group_name,MSN_NON_IM_GROUP_NAME)){
+			continue;
+		}
 		for(cnode = gnode->child; cnode; cnode = cnode->next) {
 			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
@@ -252,21 +297,26 @@
 
 					if ((remote_user != NULL) && (remote_user->list_op & MSN_LIST_FL_OP))
 					{
-						int group_id;
+						const char *group_id;
 						GList *l;
 
+						purple_debug_info("MaYuan","remote user:{%s}\n",b->name);
 						group_id = msn_userlist_find_group_id(remote_user->userlist,
 								group_name);
+						if(group_id == NULL){
+							continue;
+						}
+						purple_debug_info("MaYuan","group_id:{%s}\n",group_id);
 
 						for (l = remote_user->group_ids; l != NULL; l = l->next)
 						{
-							if (group_id == GPOINTER_TO_INT(l->data))
+							purple_debug_info("MaYuan","l->data:{%s}\n",l->data);
+							if (!g_strcasecmp(group_id ,l->data))
 							{
 								found = TRUE;
 								break;
 							}
 						}
-
 					}
 
 					if (!found)
@@ -411,3 +461,4 @@
 	 */
 	msn_cmdproc_send(session->notification->cmdproc, "URL", "%s", "INBOX");
 }
+
--- a/libpurple/protocols/msn/session.h	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/session.h	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/slp.c	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Sun Jun 03 00:40:27 2007 +0000
@@ -22,6 +22,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/slplink.c	Sun Jun 03 00:40:27 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 Jun 03 00:40:27 2007 +0000
@@ -0,0 +1,634 @@
+/**
+ * @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"
+
+/*define this Macro to debug soap server action*/
+#undef MSN_SOAP_DEBUG
+
+/*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 = -1;
+	soapconn->output_handler = -1;
+
+	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_info("MaYuan","Soap connection connected!\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_info("MaYuan","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_info("MaYuan","msn_soap_init...\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);
+
+	while ((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){
+		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);
+	}
+	/*remove the read handler*/
+	if (soapconn->input_handler > 0){
+		purple_input_remove(soapconn->input_handler);
+	}
+	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];
+
+//	requested_len = (soapconn->need_to_read > 0) ? soapconn->need_to_read : MSN_SOAP_READ_BUFF_SIZE;
+	requested_len = MSN_SOAP_READ_BUFF_SIZE;
+	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){
+		soapconn->read_buf = g_realloc(soapconn->read_buf,
+						soapconn->read_len + len + 1);
+		memcpy(soapconn->read_buf + soapconn->read_len, temp_buf, len);
+		soapconn->read_len += len;
+		soapconn->read_buf[soapconn->read_len] = '\0';
+	}
+#ifdef MSN_SOAP_DEBUG
+	purple_debug_info("MaYuan","++soap ssl read:{%d}\n",len);
+	purple_debug_info("MaYuan","nexus ssl read:{%s}\n",soapconn->read_buf);
+#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;
+
+//	purple_debug_misc("MaYuan", "soap read cb\n");
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+	if (soapconn->input_handler == -1){
+		soapconn->input_handler = purple_input_add(soapconn->gsc->fd,
+			PURPLE_INPUT_READ, msn_soap_read_cb, soapconn);
+	}
+
+	/*read the request header*/
+	len = msn_soap_read(soapconn);
+	if (len < 0 && errno == EAGAIN){
+		return;
+	}else if (len < 0) {
+		purple_debug_error("msn", "read Error!len:%d\n",len);
+		purple_input_remove(soapconn->input_handler);
+		soapconn->input_handler = -1;
+		g_free(soapconn->read_buf);
+		soapconn->read_buf = NULL;
+		soapconn->read_len = 0;
+		/* TODO: error handling */
+		return;
+	}
+
+	if(soapconn->read_buf == NULL){
+		return;
+	}
+
+	if (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL)
+	{
+		/* Redirect. */
+		char *location, *c;
+
+		purple_debug_error("MaYuan", "soap 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("MaYuan", "soap 401\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_SERV_UNAVAILABLE, error);
+	}
+	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);
+#ifdef MSN_SOAP_DEBUG
+			purple_debug_misc("MaYuan","SOAP Read length :%d,body len:%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;
+			}
+			g_free(body_len);
+
+			/*remove the read handler*/
+			purple_input_remove(soapconn->input_handler);
+			soapconn->input_handler = -1;
+			/*
+			 * 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)
+{
+	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)
+{
+	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("MaYuan","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 */
+		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?*/
+	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;
+
+	/*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,
+				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->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)
+{
+	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);
+		}
+	}
+	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_info("Ma Yuan","soap is not connected!\n");
+		msn_soap_init_func(soapconn);
+		msn_soap_connect(soapconn);
+		return;
+	}
+	purple_debug_info("Ma Yuan","soap  connected!\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 0
+	if(soapconn->step == MSN_SOAP_CONNECTED_IDLE){
+		msn_soap_post_head_request(soapconn);
+	}
+#endif
+}
+
+/*Post the soap request action*/
+void
+msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request)
+{
+	char * soap_head = NULL;
+	char * request_str = NULL;
+
+	purple_debug_info("MaYuan","msn_soap_post_request()...\n");
+	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: %d\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);
+	g_free(soap_head);
+
+#ifdef MSN_SOAP_DEBUG
+	purple_debug_info("MaYuan","send to  server{%s}\n",request_str);
+#endif
+
+	/*free read buffer*/
+	msn_soap_free_read_buf(soapconn);
+	/*post it to server*/
+	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 Jun 03 00:40:27 2007 +0000
@@ -0,0 +1,145 @@
+/**
+ * @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
+
+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;
+
+	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;
+
+	/*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,
+								 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/state.c	Sun Jun 03 00:40:27 2007 +0000
@@ -38,6 +38,209 @@
 	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}\0 Song 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);
+	return result;
+}
+
+/* parse CurrentMedia string */
+char *
+msn_parse_currentmedia(const char *cmedia)
+{
+	char **cmedia_array;
+	char *buffer=NULL, *inptr, *outptr, *tmpptr;
+	int length, strings, tmp;
+
+	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=1;	/* Skip first empty string */
+	length=5;	/* Space for '\0' (1 byte) and prefix (4 bytes) */
+	while(strcmp(cmedia_array[strings], "")) {
+		length+= strlen(cmedia_array[strings]);
+		strings++;
+	}
+
+	if((strings>3) && (!strcmp(cmedia_array[2], "1"))) { /* Check if enabled */
+
+		buffer=g_malloc(length);
+
+		inptr=cmedia_array[3];
+		outptr=buffer;
+
+		if(!strcmp(cmedia_array[1], "Music")) {
+			strcpy(outptr, "np. ");
+			outptr+=4;
+		}/* else if(!strcmp(cmedia_array[1], "Games")) {
+		} else if(!strcmp(cmedia_array[1], "Office")) {
+		}*/
+
+		while(*inptr!='\0') {
+			if((*inptr == '{') && (strlen(inptr) > 2) && (*(inptr+2) == '}') ) {
+				errno = 0;
+				tmp = strtol(inptr+1,&tmpptr,10);
+				if( (errno!=0) || (tmpptr == (inptr+1)) ||
+				                  ((tmp+5)>(strings)) ) {
+					*outptr = *inptr;	/* Conversion not successful */
+					outptr++;
+				} else {
+					/* Replace {?} tag with appropriate text */
+					strcpy(outptr, cmedia_array[tmp+4]);
+					outptr+=strlen(cmedia_array[tmp+4]);
+					inptr+=2;
+				}
+			} else {
+				*outptr = *inptr;
+				outptr++;
+			}
+			inptr++;
+		}
+		*outptr='\0';
+		purple_debug_info("msn", "Parsed currentmedia string, result: \"%s\"\n",
+		                buffer);
+	} else {
+		purple_debug_info("msn", "Current media marked disabled, not parsing\n");
+	}
+
+	g_strfreev(cmedia_array);
+	return buffer;
+}
+
+/* get the CurrentMedia info from the XML string */
+char *
+msn_get_currentmedia(char *xml_str, gsize len)
+{
+	xmlnode *payloadNode, *currentmediaNode;
+	char *currentmedia_str, *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");
+		g_free(payloadNode);
+		return NULL;
+	}
+	currentmedia_str = xmlnode_get_data(currentmediaNode);
+	currentmedia = g_strdup(currentmedia_str);
+
+	g_free(currentmediaNode);
+	g_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_str, *psm;
+	
+	purple_debug_info("Ma Yuan","msn get PSM\n");
+	payloadNode = xmlnode_from_str(xml_str, len);
+	if (!payloadNode){
+		purple_debug_error("MaYuan","PSM XML parse Error!\n");
+		return NULL;
+	}
+	psmNode = xmlnode_get_child(payloadNode, "PSM");
+	if (psmNode == NULL){
+		purple_debug_info("Ma Yuan","No PSM status Node");
+		g_free(payloadNode);
+		return NULL;
+	}
+	psm_str = xmlnode_get_data(psmNode);
+	psm = g_strdup(psm_str);
+
+	g_free(psmNode);
+	g_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;
+
+	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");
+	session->psm = msn_build_psm(statusline, NULL, NULL);
+	payload = session->psm;
+
+	purple_debug_info("MaYuan","UUX{%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 +282,7 @@
 
 		g_free(msnobj_str);
 	}
+	msn_set_psm(session);
 }
 
 const char *
--- a/libpurple/protocols/msn/state.h	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/state.h	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Sun Jun 03 00:40:27 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("MaYuan","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("Ma Yuan","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("MaYuan","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;
@@ -962,7 +973,7 @@
 
 	str = g_strdup_printf(_("%s just sent you a Nudge!"), username);
 	g_free(username);
-	msn_switchboard_report_user(swboard, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NOTIFY, str);
+	msn_switchboard_report_user(swboard, PURPLE_MESSAGE_SYSTEM, str);
 	g_free(str);
 }
 
@@ -1093,6 +1104,8 @@
 cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
 {
 	int reason = MSN_SB_ERROR_UNKNOWN;
+	MsnMessage *msg;
+	MsnSwitchBoard *swboard = trans->data;
 
 	if (error == 215)
 	{
@@ -1105,7 +1118,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("Ma Yuan","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);
 }
 
@@ -1149,6 +1174,7 @@
 		/* The conversation window was closed. */
 		return;
 
+	purple_debug_info("MaYuan","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);
@@ -1261,6 +1287,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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/sync.c	Sun Jun 03 00:40:27 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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Sun Jun 03 00:40:27 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,58 @@
 }
 
 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
@@ -218,54 +285,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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/user.h	Sun Jun 03 00:40:27 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,8 @@
 
 	GHashTable *clientcaps; /**< The client's capabilities.     */
 
-	int list_op;
+	MsnUserType type;       /**< The user type                  */
+	int list_op;            /**< Which lists the user is in     */
 };
 
 /**************************************************************************/
@@ -102,6 +119,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 +189,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 +197,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 +215,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 +315,21 @@
  */
 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);
+
 /*@}*/
 
+
 #endif /* _MSN_USER_H_ */
--- a/libpurple/protocols/msn/userlist.c	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/userlist.c	Sun Jun 03 00:40:27 2007 +0000
@@ -53,10 +53,13 @@
 static void
 msn_cancel_add_cb(MsnPermitAdd *pa)
 {
-	MsnSession *session = pa->gc->proto_data;
-	MsnUserList *userlist = session->userlist;
+	if (g_list_find(purple_connections_get_all(), pa->gc) != NULL)
+	{
+		MsnSession *session = pa->gc->proto_data;
+		MsnUserList *userlist = session->userlist;
 
-	msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_BL, NULL);
+		msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_BL, NULL);
+	}
 
 	g_free(pa->who);
 	g_free(pa->friendly);
@@ -76,6 +79,7 @@
 	purple_account_request_authorization(purple_connection_get_account(gc), passport, NULL, friendly, NULL,
 					   purple_find_buddy(purple_connection_get_account(gc), passport) != NULL,
 					   G_CALLBACK(msn_accept_add_cb), G_CALLBACK(msn_cancel_add_cb), pa);
+
 }
 
 /**************************************************************************
@@ -83,22 +87,22 @@
  **************************************************************************/
 
 static gboolean
-user_is_in_group(MsnUser *user, int group_id)
+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)
+user_is_there(MsnUser *user, int list_id, const char * group_id)
 {
 	int list_op;
 
@@ -112,7 +116,7 @@
 
 	if (list_id == MSN_LIST_FL)
 	{
-		if (group_id >= 0)
+		if (group_id != NULL)
 			return user_is_in_group(user, group_id);
 	}
 
@@ -151,25 +155,26 @@
 msn_request_add_group(MsnUserList *userlist, const char *who,
 					  const char *old_group_name, const char *new_group_name)
 {
+	MsnSession *session;
 	MsnCmdProc *cmdproc;
-	MsnTransaction *trans;
 	MsnMoveBuddy *data;
 
-	cmdproc = userlist->session->notification->cmdproc;
+	session = userlist->session;
+	cmdproc = 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);
+		/*delete the old group via SOAP action*/
+		msn_del_group(session,old_group_name);
+	}
 
-	trans = msn_transaction_new(cmdproc, "ADG", "%s %d",
-								purple_url_encode(new_group_name),
-								0);
+	/*add new group via SOAP action*/
+	msn_add_group(session, new_group_name);
 
-	msn_transaction_set_data(trans, data);
-
-	msn_cmdproc_send_trans(cmdproc, trans);
 }
 
 /**************************************************************************
@@ -193,12 +198,13 @@
 
 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("MaYuan","got add user...\n");
 	account = session->account;
 
 	passport = msn_user_get_passport(user);
@@ -212,7 +218,7 @@
 
 		serv_got_alias(gc, passport, friendly);
 
-		if (group_id >= 0)
+		if (group_id != NULL)
 		{
 			msn_user_add_group_id(user, group_id);
 		}
@@ -261,7 +267,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);
 		}
 	}
 
@@ -271,7 +277,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;
@@ -283,7 +289,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;
@@ -331,7 +337,6 @@
 	{
 		purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n",
 						passport);
-
 	}
 }
 
@@ -355,8 +360,8 @@
 		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);
 		}
 
@@ -391,11 +396,11 @@
 
 		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;
+	user->list_op |= list_op;
 }
 
 /**************************************************************************
@@ -425,18 +430,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);
@@ -447,6 +452,21 @@
 	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);
+	}
+	msn_user_set_store_name(user, userName);
+	return user;
+}
+
 void
 msn_userlist_add_user(MsnUserList *userlist, MsnUser *user)
 {
@@ -469,11 +489,13 @@
 	for (l = userlist->users; l != NULL; l = l->next)
 	{
 		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;
@@ -492,18 +514,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;
 	}
 
@@ -522,14 +544,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;
@@ -539,11 +561,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;
 
@@ -556,7 +578,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;
@@ -568,7 +590,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;
 
@@ -586,17 +608,23 @@
 					   const char *who, int list_id, const char *group_name)
 {
 	MsnUser *user;
-	int group_id;
+	const char *group_id;
 	const char *list;
 
 	user = msn_userlist_find_user(userlist, who);
-	group_id = -1;
+
+	g_return_if_fail(user != NULL);
+
+	/*delete the contact from address book via soap action*/
+	msn_delete_contact(userlist->session->contact,user->uid);
+
+	group_id = NULL;
 
 	if (group_name != NULL)
 	{
 		group_id = msn_userlist_find_group_id(userlist, group_name);
 
-		if (group_id < 0)
+		if (group_id == NULL)
 		{
 			/* Whoa, there is no such group. */
 			purple_debug_error("msn", "Group doesn't exist: %s\n", group_name);
@@ -619,17 +647,19 @@
 	msn_notification_rem_buddy(userlist->session->notification, list, who, group_id);
 }
 
+/*add buddy*/
 void
 msn_userlist_add_buddy(MsnUserList *userlist,
 					   const char *who, int list_id,
 					   const char *group_name)
 {
 	MsnUser *user;
-	int group_id;
+	const char *group_id;
 	const char *list;
 	const char *store_name;
 
-	group_id = -1;
+	purple_debug_info("MaYuan", "userlist add buddy,name:{%s},group:{%s}\n",who ,group_name);
+	group_id = NULL;
 
 	if (!purple_email_is_valid(who))
 	{
@@ -651,7 +681,7 @@
 	{
 		group_id = msn_userlist_find_group_id(userlist, group_name);
 
-		if (group_id < 0)
+		if (group_id == NULL)
 		{
 			/* Whoa, we must add that group first. */
 			msn_request_add_group(userlist, who, NULL, group_name);
@@ -659,7 +689,11 @@
 		}
 	}
 
-	user = msn_userlist_find_user(userlist, who);
+	/* XXX: using _add_user here may not be correct (should add them in the
+	   ACK to the ADL command, and we might also want to make sure the user's groups
+	   are correct. 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);
 
 	/* First we're going to check if it's already there. */
 	if (user_is_there(user, list_id, group_id))
@@ -671,9 +705,15 @@
 
 	store_name = (user != NULL) ? get_store_name(user) : who;
 
+	/* XXX: see XXX above, this should really be done when we get the response from
+	   the server */
+	msn_user_set_op(user, list_id);
+
 	/* Then request the add to the server. */
 	list = lists[list_id];
 
+	purple_debug_info("MaYuan", "add user:{%s} to group id {%s}\n",store_name ,group_id);
+	msn_add_contact(userlist->session->contact,who,group_id);
 	msn_notification_add_buddy(userlist->session->notification, list, who,
 							   store_name, group_id);
 }
@@ -682,11 +722,11 @@
 msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
 						const char *old_group_name, const char *new_group_name)
 {
-	int new_group_id;
+	const char *new_group_id;
 
 	new_group_id = msn_userlist_find_group_id(userlist, new_group_name);
 
-	if (new_group_id < 0)
+	if (new_group_id == NULL)
 	{
 		msn_request_add_group(userlist, who, old_group_name, new_group_name);
 		return;
@@ -695,3 +735,54 @@
 	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	Sat Jun 02 19:05:28 2007 +0000
+++ b/libpurple/protocols/msn/userlist.h	Sun Jun 03 00:40:27 2007 +0000
@@ -35,7 +35,8 @@
 	MSN_LIST_FL,
 	MSN_LIST_AL,
 	MSN_LIST_BL,
-	MSN_LIST_RL
+	MSN_LIST_RL,
+	MSN_LIST_PL
 
 } MsnListId;
 
@@ -67,30 +68,34 @@
 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);
+				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_id(MsnUserList *userlist, const char *id);
 MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist,
 											const char *name);
-int msn_userlist_find_group_id(MsnUserList *userlist,
+const char * 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 *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, int group_id);
+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);
@@ -99,5 +104,6 @@
 void msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
 							 const char *old_group_name,
 							 const char *new_group_name);
+void msn_userlist_load(MsnSession *session);
 
 #endif /* _MSN_USERLIST_H_ */