propagate from branch 'im.pidgin.pidgin' (head 0226ffea1a95d5e476f92e311c90add79b369e76) cpw.khc.msnp14

Sat, 07 Jul 2007 03:52:12 +0000

author
Ka-Hing Cheung <khc@pidgin.im>
date
Sat, 07 Jul 2007 03:52:12 +0000
branch
cpw.khc.msnp14
changeset 20552
f7fe5f850aae
parent 18635
0226ffea1a95 (current diff)
parent 20543
9d5453d768ea (diff)
child 20553
3bed65de9e21

propagate from branch 'im.pidgin.pidgin' (head 0226ffea1a95d5e476f92e311c90add79b369e76)
to branch 'im.pidgin.cpw.khc.msnp14' (head 9d5453d768eab8748ed65e1da17de035045ab3d9)

COPYRIGHT file | annotate | diff | comparison | revisions
libpurple/internal.h file | annotate | diff | comparison | revisions
libpurple/protocols/msn/directconn.c file | annotate | diff | comparison | revisions
libpurple/protocols/msn/msn-utils.c file | annotate | diff | comparison | revisions
libpurple/protocols/msn/msn-utils.h file | annotate | diff | comparison | revisions
libpurple/protocols/msn/msnutils.c file | annotate | diff | comparison | revisions
libpurple/protocols/msn/msnutils.h file | annotate | diff | comparison | revisions
pidgin/gtkdocklet.c file | annotate | diff | comparison | revisions
--- a/COPYRIGHT	Sat Jul 07 00:30:45 2007 +0000
+++ b/COPYRIGHT	Sat Jul 07 03:52:12 2007 +0000
@@ -270,6 +270,7 @@
 Eduardo Pérez
 Matt Perry
 Nathan Peterson
+Sebastián E. Peyrott
 Celso Pinto
 Joao Luís Marques Pinto
 Aleksander Piotrowski
--- a/libpurple/internal.h	Sat Jul 07 00:30:45 2007 +0000
+++ b/libpurple/internal.h	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/Makefile.am	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/Makefile.mingw	Sat Jul 07 03:52:12 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	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/command.c	Sat Jul 07 03:52:12 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	Sat Jul 07 03:52:12 2007 +0000
@@ -0,0 +1,912 @@
+/**
+ * @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_send_privacy(session->account->gc);
+		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	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/dialog.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/directconn.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/directconn.h	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/group.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/group.h	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/history.c	Sat Jul 07 03:52:12 2007 +0000
@@ -84,3 +84,4 @@
 		msn_transaction_destroy(trans);
 	}
 }
+
--- a/libpurple/protocols/msn/msg.c	Sat Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/msg.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/msg.h	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 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 && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
-								direction = '1';
-							g_free(attr_dir);
-							g_free(attributes);
-						}
-
-					}
-					else
-					{
-						c++;
-					}
-				}
-				if (*c == '>')
-					c++;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "font", 4))
-			{
-				c += 5;
-
-				while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
-					c++;
-
-				if (!g_ascii_strncasecmp(c, "color=\"#", 7))
-				{
-					c += 8;
-
-					fontcolor[0] = *(c + 4);
-					fontcolor[1] = *(c + 5);
-					fontcolor[2] = *(c + 2);
-					fontcolor[3] = *(c + 3);
-					fontcolor[4] = *c;
-					fontcolor[5] = *(c + 1);
-
-					c += 8;
-				}
-				else if (!g_ascii_strncasecmp(c, "face=\"", 6))
-				{
-					const char *end = NULL;
-					const char *comma = NULL;
-					unsigned int namelen = 0;
-
-					c += 6;
-					end = strchr(c, '\"');
-					comma = strchr(c, ',');
-
-					if (comma == NULL || comma > end)
-						namelen = (unsigned int)(end - c);
-					else
-						namelen = (unsigned int)(comma - c);
-
-					fontface = g_strndup(c, namelen);
-					c = end + 2;
-				}
-				else
-				{
-					/* Drop all unrecognized/misparsed font tags */
-					while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
-						c++;
-
-					if (*c != '\0')
-						c += 2;
-				}
-			}
-			else
-			{
-				while ((*c != '\0') && (*c != '>'))
-					c++;
-				if (*c != '\0')
-					c++;
-			}
-		}
-		else if (*c == '&')
-		{
-			if (!g_ascii_strncasecmp(c, "&lt;", 4))
-			{
-				msg[retcount++] = '<';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&gt;", 4))
-			{
-				msg[retcount++] = '>';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
-			{
-				msg[retcount++] = ' ';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&quot;", 6))
-			{
-				msg[retcount++] = '"';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&amp;", 5))
-			{
-				msg[retcount++] = '&';
-				c += 5;
-			}
-			else if (!g_ascii_strncasecmp(c, "&apos;", 6))
-			{
-				msg[retcount++] = '\'';
-				c += 6;
-			}
-			else
-				msg[retcount++] = *c++;
-		}
-		else
-			msg[retcount++] = *c++;
-	}
-
-	if (fontface == NULL)
-		fontface = g_strdup("MS Sans Serif");
-
-	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
-								  encode_spaces(fontface),
-								  fonteffect, fontcolor, direction);
-	*message = g_strdup(msg);
-
-	g_free(fontface);
-	g_free(msg);
-}
-
-void
-msn_parse_socket(const char *str, char **ret_host, int *ret_port)
-{
-	char *host;
-	char *c;
-	int port;
-
-	host = g_strdup(str);
-
-	if ((c = strchr(host, ':')) != NULL)
-	{
-		*c = '\0';
-		port = atoi(c + 1);
-	}
-	else
-		port = 1863;
-
-	*ret_host = host;
-	*ret_port = port;
-}
--- a/libpurple/protocols/msn/msn-utils.h	Sat Jul 07 00:30:45 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Sat Jul 07 03:52:12 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
@@ -386,6 +407,28 @@
 	return user && user->mobile;
 }
 
+void
+msn_send_privacy(PurpleConnection *gc)
+{
+       PurpleAccount *account;
+        MsnSession *session;
+        MsnCmdProc *cmdproc;
+
+        account = purple_connection_get_account(gc);
+        session = gc->proto_data;
+        cmdproc = session->notification->cmdproc;
+
+        if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
+                account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
+        {
+                msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
+        }
+        else
+        {
+                msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
+        }
+}
+
 static void
 initiate_chat_cb(PurpleBlistNode *node, gpointer data)
 {
@@ -432,6 +475,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 +527,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 +594,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 +636,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 +687,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 +874,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 +888,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 +1111,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
@@ -1102,23 +1250,7 @@
 static void
 msn_set_permit_deny(PurpleConnection *gc)
 {
-	PurpleAccount *account;
-	MsnSession *session;
-	MsnCmdProc *cmdproc;
-
-	account = purple_connection_get_account(gc);
-	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-
-	if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
-		account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
-	{
-		msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
-	}
-	else
-	{
-		msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
-	}
+	msn_send_privacy(gc);
 }
 
 static void
@@ -1256,22 +1388,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 +1467,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 +1532,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 +1601,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 +1985,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 +2210,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 +2266,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 +2295,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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/msn.h	Sat Jul 07 03:52:12 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);
+void msn_send_privacy(PurpleConnection *gc);
+
 #endif /* _MSN_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Sat Jul 07 03:52:12 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 && (!g_ascii_strncasecmp(attr_dir, "RTL", 3)))
+								direction = '1';
+							g_free(attr_dir);
+							g_free(attributes);
+						}
+
+					}
+					else
+					{
+						c++;
+					}
+				}
+				if (*c == '>')
+					c++;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "font", 4))
+			{
+				c += 5;
+
+				while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
+					c++;
+
+				if (!g_ascii_strncasecmp(c, "color=\"#", 7))
+				{
+					c += 8;
+
+					fontcolor[0] = *(c + 4);
+					fontcolor[1] = *(c + 5);
+					fontcolor[2] = *(c + 2);
+					fontcolor[3] = *(c + 3);
+					fontcolor[4] = *c;
+					fontcolor[5] = *(c + 1);
+
+					c += 8;
+				}
+				else if (!g_ascii_strncasecmp(c, "face=\"", 6))
+				{
+					const char *end = NULL;
+					const char *comma = NULL;
+					unsigned int namelen = 0;
+
+					c += 6;
+					end = strchr(c, '\"');
+					comma = strchr(c, ',');
+
+					if (comma == NULL || comma > end)
+						namelen = (unsigned int)(end - c);
+					else
+						namelen = (unsigned int)(comma - c);
+
+					fontface = g_strndup(c, namelen);
+					c = end + 2;
+				}
+				else
+				{
+					/* Drop all unrecognized/misparsed font tags */
+					while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
+						c++;
+
+					if (*c != '\0')
+						c += 2;
+				}
+			}
+			else
+			{
+				while ((*c != '\0') && (*c != '>'))
+					c++;
+				if (*c != '\0')
+					c++;
+			}
+		}
+		else if (*c == '&')
+		{
+			if (!g_ascii_strncasecmp(c, "&lt;", 4))
+			{
+				msg[retcount++] = '<';
+				c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c, "&gt;", 4))
+			{
+				msg[retcount++] = '>';
+				c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
+			{
+				msg[retcount++] = ' ';
+				c += 6;
+			}
+			else if (!g_ascii_strncasecmp(c, "&quot;", 6))
+			{
+				msg[retcount++] = '"';
+				c += 6;
+			}
+			else if (!g_ascii_strncasecmp(c, "&amp;", 5))
+			{
+				msg[retcount++] = '&';
+				c += 5;
+			}
+			else if (!g_ascii_strncasecmp(c, "&apos;", 6))
+			{
+				msg[retcount++] = '\'';
+				c += 6;
+			}
+			else
+				msg[retcount++] = *c++;
+		}
+		else
+			msg[retcount++] = *c++;
+	}
+
+	if (fontface == NULL)
+		fontface = g_strdup("MS Sans Serif");
+
+	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
+								  encode_spaces(fontface),
+								  fonteffect, fontcolor, direction);
+	*message = g_strdup(msg);
+
+	g_free(fontface);
+	g_free(msg);
+}
+
+void
+msn_parse_socket(const char *str, char **ret_host, int *ret_port)
+{
+	char *host;
+	char *c;
+	int port;
+
+	host = g_strdup(str);
+
+	if ((c = strchr(host, ':')) != NULL){
+		*c = '\0';
+		port = atoi(c + 1);
+	}else{
+		port = 1863;
+	}
+
+	*ret_host = host;
+	*ret_port = port;
+}
+/***************************************************************************
+ * MSN Time Related Funciton
+ ***************************************************************************/
+#if 0
+int
+msn_convert_iso8601(const char *timestr,struct tm tm_time)
+{
+	char temp[64];
+	struct tm ctime;
+	time_t ts;
+
+	purple_debug_info("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	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/nexus.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/nexus.h	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Sat Jul 07 03:52:12 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,228 @@
 /**************************************************************************
  * 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)
+{
+	static int initial;
+
+	purple_debug_info("MaYuan","Process ADL\n");
+
+	if (!initial)
+        {
+                purple_debug_info("--[","Initial ADL received\n");
+                msn_set_psm(cmdproc->session);
+                msn_session_finish_login(cmdproc->session);
+        }
+}
+
+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 +899,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)
@@ -642,6 +974,7 @@
 	PurpleConnection *gc;
 	MsnUser *user;
 	MsnObject *msnobj;
+	int wlmclient;
 	const char *state, *passport, *friendly;
 
 	session = cmdproc->session;
@@ -650,7 +983,9 @@
 
 	state    = cmd->params[1];
 	passport = cmd->params[2];
-	friendly = purple_url_decode(cmd->params[3]);
+	/*if a contact is actually on the WLM part or the yahoo part*/
+	wlmclient = atoi(cmd->params[3]);
+	friendly = purple_url_decode(cmd->params[4]);
 
 	user = msn_userlist_find_user(session->userlist, passport);
 
@@ -658,9 +993,9 @@
 
 	msn_user_set_friendly_name(user, friendly);
 
-	if (session->protocol_ver >= 9 && cmd->param_count == 6)
+	if (session->protocol_ver >= 9 && cmd->param_count == 8)
 	{
-		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
+		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6]));
 		msn_user_set_object(user, msnobj);
 	}
 
@@ -692,6 +1027,7 @@
 	MsnUser *user;
 	MsnObject *msnobj;
 	int clientid;
+	int wlmclient;
 	const char *state, *passport, *friendly, *old_friendly;
 
 	session = cmdproc->session;
@@ -700,7 +1036,8 @@
 
 	state    = cmd->params[0];
 	passport = cmd->params[1];
-	friendly = purple_url_decode(cmd->params[2]);
+	wlmclient = atoi(cmd->params[2]);
+	friendly = purple_url_decode(cmd->params[3]);
 
 	user = msn_userlist_find_user(session->userlist, passport);
 
@@ -713,10 +1050,9 @@
 
 	if (session->protocol_ver >= 9)
 	{
-		if (cmd->param_count == 5)
+		if (cmd->param_count == 7)
 		{
-			msnobj =
-				msn_object_new_from_string(purple_url_decode(cmd->params[4]));
+			msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
 			msn_user_set_object(user, msnobj);
 		}
 		else
@@ -725,7 +1061,7 @@
 		}
 	}
 
-	clientid = atoi(cmd->params[3]);
+	clientid = atoi(cmd->params[4]);
 	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE);
 
 	msn_user_set_state(user, state);
@@ -821,6 +1157,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]));
+		}
 	}
 }
 
@@ -828,11 +1170,10 @@
 reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnSession *session;
-	int group_id;
-	const char *group_name;
+	const char *group_id, *group_name;
 
 	session = cmdproc->session;
-	group_id = atoi(cmd->params[2]);
+	group_id = cmd->params[2];
 	group_name = purple_url_decode(cmd->params[3]);
 
 	msn_userlist_rename_group_id(session->userlist, group_id, group_name);
@@ -841,12 +1182,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);
 
@@ -858,10 +1199,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];
@@ -873,9 +1212,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);
@@ -885,10 +1224,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);
 }
@@ -896,12 +1235,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);
 
@@ -1146,6 +1485,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
  **************************************************************************/
@@ -1155,7 +1574,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"))
@@ -1187,10 +1608,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
@@ -1246,6 +1684,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)
 {
@@ -1349,46 +1866,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)
 {
@@ -1399,18 +1932,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);
@@ -1419,11 +1952,15 @@
 	/* Asynchronous */
 	msn_table_add_cmd(cbs_table, NULL, "IPG", ipg_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "UBM", ubm_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "GCF", gcf_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "SBS", sbs_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "NOT", not_cmd);
 
 	msn_table_add_cmd(cbs_table, NULL, "CHL", chl_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "REM", rem_cmd);
-	msn_table_add_cmd(cbs_table, NULL, "ADD", add_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "RML", rml_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "ADL", adl_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "FQY", fqy_cmd);
 
 	msn_table_add_cmd(cbs_table, NULL, "QRY", NULL);
 	msn_table_add_cmd(cbs_table, NULL, "QNG", qng_cmd);
@@ -1433,11 +1970,15 @@
 	msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
 	msn_table_add_cmd(cbs_table, NULL, "RNG", rng_cmd);
 
+	msn_table_add_cmd(cbs_table, NULL, "UBX", ubx_cmd);
+	msn_table_add_cmd(cbs_table, NULL, "UUX", uux_cmd);
+
 	msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd);
 
 	msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd);
 
 	msn_table_add_error(cbs_table, "ADD", add_error);
+	msn_table_add_error(cbs_table, "ADL", adl_error);
 	msn_table_add_error(cbs_table, "REG", reg_error);
 	msn_table_add_error(cbs_table, "RMG", rmg_error);
 	/* msn_table_add_error(cbs_table, "REA", rea_error); */
@@ -1446,12 +1987,24 @@
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsprofile",
 						   profile_msg);
+	/*initial OIM notification*/
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsinitialmdatanotification",
+							initial_mdata_msg);	
+	/*OIM notification when user online*/
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsoimnotification",
+							initial_mdata_msg);	
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsinitialemailnotification",
 						   initial_email_msg);
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsemailnotification",
 						   email_msg);
+	/*delete an offline Message notification*/
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsactivemailnotification",
+						   delete_oim_msg);
 	msn_table_add_msg_type(cbs_table,
 						   "application/x-msmsgssystemmessage",
 						   system_msg);
@@ -1462,3 +2015,4 @@
 {
 	msn_table_destroy(cbs_table);
 }
+
--- a/libpurple/protocols/msn/notification.h	Sat Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/notification.h	Sat Jul 07 03:52:12 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	Sat Jul 07 03:52:12 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	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/servconn.c	Sat Jul 07 03:52:12 2007 +0000
@@ -166,7 +166,7 @@
  **************************************************************************/
 
 static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
+connect_cb(gpointer data, gint source, const char *error_message)
 {
 	MsnServConn *servconn;
 
@@ -243,7 +243,9 @@
 		return TRUE;
 	}
 	else
+	{
 		return FALSE;
+	}
 }
 
 void
@@ -444,6 +446,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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/session.c	Sat Jul 07 03:52:12 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)
@@ -419,3 +469,4 @@
 		msn_cmdproc_send(session->notification->cmdproc, "URL", "%s", "INBOX");
 	}
 }
+
--- a/libpurple/protocols/msn/session.h	Sat Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/session.h	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/slp.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/slplink.c	Sat Jul 07 03:52:12 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	Sat Jul 07 03:52:12 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	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/state.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/state.h	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/sync.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/user.h	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/userlist.c	Sat Jul 07 03:52:12 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 Jul 07 00:30:45 2007 +0000
+++ b/libpurple/protocols/msn/userlist.h	Sat Jul 07 03:52:12 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_ */

mercurial