changeset 20408:14b8fb8d27b3

merge of 790a91b1bb6c10ada984951cbd2485e95e5159fd and 05f7badcad9471084218e698a5c011908d17af90
author Ka-Hing Cheung <khc@hxbc.us>
date Wed, 02 May 2007 06:06:56 +0000
parents 6b4e778ee4b4 (current diff) 1122d47583a1 (diff)
children 7c0a2ef88018
files libpurple/protocols/msn/msn-utils.c libpurple/protocols/msn/msn-utils.h libpurple/protocols/msn/nexus.c
diffstat 48 files changed, 6385 insertions(+), 1236 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/internal.h	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/internal.h	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/Makefile.am	Wed May 02 06:06:56 2007 +0000
@@ -8,6 +8,8 @@
 	cmdproc.h \
 	command.c \
 	command.h \
+	contact.c\
+	contact.h\
 	dialog.c \
 	dialog.h \
 	directconn.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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/Makefile.mingw	Wed May 02 06:06:56 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	Wed May 02 06:06:56 2007 +0000
@@ -0,0 +1,55 @@
+MSNP14 Implementation
+by Ma Yuan<mayuan2006@gmail.com>
+
+1. Introduction
+-------------
+
+MSNP14 Protocol, proposed by Windows Live Messenger, is new, and there is no available implementation except the official one on Windows Platform.
+
+It has introduced many new features attractable to many users, such as:
+* Offline Instant Message
+	You can send the offline Message to the offline User,
+	The message will be posted to that user the next time when he is online.
+
+* Communicate with Yahoo User
+	U can chat with the Yahoo User in MSN, That's Fantastic! Till now ,
+	you can send text/Nudge to Yahoo User.
+
+* Windows Live ID authentition
+	WLM use the Window Live ID Authentication process,Known as Passport 3.0,
+	The procedure is totally different to the previous Passport 2.0
+
+* Video/Audio Conversation
+	U can communicate with other's via Video/Audio.
+(Though very interesting, not implemented in this version)
+
+2.New Features Added
+-----------------
+
+Till now, This project has implemented the following Feature:
+* Windows Live ID authentication.
+
+* Offline Instant Message
+Now can send and receive the Offline Instant Message to MSN user and Yahoo User.
+
+*contact management
+Can add/delete Contact
+Can add/delete Group
+
+* Communicate with Yahoo User
+Can send/receive Message/Nudge to Yahoo User.
+
+*. Changes to made to fit MSNP14 Protocol
+
+3. Reference
+-------------
+
+The very useful sites of MSN Protocol:
+MSNpiki site:
+reverse engineer of MSN Protocol.up to dated.
+http://msnpiki.msnfanatic.com/index.php/MSN_Protocol_Version_13
+
+hypothetic site:
+old MSN Protocol Introduction,but very useful for basic idea of MSN protocol
+http://www.hypothetic.org/docs/msn/index.php
+
--- a/libpurple/protocols/msn/cmdproc.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/cmdproc.c	Wed May 02 06:06:56 2007 +0000
@@ -89,8 +89,7 @@
 
 	tmp = (incoming) ? 'S' : 'C';
 
-	if ((show[len - 1] == '\n') && (show[len - 2] == '\r'))
-	{
+	if ((show[len - 1] == '\n') && (show[len - 2] == '\r')){
 		show[len - 2] = '\0';
 	}
 
@@ -127,14 +126,14 @@
 		trans->callbacks = g_hash_table_lookup(cmdproc->cbs_table->cmds,
 											   trans->command);
 
-	if (trans->payload != NULL)
-	{
+	if (trans->payload != NULL){
 		data = g_realloc(data, len + trans->payload_len);
 		memcpy(data + len, trans->payload, trans->payload_len);
 		len += trans->payload_len;
 	}
 
 	msn_servconn_write(servconn, data, len);
+//	purple_debug_info("<<","%s\n",data);
 
 	g_free(data);
 }
@@ -228,8 +227,7 @@
 {
 	MsnMsgTypeCb cb;
 
-	if (msn_message_get_content_type(msg) == NULL)
-	{
+	if (msn_message_get_content_type(msg) == NULL){
 		purple_debug_misc("msn", "failed to find message content\n");
 		return;
 	}
@@ -237,8 +235,7 @@
 	cb = g_hash_table_lookup(cmdproc->cbs_table->msgs,
 							 msn_message_get_content_type(msg));
 
-	if (cb == NULL)
-	{
+	if (cb == NULL){
 		purple_debug_warning("msn", "Unhandled content-type '%s'\n",
 						   msn_message_get_content_type(msg));
 
--- a/libpurple/protocols/msn/command.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/command.c	Wed May 02 06:06:56 2007 +0000
@@ -24,6 +24,10 @@
 #include "msn.h"
 #include "command.h"
 
+/*local Function prototype*/
+int msn_get_payload_position(char *str);
+int msn_set_payload_len(MsnCommand *cmd);
+
 static gboolean
 is_num(char *str)
 {
@@ -36,6 +40,72 @@
 	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"))){
+			return TRUE;
+		}
+
+	return FALSE;
+}
+
+/*get the payload positon*/
+int msn_get_payload_position(char *str)
+{
+	/*because MSG has "MSG hotmail hotmail [payload length]"*/
+	if(!(strcmp(str,"MSG"))|| (!strcmp(str,"UBX")) ){
+		return 2;
+	}
+	/*Yahoo User Message UBM 
+	 * Format UBM email@yahoo.com 32 1 [payload length]*/
+	if(!(strcmp(str,"UBM"))|| (!strcmp(str,"UUM")) ){
+		return 3;
+	}
+
+	return 1;
+}
+
+/*
+ * set command Payload length
+ */
+int
+msn_set_payload_len(MsnCommand *cmd)
+{
+	char * param;
+
+	if(msn_check_payload_cmd(cmd->command)){
+		param = cmd->params[msn_get_payload_position(cmd->command)];
+#if 0
+		if(!(strcmp(cmd->command,"MSG"))){
+			param = cmd->params[2];
+		}else{
+			param = cmd->params[1];
+		}
+#endif
+		cmd->payload_len = is_num(param) ? atoi(param) : 0;
+	}else{
+		cmd->payload_len = 0;
+	}
+	return 0;
+}
+
 MsnCommand *
 msn_command_from_string(const char *string)
 {
@@ -70,7 +140,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	Wed May 02 06:06:56 2007 +0000
@@ -0,0 +1,879 @@
+/**
+ * @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;
+	}
+	return 0;
+}
+
+/*get User Type*/
+static int 
+msn_get_user_type(char * type)
+{
+	if(!strcmp(type,"Regular")){
+		return 1;
+	}
+	if(!strcmp(type,"Live")){
+		return 1;
+	}
+	if(!strcmp(type,"LivePending")){
+		return 1;
+	}
+
+	return 0;
+}
+
+/*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_blist_node_set_string(msn_session_get_bnode(contact->session),"CLLastChange",LastChangeStr);
+	purple_debug_misc("MSNCL","LastChangeNode %s\n",LastChangeStr);
+	
+	memberships =xmlnode_get_child(service,"Memberships");
+	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_blist_node_get_string(msn_session_get_bnode(contact->session),"ablastChange");
+	dynamicItemLastChange = purple_blist_node_get_string(msn_session_get_bnode(contact->session),"dynamicItemLastChange");
+	msn_get_address_book(contact, abLastChange, dynamicItemLastChange);
+}
+
+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 gboolean
+msn_parse_addressbook(MsnContact * contact)
+{
+	MsnSession * session;
+	xmlnode * node,*body,*response,*result;
+	xmlnode *groups,*group,*groupname,*groupId,*groupInfo;
+	xmlnode	*contacts,*contactNode,*contactId,*contactInfo,*contactType,*passportName,*displayName,*groupIds,*guid;
+	xmlnode *abNode;
+	char *group_name,*group_id;
+
+	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");
+	for(group = xmlnode_get_child(groups, "Group"); group;
+					group = xmlnode_get_next_twin(group)){
+		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_node_set_string(&(g->node),"groupId",group_id);
+			purple_blist_add_group(g, NULL);
+		}
+		g_free(group_id);
+		g_free(group_name);
+	}
+	/*add a default No group to set up the no group Membership*/
+	group_id = g_strdup(MSN_INDIVIDUALS_GROUP_ID);
+	group_name = g_strdup(MSN_INDIVIDUALS_GROUP_NAME);
+	msn_group_new(session->userlist,group_id , group_name);
+	if (group_id != NULL){
+		purple_debug_misc("MsnAB","group_id:%s name:%s,value:%d\n",group_id,group_name,*group_name=='\0');
+		if ((purple_find_group(group_name)) == NULL){
+			PurpleGroup *g = purple_group_new(group_name);
+			purple_blist_add_group(g, NULL);
+		}
+	}
+	g_free(group_name);
+	g_free(group_id);
+
+	/*add a default No group to set up the no group Membership*/
+	group_id = g_strdup(MSN_NON_IM_GROUP_ID);
+	group_name = g_strdup(MSN_NON_IM_GROUP_NAME);
+	msn_group_new(session->userlist,group_id , group_name);
+	if (group_id != NULL){
+		purple_debug_misc("MsnAB","group_id:%s name:%s,value:%d\n",group_id,group_name,*group_name=='\0');
+		if ((purple_find_group(group_name)) == NULL){
+			PurpleGroup *g = purple_group_new(group_name);
+			purple_blist_add_group(g, NULL);
+		}
+	}
+	g_free(group_name);
+	g_free(group_id);
+
+	/*Process contact List*/
+	purple_debug_info("MSNAB","process contact list...\n");
+	contacts =xmlnode_get_child(result,"contacts");
+	for(contactNode = xmlnode_get_child(contacts, "Contact"); contactNode;
+				contactNode = xmlnode_get_next_twin(contactNode)){
+		MsnUser *user;
+		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 */
+		}
+
+		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);
+					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,msn_get_user_type(type));
+		user->list_op |= MSN_LIST_FL_OP;
+		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)){
+				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*/
+			group_id = g_strdup(MSN_INDIVIDUALS_GROUP_ID);
+			msn_user_add_group_id(user,group_id);
+			g_free(group_id);
+		}
+	}
+
+	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_blist_node_set_string(msn_session_get_bnode(contact->session),"ablastChange",lastchange);
+		
+		DynamicItemLastChangedNode = xmlnode_get_child(abNode,"DynamicItemLastChanged");
+		dynamicChange = xmlnode_get_data(DynamicItemLastChangedNode);
+		purple_debug_info("MsnAB"," DynamicItemLastChanged :{%s}\n",dynamicChange);
+		purple_blist_node_set_string(msn_session_get_bnode(contact->session),"DynamicItemLastChanged",lastchange);
+	}
+
+	xmlnode_free(node);
+	msn_soap_free_read_buf(contact->soapconn);
+	return TRUE;
+}
+
+static void
+msn_get_address_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+	MsnContact *contact;
+	MsnSession *session;
+
+	contact = soapconn->parent;
+	g_return_if_fail(contact != NULL);
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+//	purple_debug_misc("msn", "soap contact server Reply: {%s}\n", soapconn->read_buf);
+	if (msn_parse_addressbook(contact)) {
+		msn_notification_dump_contact(session);
+		msn_set_psm(session);
+		msn_session_finish_login(session);
+	} else {
+		msn_get_address_book(contact, NULL, NULL);
+	}
+
+	/*free the read buffer*/
+	msn_soap_free_read_buf(soapconn);
+}
+
+/**/
+static void
+msn_address_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","finish contact written\n");
+	soapconn->read_cb = msn_get_address_cb;
+}
+
+/*get the address book*/
+void
+msn_get_address_book(MsnContact *contact, const char *LastChanged, const char *dynamicItemLastChange)
+{
+	MsnSoapReq *soap_request;
+	char *body = NULL;
+	char *ab_update_str,*update_str;
+
+	purple_debug_info("MaYuan","msn_get_address_book()...\n");
+	/*build SOAP and POST it*/
+	if(LastChanged != NULL){
+		ab_update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML,LastChanged);
+	}else{
+		ab_update_str = g_strdup("");
+	}
+	if(dynamicItemLastChange != NULL){
+		update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML,
+									 dynamicItemLastChange);
+	}else{
+		update_str = g_strdup(ab_update_str);
+	}
+	g_free(ab_update_str);
+	
+	body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE,update_str);
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_ADDRESS_BOOK_POST_URL,MSN_GET_ADDRESS_SOAP_ACTION,
+					body,
+					msn_get_address_cb,
+					msn_address_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	g_free(update_str);
+	g_free(body);
+}
+
+static void
+msn_add_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	purple_debug_info("MaYuan","add contact read done\n");
+}
+
+static void
+msn_add_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","finish add contact  written\n");
+	soapconn->read_cb = msn_add_contact_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+/*add a Contact */
+void
+msn_add_contact(MsnContact *contact,const char *passport,const char *groupId)
+{
+	MsnSoapReq *soap_request;
+	char *body = NULL;
+	char *contact_xml = NULL;
+	char *soap_action;
+
+	purple_debug_info("MaYuan","msn add a contact...\n");
+	contact_xml = g_strdup_printf(MSN_CONTACT_XML,passport);
+	if(groupId == NULL){
+		body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE,contact_xml);
+		g_free(contact_xml);
+		/*build SOAP and POST it*/
+		soap_action = g_strdup(MSN_CONTACT_ADD_SOAP_ACTION);
+	}else{
+		body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE,groupId,contact_xml);
+		g_free(contact_xml);
+		/*build SOAP and POST it*/
+		soap_action = g_strdup(MSN_ADD_CONTACT_GROUP_SOAP_ACTION);
+	}
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_ADDRESS_BOOK_POST_URL,soap_action,
+					body,
+					msn_add_contact_read_cb,
+					msn_add_contact_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+	g_free(soap_action);
+	g_free(body);
+}
+
+static void
+msn_delete_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	purple_debug_info("MaYuan","delete contact read done\n");
+}
+
+static void
+msn_delete_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MaYuan","delete contact written\n");
+	soapconn->read_cb = msn_delete_contact_read_cb;
+//	msn_soap_read_cb(data,source,cond);
+}
+
+/*delete a Contact*/
+void
+msn_delete_contact(MsnContact *contact,const char *contactId)
+{	
+	char *body = NULL;
+	char *contact_xml = NULL ;
+	MsnSoapReq *soap_request;
+
+	g_return_if_fail(contactId != NULL);
+	purple_debug_info("MaYuan","msn delete a contact,contactId:{%s}...\n",contactId);
+	contact_xml = g_strdup_printf(MSN_CONTACTS_DEL_XML,contactId);
+	body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE,contact_xml);
+	g_free(contact_xml);
+	/*build SOAP and POST it*/
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					MSN_ADDRESS_BOOK_POST_URL,MSN_CONTACT_DEL_SOAP_ACTION,
+					body,
+					msn_delete_contact_read_cb,
+					msn_delete_contact_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+	g_free(body);
+}
+
+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*/
+#if 0
+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);
+}
+
+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);
+}
+
+#if 0
+/*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	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/dialog.c	Wed May 02 06:06:56 2007 +0000
@@ -34,38 +34,16 @@
 
 } MsnAddRemData;
 
-/* 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. */
-static void
-msn_complete_sync_issue(MsnAddRemData *data)
-{
-	PurpleBuddy *buddy;
-	PurpleGroup *group = NULL;
-
-	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;
-
-	msn_complete_sync_issue(data);
+	if (g_list_find(purple_connections_get_all(), data->gc) != NULL)
+	{
+		MsnSession *session = data->gc->proto_data;
+		MsnUserList *userlist = session->userlist;
 
-	session = data->gc->proto_data;
-	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 +53,13 @@
 static void
 msn_rem_cb(MsnAddRemData *data)
 {
-	MsnSession *session;
-	MsnUserList *userlist;
-
-	msn_complete_sync_issue(data);
+	if (g_list_find(purple_connections_get_all(), data->gc) != NULL)
+	{
+		MsnSession *session = data->gc->proto_data;
+		MsnUserList *userlist = session->userlist;
 
-	session = data->gc->proto_data;
-	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);
@@ -98,6 +74,8 @@
 	PurpleAccount *account;
 	MsnAddRemData *data;
 	char *msg, *reason;
+	PurpleBuddy *buddy;
+	PurpleGroup *group = NULL;
 
 	account = session->account;
 	gc = purple_account_get_connection(account);
@@ -132,6 +110,17 @@
 						_("Yes"), G_CALLBACK(msn_add_cb),
 						_("No"), G_CALLBACK(msn_rem_cb));
 
+	if (group_name != NULL)
+		group = purple_find_group(group_name);
+
+	if (group != NULL)
+		buddy = purple_find_buddy_in_group(account, passport, group);
+	else
+		buddy = purple_find_buddy(account, passport);
+
+	if (buddy != NULL)
+		purple_blist_remove_buddy(buddy);
+
 	g_free(reason);
 	g_free(msg);
 }
--- a/libpurple/protocols/msn/directconn.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/directconn.c	Wed May 02 06:06:56 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)
@@ -476,6 +487,7 @@
 void
 msn_directconn_destroy(MsnDirectConn *directconn)
 {
+
 	if (directconn->connect_data != NULL)
 		purple_proxy_connect_cancel(directconn->connect_data);
 
--- a/libpurple/protocols/msn/directconn.h	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/directconn.h	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/group.c	Wed May 02 06:06:56 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;
 }
@@ -87,3 +88,4 @@
 
 	return group->name;
 }
+
--- a/libpurple/protocols/msn/group.h	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/group.h	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/history.c	Wed May 02 06:06:56 2007 +0000
@@ -84,3 +84,4 @@
 		msn_transaction_destroy(trans);
 	}
 }
+
--- a/libpurple/protocols/msn/msg.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/msg.c	Wed May 02 06:06:56 2007 +0000
@@ -23,6 +23,7 @@
  */
 #include "msn.h"
 #include "msg.h"
+#define MSN_DEBUG_MSG
 
 MsnMessage *
 msn_message_new(MsnMsgType type)
@@ -104,8 +105,7 @@
 	purple_debug_info("msn", "message unref (%p)[%d]\n", msg, msg->ref_count);
 #endif
 
-	if (msg->ref_count == 0)
-	{
+	if (msg->ref_count == 0){
 		msn_message_destroy(msg);
 
 		return NULL;
@@ -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,12 @@
 	char **elems, **cur, **tokens;
 
 	g_return_if_fail(payload != NULL);
-
+//	purple_debug_info("MaYuan","payload:{%s}\n",payload);
 	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,10 +230,9 @@
 	}
 	*end = '\0';
 
-	elems = g_strsplit(tmp, "\r\n", 0);
+	elems = g_strsplit(tmp, line_dem, 0);
 
-	for (cur = elems; *cur != NULL; cur++)
-	{
+	for (cur = elems; *cur != NULL; cur++){
 		const char *key, *value;
 
 		tokens = g_strsplit(*cur, ": ", 2);
@@ -240,20 +240,17 @@
 		key = tokens[0];
 		value = tokens[1];
 
-		if (!strcmp(key, "MIME-Version"))
-		{
+		/*if not MIME content ,then return*/
+		if (!strcmp(key, "MIME-Version")){
 			g_strfreev(tokens);
 			continue;
 		}
 
-		if (!strcmp(key, "Content-Type"))
-		{
+		if (!strcmp(key, "Content-Type")){
 			char *charset, *c;
 
-			if ((c = strchr(value, ';')) != NULL)
-			{
-				if ((charset = strchr(c, '=')) != NULL)
-				{
+			if ((c = strchr(value, ';')) != NULL){
+				if ((charset = strchr(c, '=')) != NULL)	{
 					charset++;
 					msn_message_set_charset(msg, charset);
 				}
@@ -262,9 +259,7 @@
 			}
 
 			msn_message_set_content_type(msg, value);
-		}
-		else
-		{
+		}else{
 			msn_message_set_attr(msg, key, value);
 		}
 
@@ -274,14 +269,13 @@
 	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);
 
 	if (content_type != NULL &&
-		!strcmp(content_type, "application/x-msnmsgrp2p"))
-	{
+		!strcmp(content_type, "application/x-msnmsgrp2p")){
 		MsnSlpHeader header;
 		MsnSlpFooter footer;
 		int body_len;
@@ -323,9 +317,7 @@
 			tmp += sizeof(footer);
 			msg->msnslp_footer.value = GUINT32_FROM_BE(footer.value);
 		}
-	}
-	else
-	{
+	}else{
 		if (payload_len - (tmp - tmp_base) > 0) {
 			msg->body_len = payload_len - (tmp - tmp_base);
 			msg->body = g_malloc0(msg->body_len + 1);
@@ -476,15 +468,14 @@
 	}
 	else
 	{
-		if (body != NULL)
-		{
+		if (body != NULL){
 			memcpy(n, body, body_len);
 			n += body_len;
+			*n = '\0';
 		}
 	}
 
-	if (ret_size != NULL)
-	{
+	if (ret_size != NULL){
 		*ret_size = n - base;
 
 		if (*ret_size > 1664)
@@ -523,14 +514,11 @@
 	if (msg->body != NULL)
 		g_free(msg->body);
 
-	if (data != NULL && len > 0)
-	{
+	if (data != NULL && len > 0){
 		msg->body = g_malloc0(len + 1);
 		memcpy(msg->body, data, len);
 		msg->body_len = len;
-	}
-	else
-	{
+	}else{
 		msg->body = NULL;
 		msg->body_len = 0;
 	}
@@ -785,3 +773,4 @@
 
 	g_string_free(str, TRUE);
 }
+
--- a/libpurple/protocols/msn/msg.h	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/msg.h	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,440 +0,0 @@
-/**
- * @file msn-utils.c Utility functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-#include "msn.h"
-#include "msn-utils.h"
-
-void
-msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
-{
-	char *cur;
-	GString *pre  = g_string_new(NULL);
-	GString *post = g_string_new(NULL);
-	unsigned int colors[3];
-
-	if (pre_ret  != NULL) *pre_ret  = NULL;
-	if (post_ret != NULL) *post_ret = NULL;
-
-	cur = strstr(mime, "FN=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		pre = g_string_append(pre, "<FONT FACE=\"");
-
-		while (*cur && *cur != ';')
-		{
-			pre = g_string_append_c(pre, *cur);
-			cur++;
-		}
-
-		pre = g_string_append(pre, "\">");
-		post = g_string_prepend(post, "</FONT>");
-	}
-
-	cur = strstr(mime, "EF=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		while (*cur && *cur != ';')
-		{
-			pre = g_string_append_c(pre, '<');
-			pre = g_string_append_c(pre, *cur);
-			pre = g_string_append_c(pre, '>');
-			post = g_string_prepend_c(post, '>');
-			post = g_string_prepend_c(post, *cur);
-			post = g_string_prepend_c(post, '/');
-			post = g_string_prepend_c(post, '<');
-			cur++;
-		}
-	}
-
-	cur = strstr(mime, "CO=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		int i;
-
-		i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
-
-		if (i > 0)
-		{
-			char tag[64];
-
-			if (i == 1)
-			{
-				colors[1] = 0;
-				colors[2] = 0;
-			}
-			else if (i == 2)
-			{
-				unsigned int temp = colors[0];
-
-				colors[0] = colors[1];
-				colors[1] = temp;
-				colors[2] = 0;
-			}
-			else if (i == 3)
-			{
-				unsigned int temp = colors[2];
-
-				colors[2] = colors[0];
-				colors[0] = temp;
-			}
-
-			g_snprintf(tag, sizeof(tag),
-					   "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">",
-					   colors[0], colors[1], colors[2]);
-
-			pre = g_string_append(pre, tag);
-			post = g_string_prepend(post, "</FONT>");
-		}
-	}
-
-	cur = strstr(mime, "RL=");
-
-	if (cur && (*(cur = cur + 3) != ';'))
-	{
-		if (*cur == '1')
-		{
-			/* RTL text was received */
-			pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
-			post = g_string_prepend(post, "</SPAN>");
-		}
-	}
-
-	cur = g_strdup(purple_url_decode(pre->str));
-	g_string_free(pre, TRUE);
-
-	if (pre_ret != NULL)
-		*pre_ret = cur;
-	else
-		g_free(cur);
-
-	cur = g_strdup(purple_url_decode(post->str));
-	g_string_free(post, TRUE);
-
-	if (post_ret != NULL)
-		*post_ret = cur;
-	else
-		g_free(cur);
-}
-
-/*
- * We need this because we're only supposed to encode spaces in the font
- * names. purple_url_encode() isn't acceptable.
- */
-static const char *
-encode_spaces(const char *str)
-{
-	static char buf[BUF_LEN];
-	const char *c;
-	char *d;
-
-	g_return_val_if_fail(str != NULL, NULL);
-
-	for (c = str, d = buf; *c != '\0'; c++)
-	{
-		if (*c == ' ')
-		{
-			*d++ = '%';
-			*d++ = '2';
-			*d++ = '0';
-		}
-		else
-			*d++ = *c;
-	}
-
-	return buf;
-}
-
-/*
- * Taken from the zephyr plugin.
- * This parses HTML formatting (put out by one of the gtkimhtml widgets
- * and converts it to msn formatting. It doesn't deal with the tag closing,
- * but gtkimhtml widgets give valid html.
- * It currently deals properly with <b>, <u>, <i>, <font face=...>,
- * <font color=...>, <span dir=...>, <span style="direction: ...">.
- * It ignores <font back=...> and <font size=...>
- */
-void
-msn_import_html(const char *html, char **attributes, char **message)
-{
-	int len, retcount = 0;
-	const char *c;
-	char *msg;
-	char *fontface = NULL;
-	char fonteffect[4];
-	char fontcolor[7];
-	char direction = '0';
-
-	gboolean has_bold = FALSE;
-	gboolean has_italic = FALSE;
-	gboolean has_underline = FALSE;
-	gboolean has_strikethrough = FALSE;
-
-	g_return_if_fail(html       != NULL);
-	g_return_if_fail(attributes != NULL);
-	g_return_if_fail(message    != NULL);
-
-	len = strlen(html);
-	msg = g_malloc0(len + 1);
-
-	memset(fontcolor, 0, sizeof(fontcolor));
-	strcat(fontcolor, "0");
-	memset(fonteffect, 0, sizeof(fonteffect));
-
-	for (c = html; *c != '\0';)
-	{
-		if (*c == '<')
-		{
-			if (!g_ascii_strncasecmp(c + 1, "br>", 3))
-			{
-				msg[retcount++] = '\r';
-				msg[retcount++] = '\n';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
-			{
-				if (!has_italic)
-				{
-					strcat(fonteffect, "I");
-					has_italic = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
-			{
-				if (!has_bold)
-				{
-					strcat(fonteffect, "B");
-					has_bold = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
-			{
-				if (!has_underline)
-				{
-					strcat(fonteffect, "U");
-					has_underline = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
-			{
-				if (!has_strikethrough)
-				{
-					strcat(fonteffect, "S");
-					has_strikethrough = TRUE;
-				}
-				c += 3;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
-			{
-				c += 9;
-
-				if (!g_ascii_strncasecmp(c, "mailto:", 7))
-					c += 7;
-
-				while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
-					msg[retcount++] = *c++;
-
-				if (*c != '\0')
-					c += 2;
-
-				/* ignore descriptive string */
-				while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
-					c++;
-
-				if (*c != '\0')
-					c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "span", 4))
-			{
-				/* Bi-directional text support using CSS properties in span tags */
-				c += 5;
-
-				while (*c != '\0' && *c != '>')
-				{
-					while (*c == ' ')
-						c++;
-					if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
-					{
-						c += 9;
-						direction = '1';
-					}
-					else if (!g_ascii_strncasecmp(c, "style=\"", 7))
-					{
-						/* Parse inline CSS attributes */
-						char *attributes;
-						int attr_len = 0;
-						c += 7;
-						while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
-							attr_len++;
-						if (*(c + attr_len) == '"')
-						{
-							char *attr_dir;
-							attributes = g_strndup(c, attr_len);
-							attr_dir = purple_markup_get_css_property(attributes, "direction");
-							if (attr_dir && (!strncasecmp(attr_dir, "RTL", 3)))
-								direction = '1';
-							g_free(attr_dir);
-							g_free(attributes);
-						}
-
-					}
-					else
-					{
-						c++;
-					}
-				}
-				if (*c == '>')
-					c++;
-			}
-			else if (!g_ascii_strncasecmp(c + 1, "font", 4))
-			{
-				c += 5;
-
-				while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
-					c++;
-
-				if (!g_ascii_strncasecmp(c, "color=\"#", 7))
-				{
-					c += 8;
-
-					fontcolor[0] = *(c + 4);
-					fontcolor[1] = *(c + 5);
-					fontcolor[2] = *(c + 2);
-					fontcolor[3] = *(c + 3);
-					fontcolor[4] = *c;
-					fontcolor[5] = *(c + 1);
-
-					c += 8;
-				}
-				else if (!g_ascii_strncasecmp(c, "face=\"", 6))
-				{
-					const char *end = NULL;
-					const char *comma = NULL;
-					unsigned int namelen = 0;
-
-					c += 6;
-					end = strchr(c, '\"');
-					comma = strchr(c, ',');
-
-					if (comma == NULL || comma > end)
-						namelen = (unsigned int)(end - c);
-					else
-						namelen = (unsigned int)(comma - c);
-
-					fontface = g_strndup(c, namelen);
-					c = end + 2;
-				}
-				else
-				{
-					/* Drop all unrecognized/misparsed font tags */
-					while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
-						c++;
-
-					if (*c != '\0')
-						c += 2;
-				}
-			}
-			else
-			{
-				while ((*c != '\0') && (*c != '>'))
-					c++;
-				if (*c != '\0')
-					c++;
-			}
-		}
-		else if (*c == '&')
-		{
-			if (!g_ascii_strncasecmp(c, "&lt;", 4))
-			{
-				msg[retcount++] = '<';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&gt;", 4))
-			{
-				msg[retcount++] = '>';
-				c += 4;
-			}
-			else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
-			{
-				msg[retcount++] = ' ';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&quot;", 6))
-			{
-				msg[retcount++] = '"';
-				c += 6;
-			}
-			else if (!g_ascii_strncasecmp(c, "&amp;", 5))
-			{
-				msg[retcount++] = '&';
-				c += 5;
-			}
-			else if (!g_ascii_strncasecmp(c, "&apos;", 6))
-			{
-				msg[retcount++] = '\'';
-				c += 6;
-			}
-			else
-				msg[retcount++] = *c++;
-		}
-		else
-			msg[retcount++] = *c++;
-	}
-
-	if (fontface == NULL)
-		fontface = g_strdup("MS Sans Serif");
-
-	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
-								  encode_spaces(fontface),
-								  fonteffect, fontcolor, direction);
-	*message = g_strdup(msg);
-
-	g_free(fontface);
-	g_free(msg);
-}
-
-void
-msn_parse_socket(const char *str, char **ret_host, int *ret_port)
-{
-	char *host;
-	char *c;
-	int port;
-
-	host = g_strdup(str);
-
-	if ((c = strchr(host, ':')) != NULL)
-	{
-		*c = '\0';
-		port = atoi(c + 1);
-	}
-	else
-		port = 1863;
-
-	*ret_host = host;
-	*ret_port = port;
-}
--- a/libpurple/protocols/msn/msn-utils.h	Mon Apr 23 17:19:11 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Wed May 02 06:06:56 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"
@@ -114,7 +114,7 @@
 	return PURPLE_CMD_RET_OK;
 }
 
-static void
+void
 msn_act_id(PurpleConnection *gc, const char *entry)
 {
 	MsnCmdProc *cmdproc;
@@ -122,6 +122,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);
@@ -138,9 +141,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
@@ -429,6 +450,7 @@
 	session = gc->proto_data;
 
 	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
+
 	if (xfer)
 	{
 		slplink = msn_session_get_slplink(session, who);
@@ -480,18 +502,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;
@@ -509,8 +569,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)
@@ -535,29 +611,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_AWAY, "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_AWAY, "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,
@@ -696,7 +791,8 @@
 		return;
 	}
 
-	http_method = purple_account_get_bool(account, "http_method", FALSE);
+	if (purple_account_get_bool(account, "http_method", FALSE))
+		http_method = TRUE;
 
 	host = purple_account_get_string(account, "server", MSN_SERVER);
 	port = purple_account_get_int(account, "port", MSN_PORT);
@@ -743,6 +839,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) {
@@ -756,58 +853,86 @@
 	}
 
 	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;
+			/*
+			 * 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);
+			serv_got_im(gc, who, body_str, flags, time(NULL));
+			g_free(body_str);
+		}
 
+		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;
-		/*
-		 * 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);
-		serv_got_im(gc, who, body_str, flags, time(NULL));
-		g_free(body_str);
-	}
-
-	msn_message_destroy(msg);
-
 	return 1;
 }
 
@@ -867,8 +992,7 @@
 
 	gc = purple_account_get_connection(account);
 
-	if (gc != NULL)
-	{
+	if (gc != NULL){
 		session = gc->proto_data;
 		msn_change_status(session);
 	}
@@ -945,6 +1069,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
@@ -1092,12 +1217,9 @@
 	cmdproc = session->notification->cmdproc;
 
 	if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
-		account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
-	{
+		account->perm_deny == PURPLE_PRIVACY_DENY_USERS){
 		msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
-	}
-	else
-	{
+	}else{
 		msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
 	}
 }
@@ -1237,22 +1359,22 @@
 {
 	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
-	{
+	}else{
+		/*not found*/
 		msn_cmdproc_send(cmdproc, "ADG", "%s 0", enc_new_group_name);
 	}
 }
@@ -1308,14 +1430,19 @@
 {
 	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)
-	{
-		msn_cmdproc_send(cmdproc, "RMG", "%d", group_id);
+	/*we can't delete the default group*/
+	if(!strcmp(group->name,MSN_INDIVIDUALS_GROUP_NAME)||
+		!strcmp(group->name,MSN_NON_IM_GROUP_NAME)){
+		return ;
+	}
+	group_id = msn_userlist_find_group_id(session->userlist, group->name);
+	if (group_id != NULL){
+		msn_del_group(session,group_id);
 	}
 }
 
@@ -1330,19 +1457,16 @@
 	b = purple_find_buddy(purple_connection_get_account(info_data->gc),
 						info_data->name);
 
-	if (b)
-	{
+	if (b){
 		char *tmp;
 
-		if (b->alias && b->alias[0])
-		{
+		if (b->alias && b->alias[0]){
 			char *aliastext = g_markup_escape_text(b->alias, -1);
 			purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext);
 			g_free(aliastext);
 		}
 
-		if (b->server_alias)
-		{
+		if (b->server_alias){
 			char *nicktext = g_markup_escape_text(b->server_alias, -1);
 			tmp = g_strdup_printf("<font sml=\"msn\">%s</font><br>", nicktext);
 			purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp);
@@ -1366,12 +1490,10 @@
 {
 	char *p, *q;
 
-	if ((p = strstr(url_text, " contactparams:photopreauthurl=\"")) != NULL)
-	{
-		p += strlen(" contactparams:photopreauthurl=\"");
+	if ((p = strstr(url_text, PHOTO_URL)) != NULL){
+		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;
@@ -1420,7 +1542,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)
@@ -1801,6 +1923,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));
@@ -2025,7 +2148,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 */
@@ -2075,10 +2198,10 @@
 	"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"),
+	"MaYuan <mayuan2006@gmail.com>",				/**< author         */
 	PURPLE_WEBSITE,                                     /**< homepage       */
 
 	msn_load,                                         /**< load           */
@@ -2097,11 +2220,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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/msn.h	Wed May 02 06:06:56 2007 +0000
@@ -62,12 +62,24 @@
 #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 PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login="
-#define PROFILE_URL "http://spaces.msn.com/profile.aspx?mem="
+/*#define PROFILE_URL "http://spaces.msn.com/profile.aspx?mem="*/
+#define PROFILE_URL "http://spaces.live.com/profile.aspx?mem="
+#define PHOTO_URL	" contactparams:photopreauthurl=\""
 
 #define USEROPT_HOTMAIL 0
 
@@ -88,6 +100,7 @@
 	MSN_LIST_RL_OP = 0x08
 
 } MsnListOp;
+#define MSN_LIST_OP_MASK	0x07
 
 typedef enum
 {
@@ -128,4 +141,7 @@
 	 (MSN_CLIENT_ID_RESERVED_2 <<  8) | \
 	 (MSN_CLIENT_ID_CAPABILITIES))
 
+void msn_act_id(PurpleConnection *gc, const char *entry);
+
+
 #endif /* _MSN_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/msnutils.c	Wed May 02 06:06:56 2007 +0000
@@ -0,0 +1,1592 @@
+/**
+ * @file msnutils.c Utility functions
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "msn.h"
+#include "msnutils.h"
+#include "time.h"
+//#include <openssl/md5.h>
+
+char *rand_guid(void);
+
+/**************************************************************************
+ * Util
+ **************************************************************************/
+char *
+rand_guid()
+{
+	return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111,
+			rand() % 0xAAFF + 0x1111);
+}
+
+void
+msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
+{
+	char *cur;
+	GString *pre  = g_string_new(NULL);
+	GString *post = g_string_new(NULL);
+	unsigned int colors[3];
+
+	if (pre_ret  != NULL) *pre_ret  = NULL;
+	if (post_ret != NULL) *post_ret = NULL;
+
+	cur = strstr(mime, "FN=");
+
+	if (cur && (*(cur = cur + 3) != ';'))
+	{
+		pre = g_string_append(pre, "<FONT FACE=\"");
+
+		while (*cur && *cur != ';')
+		{
+			pre = g_string_append_c(pre, *cur);
+			cur++;
+		}
+
+		pre = g_string_append(pre, "\">");
+		post = g_string_prepend(post, "</FONT>");
+	}
+
+	cur = strstr(mime, "EF=");
+
+	if (cur && (*(cur = cur + 3) != ';'))
+	{
+		while (*cur && *cur != ';')
+		{
+			pre = g_string_append_c(pre, '<');
+			pre = g_string_append_c(pre, *cur);
+			pre = g_string_append_c(pre, '>');
+			post = g_string_prepend_c(post, '>');
+			post = g_string_prepend_c(post, *cur);
+			post = g_string_prepend_c(post, '/');
+			post = g_string_prepend_c(post, '<');
+			cur++;
+		}
+	}
+
+	cur = strstr(mime, "CO=");
+
+	if (cur && (*(cur = cur + 3) != ';'))
+	{
+		int i;
+
+		i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]);
+
+		if (i > 0)
+		{
+			char tag[64];
+
+			if (i == 1)
+			{
+				colors[1] = 0;
+				colors[2] = 0;
+			}
+			else if (i == 2)
+			{
+				unsigned int temp = colors[0];
+
+				colors[0] = colors[1];
+				colors[1] = temp;
+				colors[2] = 0;
+			}
+			else if (i == 3)
+			{
+				unsigned int temp = colors[2];
+
+				colors[2] = colors[0];
+				colors[0] = temp;
+			}
+
+			g_snprintf(tag, sizeof(tag),
+					   "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">",
+					   colors[0], colors[1], colors[2]);
+
+			pre = g_string_append(pre, tag);
+			post = g_string_prepend(post, "</FONT>");
+		}
+	}
+
+	cur = strstr(mime, "RL=");
+
+	if (cur && (*(cur = cur + 3) != ';'))
+	{
+		if (*cur == '1')
+		{
+			/* RTL text was received */
+			pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">");
+			post = g_string_prepend(post, "</SPAN>");
+		}
+	}
+
+	cur = g_strdup(purple_url_decode(pre->str));
+	g_string_free(pre, TRUE);
+
+	if (pre_ret != NULL)
+		*pre_ret = cur;
+	else
+		g_free(cur);
+
+	cur = g_strdup(purple_url_decode(post->str));
+	g_string_free(post, TRUE);
+
+	if (post_ret != NULL)
+		*post_ret = cur;
+	else
+		g_free(cur);
+}
+
+/*encode the str to RFC2047 style
+ * Currently only support the UTF-8 and base64 encode
+ */
+char *
+msn_encode_mime(const char *str)
+{
+	char *base64;
+	
+	base64 = purple_base64_encode((guchar *)str, strlen(str));
+	return g_strdup_printf("=?utf-8?B?%s?=", base64);
+}
+
+/*
+ * We need this because we're only supposed to encode spaces in the font
+ * names. purple_url_encode() isn't acceptable.
+ */
+static const char *
+encode_spaces(const char *str)
+{
+	static char buf[BUF_LEN];
+	const char *c;
+	char *d;
+
+	g_return_val_if_fail(str != NULL, NULL);
+
+	for (c = str, d = buf; *c != '\0'; c++)
+	{
+		if (*c == ' ')
+		{
+			*d++ = '%';
+			*d++ = '2';
+			*d++ = '0';
+		}
+		else
+			*d++ = *c;
+	}
+
+	return buf;
+}
+
+/*
+ * Taken from the zephyr plugin.
+ * This parses HTML formatting (put out by one of the gtkimhtml widgets
+ * and converts it to msn formatting. It doesn't deal with the tag closing,
+ * but gtkimhtml widgets give valid html.
+ * It currently deals properly with <b>, <u>, <i>, <font face=...>,
+ * <font color=...>, <span dir=...>, <span style="direction: ...">.
+ * It ignores <font back=...> and <font size=...>
+ */
+void
+msn_import_html(const char *html, char **attributes, char **message)
+{
+	int len, retcount = 0;
+	const char *c;
+	char *msg;
+	char *fontface = NULL;
+	char fonteffect[4];
+	char fontcolor[7];
+	char direction = '0';
+
+	gboolean has_bold = FALSE;
+	gboolean has_italic = FALSE;
+	gboolean has_underline = FALSE;
+	gboolean has_strikethrough = FALSE;
+
+	g_return_if_fail(html       != NULL);
+	g_return_if_fail(attributes != NULL);
+	g_return_if_fail(message    != NULL);
+
+	len = strlen(html);
+	msg = g_malloc0(len + 1);
+
+	memset(fontcolor, 0, sizeof(fontcolor));
+	strcat(fontcolor, "0");
+	memset(fonteffect, 0, sizeof(fonteffect));
+
+	for (c = html; *c != '\0';)
+	{
+		if (*c == '<')
+		{
+			if (!g_ascii_strncasecmp(c + 1, "br>", 3))
+			{
+				msg[retcount++] = '\r';
+				msg[retcount++] = '\n';
+				c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "i>", 2))
+			{
+				if (!has_italic)
+				{
+					strcat(fonteffect, "I");
+					has_italic = TRUE;
+				}
+				c += 3;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "b>", 2))
+			{
+				if (!has_bold)
+				{
+					strcat(fonteffect, "B");
+					has_bold = TRUE;
+				}
+				c += 3;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "u>", 2))
+			{
+				if (!has_underline)
+				{
+					strcat(fonteffect, "U");
+					has_underline = TRUE;
+				}
+				c += 3;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "s>", 2))
+			{
+				if (!has_strikethrough)
+				{
+					strcat(fonteffect, "S");
+					has_strikethrough = TRUE;
+				}
+				c += 3;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8))
+			{
+				c += 9;
+
+				if (!g_ascii_strncasecmp(c, "mailto:", 7))
+					c += 7;
+
+				while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
+					msg[retcount++] = *c++;
+
+				if (*c != '\0')
+					c += 2;
+
+				/* ignore descriptive string */
+				while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4))
+					c++;
+
+				if (*c != '\0')
+					c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "span", 4))
+			{
+				/* Bi-directional text support using CSS properties in span tags */
+				c += 5;
+
+				while (*c != '\0' && *c != '>')
+				{
+					while (*c == ' ')
+						c++;
+					if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9))
+					{
+						c += 9;
+						direction = '1';
+					}
+					else if (!g_ascii_strncasecmp(c, "style=\"", 7))
+					{
+						/* Parse inline CSS attributes */
+						char *attributes;
+						int attr_len = 0;
+						c += 7;
+						while (*(c + attr_len) != '\0' && *(c + attr_len) != '"')
+							attr_len++;
+						if (*(c + attr_len) == '"')
+						{
+							char *attr_dir;
+							attributes = g_strndup(c, attr_len);
+							attr_dir = purple_markup_get_css_property(attributes, "direction");
+							if (attr_dir && (!strncasecmp(attr_dir, "RTL", 3)))
+								direction = '1';
+							g_free(attr_dir);
+							g_free(attributes);
+						}
+
+					}
+					else
+					{
+						c++;
+					}
+				}
+				if (*c == '>')
+					c++;
+			}
+			else if (!g_ascii_strncasecmp(c + 1, "font", 4))
+			{
+				c += 5;
+
+				while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1))
+					c++;
+
+				if (!g_ascii_strncasecmp(c, "color=\"#", 7))
+				{
+					c += 8;
+
+					fontcolor[0] = *(c + 4);
+					fontcolor[1] = *(c + 5);
+					fontcolor[2] = *(c + 2);
+					fontcolor[3] = *(c + 3);
+					fontcolor[4] = *c;
+					fontcolor[5] = *(c + 1);
+
+					c += 8;
+				}
+				else if (!g_ascii_strncasecmp(c, "face=\"", 6))
+				{
+					const char *end = NULL;
+					const char *comma = NULL;
+					unsigned int namelen = 0;
+
+					c += 6;
+					end = strchr(c, '\"');
+					comma = strchr(c, ',');
+
+					if (comma == NULL || comma > end)
+						namelen = (unsigned int)(end - c);
+					else
+						namelen = (unsigned int)(comma - c);
+
+					fontface = g_strndup(c, namelen);
+					c = end + 2;
+				}
+				else
+				{
+					/* Drop all unrecognized/misparsed font tags */
+					while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2))
+						c++;
+
+					if (*c != '\0')
+						c += 2;
+				}
+			}
+			else
+			{
+				while ((*c != '\0') && (*c != '>'))
+					c++;
+				if (*c != '\0')
+					c++;
+			}
+		}
+		else if (*c == '&')
+		{
+			if (!g_ascii_strncasecmp(c, "&lt;", 4))
+			{
+				msg[retcount++] = '<';
+				c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c, "&gt;", 4))
+			{
+				msg[retcount++] = '>';
+				c += 4;
+			}
+			else if (!g_ascii_strncasecmp(c, "&nbsp;", 6))
+			{
+				msg[retcount++] = ' ';
+				c += 6;
+			}
+			else if (!g_ascii_strncasecmp(c, "&quot;", 6))
+			{
+				msg[retcount++] = '"';
+				c += 6;
+			}
+			else if (!g_ascii_strncasecmp(c, "&amp;", 5))
+			{
+				msg[retcount++] = '&';
+				c += 5;
+			}
+			else if (!g_ascii_strncasecmp(c, "&apos;", 6))
+			{
+				msg[retcount++] = '\'';
+				c += 6;
+			}
+			else
+				msg[retcount++] = *c++;
+		}
+		else
+			msg[retcount++] = *c++;
+	}
+
+	if (fontface == NULL)
+		fontface = g_strdup("MS Sans Serif");
+
+	*attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c",
+								  encode_spaces(fontface),
+								  fonteffect, fontcolor, direction);
+	*message = g_strdup(msg);
+
+	g_free(fontface);
+	g_free(msg);
+}
+
+void
+msn_parse_socket(const char *str, char **ret_host, int *ret_port)
+{
+	char *host;
+	char *c;
+	int port;
+
+	host = g_strdup(str);
+
+	if ((c = strchr(host, ':')) != NULL){
+		*c = '\0';
+		port = atoi(c + 1);
+	}else{
+		port = 1863;
+	}
+
+	*ret_host = host;
+	*ret_port = port;
+}
+/***************************************************************************
+ * MSN Time Related Funciton
+ ***************************************************************************/
+#if 0
+int
+msn_convert_iso8601(const char *timestr,struct tm tm_time)
+{
+	char temp[64];
+	struct tm ctime;
+	time_t ts;
+
+	purple_debug_info("MaYuan","convert string is{%s}\n",timestr);
+	tzset();
+	/*copy string first*/
+	memset(temp, 0, sizeof(temp));
+	strncpy(temp, timestr, strlen(timestr));
+
+	/*convert via strptime()*/
+	memset(&ctime, 0, sizeof(struct tm));
+	strptime(temp, "%d %b %Y %T %Z", &ctime);
+	ts = mktime(&ctime) - timezone;
+	localtime_r(&ts, tm_time);
+}
+#endif
+
+/***************************************************************************
+ * MSN Challenge Computing Function
+ ***************************************************************************/
+/*check the edian of system*/
+int 
+isBigEndian(void)
+{
+		short int word = 0x0100;
+		char *byte = (char *)&word;
+
+		return(byte[0]);
+}
+
+/*swap utility*/
+unsigned int 
+swapInt(unsigned int dw)
+{
+		unsigned int tmp;
+		tmp =  (dw & 0x000000FF);
+		tmp = ((dw & 0x0000FF00) >> 0x08) | (tmp << 0x08);
+		tmp = ((dw & 0x00FF0000) >> 0x10) | (tmp << 0x08);
+		tmp = ((dw & 0xFF000000) >> 0x18) | (tmp << 0x08);
+		return(tmp);
+}
+
+/*
+ * 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, bigEndian;
+
+		/* Determine our endianess */
+		bigEndian = isBigEndian();
+
+		/* 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++){  
+				/* check for endianess */
+				if(bigEndian)
+						md5Parts[i] = swapInt(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;
+
+				if(bigEndian){
+						chlStringParts[i]   = swapInt(chlStringParts[i]);
+						chlStringParts[i+1] = swapInt(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;
+
+		/* swap more bytes if big endian */
+		for(i=0; i<4 && bigEndian; i++)
+				newHashParts[i] = swapInt(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);
+}
+
+#if (!defined(_XOPEN_SOURCE))||defined(_WIN32)
+
+#ifndef __P
+# if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
+#  define __P(args) args
+# else
+#  define __P(args) ()
+# endif  /* GCC.  */
+#endif  /* Not __P.  */
+
+#if defined(HAVE_LOCALTIME_R) && ! HAVE_LOCALTIME_R && ! defined localtime_r
+# ifdef _LIBC
+#  define localtime_r __localtime_r
+# else
+/* Approximate localtime_r as best we can in its absence.  */
+#  define localtime_r my_localtime_r
+static struct tm *localtime_r __P ((const time_t *, struct tm *));
+static struct tm *
+localtime_r (t, tp)
+     const time_t *t;
+     struct tm *tp;
+{
+  struct tm *l = localtime (t);
+  if (! l)
+    return 0;
+  *tp = *l;
+  return tp;
+}
+# endif /* ! _LIBC */
+#endif /* ! HAVE_LOCALTIME_R && ! defined (localtime_r) */
+
+
+#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
+
+#if defined __GNUC__ && __GNUC__ >= 2
+# define match_string(cs1, s2) \
+  ({ size_t len = strlen (cs1);						      \
+     int result = strncasecmp ((cs1), (s2), len) == 0;			      \
+     if (result) (s2) += len;						      \
+     result; })
+#else
+/* Oh come on.  Get a reasonable compiler.  */
+# define match_string(cs1, s2) \
+  (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
+#endif
+
+/* We intentionally do not use isdigit() for testing because this will
+   lead to problems with the wide character version.  */
+#define get_number(from, to, n) \
+  do {									      \
+    int __n = n;							      \
+    val = 0;								      \
+    while (*rp == ' ')							      \
+      ++rp;								      \
+    if ((*rp < '0') || (*rp > '9'))						      \
+      return NULL;							      \
+    do {								      \
+      val *= 10;							      \
+      val += *rp++ - '0';						      \
+    } while ((--__n > 0) && (val * 10 <= to) && (*rp >= '0') && (*rp <= '9'));	      \
+    if ((val < from) || (val > to))						      \
+      return NULL;							      \
+  } while (0)
+
+#ifdef _NL_CURRENT
+# define get_alt_number(from, to, n) \
+  ({									      \
+    __label__ do_normal;						      \
+    if (*decided != raw)						      \
+      {									      \
+	const char *alts = _NL_CURRENT (LC_TIME, ALT_DIGITS);		      \
+	int __n = n;							      \
+	int any = 0;							      \
+	while (*rp == ' ')						      \
+	  ++rp;								      \
+	val = 0;							      \
+	do {								      \
+	  val *= 10;							      \
+	  while (*alts != '\0')						      \
+	    {								      \
+	      size_t len = strlen (alts);				      \
+	      if (strncasecmp (alts, rp, len) == 0)			      \
+	        break;							      \
+	      alts += len + 1;						      \
+	      ++val;							      \
+	    }								      \
+	  if (*alts == '\0')						      \
+	    {								      \
+	      if (*decided == not && ! any)				      \
+		goto do_normal;						      \
+	      /* If we haven't read anything it's an error.  */		      \
+	      if (! any)						      \
+		return NULL;						      \
+	      /* Correct the premature multiplication.  */		      \
+	      val /= 10;						      \
+	      break;							      \
+	    }								      \
+	  else								      \
+	    *decided = loc;						      \
+	} while (--__n > 0 && val * 10 <= to);				      \
+	if (val < from || val > to)					      \
+	  return NULL;							      \
+      }									      \
+    else								      \
+      {									      \
+       do_normal:							      \
+        get_number (from, to, n);					      \
+      }									      \
+    0;									      \
+  })
+#else
+# define get_alt_number(from, to, n) \
+  /* We don't have the alternate representation.  */			      \
+  get_number(from, to, n)
+#endif
+
+#define recursive(new_fmt) \
+  (*(new_fmt) != '\0'							      \
+   && (rp = strptime_internal (rp, (new_fmt), tm, decided, era_cnt)) != NULL)
+
+
+#ifdef _LIBC
+/* This is defined in locale/C-time.c in the GNU libc.  */
+extern const struct locale_data _nl_C_LC_TIME;
+extern const unsigned short int __mon_yday[2][13];
+
+# define weekday_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (DAY_1)].string)
+# define ab_weekday_name \
+  (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABDAY_1)].string)
+# define month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (MON_1)].string)
+# define ab_month_name (&_nl_C_LC_TIME.values[_NL_ITEM_INDEX (ABMON_1)].string)
+# define HERE_D_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_T_FMT)].string)
+# define HERE_D_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (D_FMT)].string)
+# define HERE_AM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (AM_STR)].string)
+# define HERE_PM_STR (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (PM_STR)].string)
+# define HERE_T_FMT_AMPM \
+  (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT_AMPM)].string)
+# define HERE_T_FMT (_nl_C_LC_TIME.values[_NL_ITEM_INDEX (T_FMT)].string)
+
+# define strncasecmp(s1, s2, n) __strncasecmp (s1, s2, n)
+#else
+static char const weekday_name[][10] =
+  {
+    "Sunday", "Monday", "Tuesday", "Wednesday",
+    "Thursday", "Friday", "Saturday"
+  };
+static char const ab_weekday_name[][4] =
+  {
+    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+  };
+static char const month_name[][10] =
+  {
+    "January", "February", "March", "April", "May", "June",
+    "July", "August", "September", "October", "November", "December"
+  };
+static char const ab_month_name[][4] =
+  {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+  };
+# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
+# define HERE_D_FMT "%m/%d/%y"
+# define HERE_AM_STR "AM"
+# define HERE_PM_STR "PM"
+# define HERE_T_FMT_AMPM "%I:%M:%S %p"
+# define HERE_T_FMT "%H:%M:%S"
+
+const unsigned short int __mon_yday[2][13] =
+  {
+    /* Normal years.  */
+    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+    /* Leap years.  */
+    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+  };
+#endif
+
+/* Status of lookup: do we use the locale data or the raw data?  */
+enum locale_status { not, loc, raw };
+
+
+#ifndef __isleap
+/* Nonzero if YEAR is a leap year (every 4 years,
+   except every 100th isn't, and every 400th is).  */
+# define __isleap(year)	\
+  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+#endif
+
+/* Compute the day of the week.  */
+static void
+day_of_the_week (struct tm *tm)
+{
+  /* We know that January 1st 1970 was a Thursday (= 4).  Compute the
+     the difference between this data in the one on TM and so determine
+     the weekday.  */
+  int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
+  int wday = (-473
+	      + (365 * (tm->tm_year - 70))
+	      + (corr_year / 4)
+	      - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
+	      + (((corr_year / 4) / 25) / 4)
+	      + __mon_yday[0][tm->tm_mon]
+	      + tm->tm_mday - 1);
+  tm->tm_wday = ((wday % 7) + 7) % 7;
+}
+
+/* Compute the day of the year.  */
+static void
+day_of_the_year (struct tm *tm)
+{
+  tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
+		 + (tm->tm_mday - 1));
+}
+
+static char *
+#ifdef _LIBC
+internal_function
+#endif
+strptime_internal __P ((const char *rp, const char *fmt, struct tm *tm,
+			enum locale_status *decided, int era_cnt));
+
+static char *
+#ifdef _LIBC
+internal_function
+#endif
+strptime_internal (rp, fmt, tm, decided, era_cnt)
+     const char *rp;
+     const char *fmt;
+     struct tm *tm;
+     enum locale_status *decided;
+     int era_cnt;
+{
+  const char *rp_backup;
+  int cnt;
+  size_t val;
+  int have_I, is_pm;
+  int century, want_century;
+  int want_era;
+  int have_wday, want_xday;
+  int have_yday;
+  int have_mon, have_mday;
+#ifdef _NL_CURRENT
+  size_t num_eras;
+#endif
+  struct era_entry *era;
+
+  have_I = is_pm = 0;
+  century = -1;
+  want_century = 0;
+  want_era = 0;
+  era = NULL;
+
+  have_wday = want_xday = have_yday = have_mon = have_mday = 0;
+
+  while (*fmt != '\0')
+    {
+      /* A white space in the format string matches 0 more or white
+	 space in the input string.  */
+      if (isspace (*fmt))
+	{
+	  while (isspace (*rp))
+	    ++rp;
+	  ++fmt;
+	  continue;
+	}
+
+      /* Any character but `%' must be matched by the same character
+	 in the iput string.  */
+      if (*fmt != '%')
+	{
+	  match_char (*fmt++, *rp++);
+	  continue;
+	}
+
+      ++fmt;
+#ifndef _NL_CURRENT
+      /* We need this for handling the `E' modifier.  */
+    start_over:
+#endif
+
+      /* Make back up of current processing pointer.  */
+      rp_backup = rp;
+
+      switch (*fmt++)
+	{
+	case '%':
+	  /* Match the `%' character itself.  */
+	  match_char ('%', *rp++);
+	  break;
+	case 'a':
+	case 'A':
+	  /* Match day of week.  */
+	  for (cnt = 0; cnt < 7; ++cnt)
+	    {
+#ifdef _NL_CURRENT
+	      if (*decided !=raw)
+		{
+		  if (match_string (_NL_CURRENT (LC_TIME, DAY_1 + cnt), rp))
+		    {
+		      if (*decided == not
+			  && strcmp (_NL_CURRENT (LC_TIME, DAY_1 + cnt),
+				     weekday_name[cnt]))
+			*decided = loc;
+		      break;
+		    }
+		  if (match_string (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt), rp))
+		    {
+		      if (*decided == not
+			  && strcmp (_NL_CURRENT (LC_TIME, ABDAY_1 + cnt),
+				     ab_weekday_name[cnt]))
+			*decided = loc;
+		      break;
+		    }
+		}
+#endif
+	      if (*decided != loc
+		  && (match_string (weekday_name[cnt], rp)
+		      || match_string (ab_weekday_name[cnt], rp)))
+		{
+		  *decided = raw;
+		  break;
+		}
+	    }
+	  if (cnt == 7)
+	    /* Does not match a weekday name.  */
+	    return NULL;
+	  tm->tm_wday = cnt;
+	  have_wday = 1;
+	  break;
+	case 'b':
+	case 'B':
+	case 'h':
+	  /* Match month name.  */
+	  for (cnt = 0; cnt < 12; ++cnt)
+	    {
+#ifdef _NL_CURRENT
+	      if (*decided !=raw)
+		{
+		  if (match_string (_NL_CURRENT (LC_TIME, MON_1 + cnt), rp))
+		    {
+		      if (*decided == not
+			  && strcmp (_NL_CURRENT (LC_TIME, MON_1 + cnt),
+				     month_name[cnt]))
+			*decided = loc;
+		      break;
+		    }
+		  if (match_string (_NL_CURRENT (LC_TIME, ABMON_1 + cnt), rp))
+		    {
+		      if (*decided == not
+			  && strcmp (_NL_CURRENT (LC_TIME, ABMON_1 + cnt),
+				     ab_month_name[cnt]))
+			*decided = loc;
+		      break;
+		    }
+		}
+#endif
+	      if (match_string (month_name[cnt], rp)
+		  || match_string (ab_month_name[cnt], rp))
+		{
+		  *decided = raw;
+		  break;
+		}
+	    }
+	  if (cnt == 12)
+	    /* Does not match a month name.  */
+	    return NULL;
+	  tm->tm_mon = cnt;
+	  want_xday = 1;
+	  break;
+	case 'c':
+	  /* Match locale's date and time format.  */
+#ifdef _NL_CURRENT
+	  if (*decided != raw)
+	    {
+	      if (!recursive (_NL_CURRENT (LC_TIME, D_T_FMT)))
+		{
+		  if (*decided == loc)
+		    return NULL;
+		  else
+		    rp = rp_backup;
+		}
+	      else
+		{
+		  if (*decided == not &&
+		      strcmp (_NL_CURRENT (LC_TIME, D_T_FMT), HERE_D_T_FMT))
+		    *decided = loc;
+		  want_xday = 1;
+		  break;
+		}
+	      *decided = raw;
+	    }
+#endif
+	  if (!recursive (HERE_D_T_FMT))
+	    return NULL;
+	  want_xday = 1;
+	  break;
+	case 'C':
+	  /* Match century number.  */
+#ifdef _NL_CURRENT
+	match_century:
+#endif
+	  get_number (0, 99, 2);
+	  century = val;
+	  want_xday = 1;
+	  break;
+	case 'd':
+	case 'e':
+	  /* Match day of month.  */
+	  get_number (1, 31, 2);
+	  tm->tm_mday = val;
+	  have_mday = 1;
+	  want_xday = 1;
+	  break;
+	case 'F':
+	  if (!recursive ("%Y-%m-%d"))
+	    return NULL;
+	  want_xday = 1;
+	  break;
+	case 'x':
+#ifdef _NL_CURRENT
+	  if (*decided != raw)
+	    {
+	      if (!recursive (_NL_CURRENT (LC_TIME, D_FMT)))
+		{
+		  if (*decided == loc)
+		    return NULL;
+		  else
+		    rp = rp_backup;
+		}
+	      else
+		{
+		  if (*decided == not
+		      && strcmp (_NL_CURRENT (LC_TIME, D_FMT), HERE_D_FMT))
+		    *decided = loc;
+		  want_xday = 1;
+		  break;
+		}
+	      *decided = raw;
+	    }
+#endif
+	  /* Fall through.  */
+	case 'D':
+	  /* Match standard day format.  */
+	  if (!recursive (HERE_D_FMT))
+	    return NULL;
+	  want_xday = 1;
+	  break;
+	case 'k':
+	case 'H':
+	  /* Match hour in 24-hour clock.  */
+	  get_number (0, 23, 2);
+	  tm->tm_hour = val;
+	  have_I = 0;
+	  break;
+	case 'I':
+	  /* Match hour in 12-hour clock.  */
+	  get_number (1, 12, 2);
+	  tm->tm_hour = val % 12;
+	  have_I = 1;
+	  break;
+	case 'j':
+	  /* Match day number of year.  */
+	  get_number (1, 366, 3);
+	  tm->tm_yday = val - 1;
+	  have_yday = 1;
+	  break;
+	case 'm':
+	  /* Match number of month.  */
+	  get_number (1, 12, 2);
+	  tm->tm_mon = val - 1;
+	  have_mon = 1;
+	  want_xday = 1;
+	  break;
+	case 'M':
+	  /* Match minute.  */
+	  get_number (0, 59, 2);
+	  tm->tm_min = val;
+	  break;
+	case 'n':
+	case 't':
+	  /* Match any white space.  */
+	  while (isspace (*rp))
+	    ++rp;
+	  break;
+	case 'p':
+	  /* Match locale's equivalent of AM/PM.  */
+#ifdef _NL_CURRENT
+	  if (*decided != raw)
+	    {
+	      if (match_string (_NL_CURRENT (LC_TIME, AM_STR), rp))
+		{
+		  if (strcmp (_NL_CURRENT (LC_TIME, AM_STR), HERE_AM_STR))
+		    *decided = loc;
+		  break;
+		}
+	      if (match_string (_NL_CURRENT (LC_TIME, PM_STR), rp))
+		{
+		  if (strcmp (_NL_CURRENT (LC_TIME, PM_STR), HERE_PM_STR))
+		    *decided = loc;
+		  is_pm = 1;
+		  break;
+		}
+	      *decided = raw;
+	    }
+#endif
+	  if (!match_string (HERE_AM_STR, rp))
+	    if (match_string (HERE_PM_STR, rp))
+	      is_pm = 1;
+	    else
+	      return NULL;
+	  break;
+	case 'r':
+#ifdef _NL_CURRENT
+	  if (*decided != raw)
+	    {
+	      if (!recursive (_NL_CURRENT (LC_TIME, T_FMT_AMPM)))
+		{
+		  if (*decided == loc)
+		    return NULL;
+		  else
+		    rp = rp_backup;
+		}
+	      else
+		{
+		  if (*decided == not &&
+		      strcmp (_NL_CURRENT (LC_TIME, T_FMT_AMPM),
+			      HERE_T_FMT_AMPM))
+		    *decided = loc;
+		  break;
+		}
+	      *decided = raw;
+	    }
+#endif
+	  if (!recursive (HERE_T_FMT_AMPM))
+	    return NULL;
+	  break;
+	case 'R':
+	  if (!recursive ("%H:%M"))
+	    return NULL;
+	  break;
+	case 's':
+	  {
+	    /* The number of seconds may be very high so we cannot use
+	       the `get_number' macro.  Instead read the number
+	       character for character and construct the result while
+	       doing this.  */
+	    time_t secs = 0;
+	    if (*rp < '0' || *rp > '9')
+	      /* We need at least one digit.  */
+	      return NULL;
+
+	    do
+	      {
+		secs *= 10;
+		secs += *rp++ - '0';
+	      }
+	    while (*rp >= '0' && *rp <= '9');
+
+	    if (localtime_r (&secs, tm) == NULL)
+	      /* Error in function.  */
+	      return NULL;
+	  }
+	  break;
+	case 'S':
+	  get_number (0, 61, 2);
+	  tm->tm_sec = val;
+	  break;
+	case 'X':
+#ifdef _NL_CURRENT
+	  if (*decided != raw)
+	    {
+	      if (!recursive (_NL_CURRENT (LC_TIME, T_FMT)))
+		{
+		  if (*decided == loc)
+		    return NULL;
+		  else
+		    rp = rp_backup;
+		}
+	      else
+		{
+		  if (strcmp (_NL_CURRENT (LC_TIME, T_FMT), HERE_T_FMT))
+		    *decided = loc;
+		  break;
+		}
+	      *decided = raw;
+	    }
+#endif
+	  /* Fall through.  */
+	case 'T':
+	  if (!recursive (HERE_T_FMT))
+	    return NULL;
+	  break;
+	case 'u':
+	  get_number (1, 7, 1);
+	  tm->tm_wday = val % 7;
+	  have_wday = 1;
+	  break;
+	case 'g':
+	  get_number (0, 99, 2);
+	  /* XXX This cannot determine any field in TM.  */
+	  break;
+	case 'G':
+	  if (*rp < '0' || *rp > '9')
+	    return NULL;
+	  /* XXX Ignore the number since we would need some more
+	     information to compute a real date.  */
+	  do
+	    ++rp;
+	  while (*rp >= '0' && *rp <= '9');
+	  break;
+	case 'U':
+	case 'V':
+	case 'W':
+	  get_number (0, 53, 2);
+	  /* XXX This cannot determine any field in TM without some
+	     information.  */
+	  break;
+	case 'w':
+	  /* Match number of weekday.  */
+	  get_number (0, 6, 1);
+	  tm->tm_wday = val;
+	  have_wday = 1;
+	  break;
+	case 'y':
+#ifdef _NL_CURRENT
+	match_year_in_century:
+#endif
+	  /* Match year within century.  */
+	  get_number (0, 99, 2);
+	  /* The "Year 2000: The Millennium Rollover" paper suggests that
+	     values in the range 69-99 refer to the twentieth century.  */
+	  tm->tm_year = val >= 69 ? val : val + 100;
+	  /* Indicate that we want to use the century, if specified.  */
+	  want_century = 1;
+	  want_xday = 1;
+	  break;
+	case 'Y':
+	  /* Match year including century number.  */
+	  get_number (0, 9999, 4);
+	  tm->tm_year = val - 1900;
+	  want_century = 0;
+	  want_xday = 1;
+	  break;
+	case 'Z':
+	  /* XXX How to handle this?  */
+	  break;
+	case 'E':
+#ifdef _NL_CURRENT
+	  switch (*fmt++)
+	    {
+	    case 'c':
+	      /* Match locale's alternate date and time format.  */
+	      if (*decided != raw)
+		{
+		  const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_T_FMT);
+
+		  if (*fmt == '\0')
+		    fmt = _NL_CURRENT (LC_TIME, D_T_FMT);
+
+		  if (!recursive (fmt))
+		    {
+		      if (*decided == loc)
+			return NULL;
+		      else
+			rp = rp_backup;
+		    }
+		  else
+		    {
+		      if (strcmp (fmt, HERE_D_T_FMT))
+			*decided = loc;
+		      want_xday = 1;
+		      break;
+		    }
+		  *decided = raw;
+		}
+	      /* The C locale has no era information, so use the
+		 normal representation.  */
+	      if (!recursive (HERE_D_T_FMT))
+		return NULL;
+	      want_xday = 1;
+	      break;
+	    case 'C':
+	      if (*decided != raw)
+		{
+		  if (era_cnt >= 0)
+		    {
+		      era = _nl_select_era_entry (era_cnt);
+		      if (match_string (era->era_name, rp))
+			{
+			  *decided = loc;
+			  break;
+			}
+		      else
+			return NULL;
+		    }
+		  else
+		    {
+		      num_eras = _NL_CURRENT_WORD (LC_TIME,
+						   _NL_TIME_ERA_NUM_ENTRIES);
+		      for (era_cnt = 0; era_cnt < (int) num_eras;
+			   ++era_cnt, rp = rp_backup)
+			{
+			  era = _nl_select_era_entry (era_cnt);
+			  if (match_string (era->era_name, rp))
+			    {
+			      *decided = loc;
+			      break;
+			    }
+			}
+		      if (era_cnt == (int) num_eras)
+			{
+			  era_cnt = -1;
+			  if (*decided == loc)
+			    return NULL;
+			}
+		      else
+			break;
+		    }
+
+		  *decided = raw;
+		}
+	      /* The C locale has no era information, so use the
+		 normal representation.  */
+	      goto match_century;
+ 	    case 'y':
+	      if (*decided == raw)
+		goto match_year_in_century;
+
+	      get_number(0, 9999, 4);
+	      tm->tm_year = val;
+	      want_era = 1;
+	      want_xday = 1;
+	      break;
+	    case 'Y':
+	      if (*decided != raw)
+		{
+		  num_eras = _NL_CURRENT_WORD (LC_TIME,
+					       _NL_TIME_ERA_NUM_ENTRIES);
+		  for (era_cnt = 0; era_cnt < (int) num_eras;
+		       ++era_cnt, rp = rp_backup)
+		    {
+		      era = _nl_select_era_entry (era_cnt);
+		      if (recursive (era->era_format))
+			break;
+		    }
+		  if (era_cnt == (int) num_eras)
+		    {
+		      era_cnt = -1;
+		      if (*decided == loc)
+			return NULL;
+		      else
+			rp = rp_backup;
+		    }
+		  else
+		    {
+		      *decided = loc;
+		      era_cnt = -1;
+		      break;
+		    }
+
+		  *decided = raw;
+		}
+	      get_number (0, 9999, 4);
+	      tm->tm_year = val - 1900;
+	      want_century = 0;
+	      want_xday = 1;
+	      break;
+	    case 'x':
+	      if (*decided != raw)
+		{
+		  const char *fmt = _NL_CURRENT (LC_TIME, ERA_D_FMT);
+
+		  if (*fmt == '\0')
+		    fmt = _NL_CURRENT (LC_TIME, D_FMT);
+
+		  if (!recursive (fmt))
+		    {
+		      if (*decided == loc)
+			return NULL;
+		      else
+			rp = rp_backup;
+		    }
+		  else
+		    {
+		      if (strcmp (fmt, HERE_D_FMT))
+			*decided = loc;
+		      break;
+		    }
+		  *decided = raw;
+		}
+	      if (!recursive (HERE_D_FMT))
+		return NULL;
+	      break;
+	    case 'X':
+	      if (*decided != raw)
+		{
+		  const char *fmt = _NL_CURRENT (LC_TIME, ERA_T_FMT);
+
+		  if (*fmt == '\0')
+		    fmt = _NL_CURRENT (LC_TIME, T_FMT);
+
+		  if (!recursive (fmt))
+		    {
+		      if (*decided == loc)
+			return NULL;
+		      else
+			rp = rp_backup;
+		    }
+		  else
+		    {
+		      if (strcmp (fmt, HERE_T_FMT))
+			*decided = loc;
+		      break;
+		    }
+		  *decided = raw;
+		}
+	      if (!recursive (HERE_T_FMT))
+		return NULL;
+	      break;
+	    default:
+	      return NULL;
+	    }
+	  break;
+#else
+	  /* We have no information about the era format.  Just use
+	     the normal format.  */
+	  if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
+	      && *fmt != 'x' && *fmt != 'X')
+	    /* This is an illegal format.  */
+	    return NULL;
+
+	  goto start_over;
+#endif
+	case 'O':
+	  switch (*fmt++)
+	    {
+	    case 'd':
+	    case 'e':
+	      /* Match day of month using alternate numeric symbols.  */
+	      get_alt_number (1, 31, 2);
+	      tm->tm_mday = val;
+	      have_mday = 1;
+	      want_xday = 1;
+	      break;
+	    case 'H':
+	      /* Match hour in 24-hour clock using alternate numeric
+		 symbols.  */
+	      get_alt_number (0, 23, 2);
+	      tm->tm_hour = val;
+	      have_I = 0;
+	      break;
+	    case 'I':
+	      /* Match hour in 12-hour clock using alternate numeric
+		 symbols.  */
+	      get_alt_number (1, 12, 2);
+	      tm->tm_hour = val - 1;
+	      have_I = 1;
+	      break;
+	    case 'm':
+	      /* Match month using alternate numeric symbols.  */
+	      get_alt_number (1, 12, 2);
+	      tm->tm_mon = val - 1;
+	      have_mon = 1;
+	      want_xday = 1;
+	      break;
+	    case 'M':
+	      /* Match minutes using alternate numeric symbols.  */
+	      get_alt_number (0, 59, 2);
+	      tm->tm_min = val;
+	      break;
+	    case 'S':
+	      /* Match seconds using alternate numeric symbols.  */
+	      get_alt_number (0, 61, 2);
+	      tm->tm_sec = val;
+	      break;
+	    case 'U':
+	    case 'V':
+	    case 'W':
+	      get_alt_number (0, 53, 2);
+	      /* XXX This cannot determine any field in TM without
+		 further information.  */
+	      break;
+	    case 'w':
+	      /* Match number of weekday using alternate numeric symbols.  */
+	      get_alt_number (0, 6, 1);
+	      tm->tm_wday = val;
+	      have_wday = 1;
+	      break;
+	    case 'y':
+	      /* Match year within century using alternate numeric symbols.  */
+	      get_alt_number (0, 99, 2);
+	      tm->tm_year = val >= 69 ? val : val + 100;
+	      want_xday = 1;
+	      break;
+	    default:
+	      return NULL;
+	    }
+	  break;
+	default:
+	  return NULL;
+	}
+    }
+
+  if (have_I && is_pm)
+    tm->tm_hour += 12;
+
+  if (century != -1)
+    {
+      if (want_century)
+	tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
+      else
+	/* Only the century, but not the year.  Strange, but so be it.  */
+	tm->tm_year = (century - 19) * 100;
+    }
+
+#ifdef _NL_CURRENT
+  if (era_cnt != -1)
+    {
+      era = _nl_select_era_entry(era_cnt);
+      if (want_era)
+	tm->tm_year = (era->start_date[0]
+		       + ((tm->tm_year - era->offset)
+			  * era->absolute_direction));
+      else
+	/* Era start year assumed.  */
+	tm->tm_year = era->start_date[0];
+    }
+  else
+#endif
+    if (want_era)
+      return NULL;
+
+  if (want_xday && !have_wday)
+    {
+      if ( !(have_mon && have_mday) && have_yday)
+	{
+	  /* We don't have tm_mon and/or tm_mday, compute them.  */
+	  int t_mon = 0;
+	  while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
+	      t_mon++;
+	  if (!have_mon)
+	      tm->tm_mon = t_mon - 1;
+	  if (!have_mday)
+	      tm->tm_mday =
+		(tm->tm_yday
+		 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
+	}
+      day_of_the_week (tm);
+    }
+  if (want_xday && !have_yday)
+    day_of_the_year (tm);
+
+  return (char *) rp;
+}
+
+
+char *
+msn_strptime (buf, format, tm)
+     const char *buf;
+     const char *format;
+     struct tm *tm;
+{
+  enum locale_status decided;
+
+#ifdef _NL_CURRENT
+  decided = not;
+#else
+  decided = raw;
+#endif
+  return strptime_internal (buf, format, tm, &decided, -1);
+}
+#else
+#define msn_strptime	strptime
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/msnutils.h	Wed May 02 06:06:56 2007 +0000
@@ -0,0 +1,64 @@
+/**
+ * @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);
+int isBigEndian(void);
+unsigned int swapInt(unsigned int dw);
+char * msn_strptime (const char *buf,const char *format,struct tm *tm);
+
+
+#endif /* _MSN_UTILS_H_ */
--- a/libpurple/protocols/msn/nexus.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/nexus.c	Wed May 02 06:06:56 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,283 +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);
-			}
-		}
-
-		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;
 
@@ -406,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);
 
@@ -468,10 +426,13 @@
 }
 
 
+#endif
+
 /**************************************************************************
  * Connect
  **************************************************************************/
 
+#if 0 /* khc */
 static void
 nexus_connect_cb(gpointer data, PurpleSslConnection *gsc,
 				 PurpleInputCondition cond)
@@ -500,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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/nexus.h	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Wed May 02 06:06:56 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, char *payload, int payload_len);
+static void msn_add_contact_xml(xmlnode *mlNode, const char *passport, int list_op, int type);
+
 /**************************************************************************
  * Main
  **************************************************************************/
@@ -102,19 +110,22 @@
 	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");
 
 	vers = g_strjoinv(" ", a);
 
-	if (session->login_step == MSN_LOGIN_STEP_START)
+	if (session->login_step == MSN_LOGIN_STEP_START){
 		msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE);
-	else
+	}else{
 		msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE2);
+	}
 
 	msn_cmdproc_send(cmdproc, "VER", "%s", vers);
 
@@ -153,7 +164,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;
@@ -163,24 +174,16 @@
 	account = session->account;
 	gc = purple_account_get_connection(account);
 
-	if (error == 224)
-	{
-		if (group_id == 0)
-		{
+	if (error == 224){
+		if (group_id == 0){
 			return;
-		}
-		else
-		{
+		}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);
 		}
-	}
-	else
-	{
+	}else{
 		reason = g_strdup(_("Unknown error."));
 	}
 
@@ -214,7 +217,6 @@
 	PurpleAccount *account;
 
 	account = cmdproc->session->account;
-
 	msn_cmdproc_send(cmdproc, "USR", "TWN I %s",
 					 purple_account_get_username(account));
 }
@@ -230,34 +232,37 @@
 	account = session->account;
 	gc = purple_account_get_connection(account);
 
-	if (!g_ascii_strcasecmp(cmd->params[1], "OK"))
-	{
-		/* OK */
+	if (!g_ascii_strcasecmp(cmd->params[1], "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");
-	}
-	else if (!g_ascii_strcasecmp(cmd->params[1], "TWN"))
-	{
+//		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")){
 		/* Passport authentication */
 		char **elems, **cur, **tokens;
 
 		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);
+		for (cur = elems; *cur != NULL; cur++){
+			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);
@@ -306,24 +311,31 @@
 
 	g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver);
 
-	for (i = 1; i < cmd->param_count; i++)
+	for (i = 1; i < cmd->param_count -1; i++)
 	{
-		if (!strcmp(cmd->params[i], proto_str))
+		purple_debug_info("MaYuan","%s,proto_str:%s\n",cmd->params[i],proto_str);
+		if (strcmp(cmd->params[i], proto_str) >= 0)
 		{
 			protocol_supported = TRUE;
 			break;
 		}
 	}
 
-	if (!protocol_supported)
-	{
+	if (!protocol_supported){
 		msn_session_set_error(session, MSN_ERROR_UNSUPPORTED_PROTOCOL,
 							  NULL);
 		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 +378,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 +391,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,42 +406,159 @@
 	{
 		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 Chanllenges 
  **************************************************************************/
-
 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));
 	purple_cipher_context_digest(context, sizeof(digest), digest, NULL);
 	purple_cipher_context_destroy(context);
 
-	for (i = 0; i < 16; i++)
+	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 +568,182 @@
 /**************************************************************************
  * Buddy Lists
  **************************************************************************/
+/* add contact to xmlnode */
+static void 
+msn_add_contact_xml(xmlnode *mlNode,const char *passport,int list_op,int type)
+{
+	xmlnode *d_node,*c_node;
+	char **tokens;
+	char *email,*domain;
+	char *list_op_str,*type_str;
+
+	purple_debug_info("MaYuan","passport:%s\n",passport);
+	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_str);
+	xmlnode_set_attrib(c_node,"l",list_op_str);
+	g_free(list_op_str);
+#if 0
+	type_str = g_strdup_printf("%d",type);
+	xmlnode_set_attrib(c_node,"t",type_str);
+#else
+	if(g_strrstr(domain,"yahoo") != NULL){
+		type_str = g_strdup_printf("32");
+	}else{
+		/*passport*/
+		type_str = g_strdup_printf("1");
+	}
+	/*mobile*/
+	//type_str = g_strdup_printf("4");
+	xmlnode_set_attrib(c_node,"t",type_str);
+	g_free(type_str);
+#endif
+	xmlnode_insert_child(d_node, c_node);
+
+	g_free(tokens);
+}
 
 static void
-add_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+msn_notification_post_adl(MsnCmdProc *cmdproc, char *payload, int payload_len)
 {
-	MsnSession *session;
+	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)
+{
+	MsnUserList *userlist;
 	MsnUser *user;
-	const char *list;
-	const char *passport;
-	const char *friendly;
-	MsnListId list_id;
-	int group_id;
+	GList *l;
+	xmlnode *adl_node;
+	char *payload;
+	int payload_len;
+	const char *display_name;
+
+	userlist = session->userlist;
+	adl_node = xmlnode_new("ml");
+	adl_node->child = NULL;
+	xmlnode_set_attrib(adl_node, "l", "1");
+
+	/*get the userlist*/
+	for (l = userlist->users; l != NULL; l = l->next){
+		user = l->data;
+		msn_add_contact_xml(adl_node,user->passport,user->list_op&MSN_LIST_OP_MASK,user->type);
+	}
+
+	payload = xmlnode_to_str(adl_node,&payload_len);
+	xmlnode_free(adl_node);
 
-	list     = cmd->params[1];
-	passport = cmd->params[3];
-	friendly = purple_url_decode(cmd->params[4]);
+	msn_notification_post_adl(session->notification->cmdproc,payload,payload_len);
+
+	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);
+	}
+
+}
 
-	session = cmdproc->session;
+/*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;
 
-	user = msn_userlist_find_user(session->userlist, passport);
+	cmdproc = session->notification->cmdproc;
 
-	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);
+	tokens = g_strsplit(passport, "@", 2);
+	email = tokens[0];
+	domain = tokens[1];
+
+	payload = g_strdup_printf("<ml><d n=\"%s\"><c n=\"%s\"/></d></ml>",domain,email);
+	trans = msn_transaction_new(cmdproc, "FQY","%d",strlen(payload));
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+	msn_cmdproc_send_trans(cmdproc, trans);
+
+	g_free(payload);
+	g_free(tokens);
+}
+
+static void
+blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MaYuan","Process BLP\n");
+}
+
+static void
+adl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MaYuan","Process ADL\n");
+}
 
-	list_id = msn_get_list_id(list);
+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);
+}
+
+static void
+fqy_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+	purple_debug_info("MaYuan","Process FQY\n");
+	cmdproc->last_cmd->payload_cb  = fqy_cmd_post;
+}
 
-	if (cmd->param_count >= 6)
-		group_id = atoi(cmd->params[5]);
-	else
-		group_id = -1;
+static void
+rml_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+#if 0
+	MsnTransaction *trans;
+	char * payload;
+#endif
 
-	msn_got_add_user(session, user, list_id, group_id);
-	msn_user_update(user);
+	purple_debug_info("MaYuan","Process ADL\n");
+#if 0
+	trans = msn_transaction_new(cmdproc, "RML","");
+
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+
+	msn_cmdproc_send_trans(cmdproc, trans);
+#endif
 }
 
 static void
@@ -567,7 +838,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)
@@ -614,6 +885,7 @@
 	PurpleConnection *gc;
 	MsnUser *user;
 	MsnObject *msnobj;
+	int wlmclient;
 	const char *state, *passport, *friendly;
 
 	session = cmdproc->session;
@@ -622,7 +894,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);
 
@@ -630,9 +904,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);
 	}
 
@@ -664,6 +938,7 @@
 	MsnUser *user;
 	MsnObject *msnobj;
 	int clientid;
+	int wlmclient;
 	const char *state, *passport, *friendly, *old_friendly;
 
 	session = cmdproc->session;
@@ -672,7 +947,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);
 
@@ -685,10 +961,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
@@ -697,7 +972,7 @@
 		}
 	}
 
-	clientid = atoi(cmd->params[3]);
+	clientid = atoi(cmd->params[4]);
 	user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE);
 
 	msn_user_set_state(user, state);
@@ -793,6 +1068,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]));
+		}
 	}
 }
 
@@ -800,11 +1081,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);
@@ -813,12 +1093,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);
 
@@ -830,10 +1110,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];
@@ -845,9 +1123,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);
@@ -857,10 +1135,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);
 }
@@ -868,7 +1146,7 @@
 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);
@@ -1124,6 +1402,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
  **************************************************************************/
@@ -1133,23 +1491,23 @@
 {
 	MsnSession *session;
 	const char *value;
+	const char *clLastChange;
 
+	purple_debug_info("MaYuan","profile_msg... \n");
 	session = cmdproc->session;
 
 	if (strcmp(msg->remote_user, "Hotmail"))
 		/* This isn't an official message. */
 		return;
 
-	if ((value = msn_message_get_attr(msg, "kv")) != NULL)
-	{
+	if ((value = msn_message_get_attr(msg, "kv")) != NULL){
 		if (session->passport_info.kv != NULL)
 			g_free(session->passport_info.kv);
 
 		session->passport_info.kv = g_strdup(value);
 	}
 
-	if ((value = msn_message_get_attr(msg, "sid")) != NULL)
-	{
+	if ((value = msn_message_get_attr(msg, "sid")) != NULL){
 		if (session->passport_info.sid != NULL)
 			g_free(session->passport_info.sid);
 
@@ -1161,6 +1519,7 @@
 		if (session->passport_info.mspauth != NULL)
 			g_free(session->passport_info.mspauth);
 
+		purple_debug_info("MaYuan","MSPAuth:%s\n",value);
 		session->passport_info.mspauth = g_strdup(value);
 	}
 
@@ -1173,10 +1532,21 @@
 	}
 
 	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*/
+	msn_userlist_load(session);
+	
+	msn_session_set_bnode(session);
+	session->contact = msn_contact_new(session);
+	clLastChange = purple_blist_node_get_string(msn_session_get_bnode(session),"CLLastChange");
+	msn_get_contact_list(session->contact, clLastChange);
+//	msn_contact_connect(session->contact);
 }
 
 static void
@@ -1232,6 +1602,71 @@
 	g_hash_table_destroy(table);
 }
 
+/*offline Message notification process*/
+static void
+initial_mdata_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+	MsnSession *session;
+	char **elems, **cur, **tokens;
+
+//	purple_debug_info("MaYuan","mdata...{%s} \n",msg->body);
+
+//	/*time debug*/
+	{
+	const char *timestr;
+	time_t t;
+	struct tm *tm;
+	char datestr[]="2006-07-15T07:21:26+0700";
+	GDate *date;
+	time(&t);
+	tm = gmtime(&t);
+	timestr = purple_utf8_strftime("%a, %d %b %Y %T %Z", tm);
+//	strftime(datestr,strlen(datestr),"%a",tm);
+	date = g_date_new();
+	g_date_set_parse(date,datestr);
+	purple_debug_info("MaYuan","date is NULL?date valid%d\n",g_date_valid(date));
+	g_date_free(date);
+	purple_debug_info("MaYuan","utf8 time:{%s}\n",timestr);
+	}
+
+	/*new a oim session*/
+	session = cmdproc->session;
+	session->oim = msn_oim_new(session);
+//	msn_oim_connect(session->oim);
+
+	/*parse offline message data*/
+	elems = g_strsplit(msg->body, "\r\n", 0);
+	for (cur = elems; *cur != NULL; cur++){
+		const char *key, *value;
+
+//		purple_debug_info("MaYuan","cur:{%s}\n",*cur);
+		tokens = g_strsplit(*cur, ": ", 2);
+
+		key = tokens[0];
+		value = tokens[1];
+
+		/*if not MIME content ,then return*/
+		if ((key != NULL) && (!strcmp(key, "Mail-Data")) ){
+//			purple_debug_info("MaYuan","data:{%s}\n",value);
+			msn_parse_oim_msg(session->oim,value);
+			g_strfreev(tokens);
+			break;
+		}
+
+		g_strfreev(tokens);
+	}
+
+	g_strfreev(elems);
+/* test code for add group*/
+//	msn_add_group(session,"hello");
+}
+
+/*offline Message Notification*/
+static void
+delete_oim_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+}
+
 static void
 email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
 {
@@ -1338,46 +1773,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(adl_node,who,1,1);
 
-	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(rml_node,who,1,1);
+
+	payload = xmlnode_to_str(rml_node,&payload_len);
+	xmlnode_free(rml_node);
+
+	purple_debug_info("MaYuan","RML{%s}\n",payload);
+	trans = msn_transaction_new(cmdproc, "RML","%d",strlen(payload));
+	msn_transaction_set_payload(trans, payload, strlen(payload));
+	msn_cmdproc_send_trans(cmdproc, trans);
 }
 
 /**************************************************************************
  * Init
  **************************************************************************/
-
 void
 msn_notification_init(void)
 {
@@ -1388,18 +1839,18 @@
 	/* Synchronous */
 	msn_table_add_cmd(cbs_table, "CHG", "CHG", NULL);
 	msn_table_add_cmd(cbs_table, "CHG", "ILN", iln_cmd);
-	msn_table_add_cmd(cbs_table, "ADD", "ADD", add_cmd);
-	msn_table_add_cmd(cbs_table, "ADD", "ILN", iln_cmd);
+	msn_table_add_cmd(cbs_table, "ADL", "ILN", iln_cmd);
 	msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd);
 	msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
 	msn_table_add_cmd(cbs_table, "USR", "XFR", xfr_cmd);
+	msn_table_add_cmd(cbs_table, "USR", "GCF", gcf_cmd);
 	msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd);
 	msn_table_add_cmd(cbs_table, "CVR", "CVR", cvr_cmd);
 	msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd);
 	msn_table_add_cmd(cbs_table, "REA", "REA", rea_cmd);
 	msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd);
-	/* msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); */
-	msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL);
+	msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd);
+//	msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL);
 	msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd);
 	msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd);
 	msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd);
@@ -1408,11 +1859,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", NULL);
@@ -1422,6 +1877,9 @@
 	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);
@@ -1435,12 +1893,24 @@
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsprofile",
 						   profile_msg);
+	/*initial OIM notification*/
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsinitialmdatanotification",
+							initial_mdata_msg);	
+	/*OIM notification when user online*/
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsoimnotification",
+							initial_mdata_msg);	
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsinitialemailnotification",
 						   initial_email_msg);
 	msn_table_add_msg_type(cbs_table,
 						   "text/x-msmsgsemailnotification",
 						   email_msg);
+	/*delete an offline Message notification*/
+	msn_table_add_msg_type(cbs_table,
+							"text/x-msmsgsactivemailnotification",
+						   delete_oim_msg);
 	msn_table_add_msg_type(cbs_table,
 						   "application/x-msmsgssystemmessage",
 						   system_msg);
@@ -1451,3 +1921,4 @@
 {
 	msn_table_destroy(cbs_table);
 }
+
--- a/libpurple/protocols/msn/notification.h	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/notification.h	Wed May 02 06:06:56 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.
--- a/libpurple/protocols/msn/object.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/object.c	Wed May 02 06:06:56 2007 +0000
@@ -294,8 +294,7 @@
 
 	g_return_val_if_fail(sha1 != NULL, NULL);
 
-	for (l = local_objs; l != NULL; l = l->next)
-	{
+	for (l = local_objs; l != NULL; l = l->next){
 		MsnObject *local_obj = l->data;
 
 		if (!strcmp(msn_object_get_sha1(local_obj), sha1))
@@ -344,3 +343,4 @@
 
 	return NULL;
 }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/oim.c	Wed May 02 06:06:56 2007 +0000
@@ -0,0 +1,597 @@
+/**
+ * @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);
+const 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.
+ */
+const 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");
+
+	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, PurpleSslConnection *gsc,
+				 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, PurpleSslConnection *gsc,
+				 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;
+
+	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 = 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);
+
+	msn_session_report_user(oim->session,passport,decode_msg,PURPLE_MESSAGE_SYSTEM);
+
+	/*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, PurpleSslConnection *gsc,
+				 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 *mdNode,*mNode,*ENode,*INode,*rtNode,*nNode;
+	char *passport,*rTime,*msgid,*nickname;
+
+	mdNode = xmlnode_from_str(xmlmsg, strlen(xmlmsg));
+	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);
+/*		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);
+		g_free(nickname);
+	}
+}
+
+/*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	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/servconn.c	Wed May 02 06:06:56 2007 +0000
@@ -166,9 +166,9 @@
  **************************************************************************/
 
 static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
+connect_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
-	MsnServConn *servconn;
+	MsnServConn *servconn = data;
 
 	servconn = data;
 	servconn->connect_data = NULL;
@@ -242,7 +242,9 @@
 		return TRUE;
 	}
 	else
+	{
 		return FALSE;
+	}
 }
 
 void
@@ -443,6 +445,7 @@
 		else
 		{
 			msn_cmdproc_process_cmd_text(servconn->cmdproc, cur);
+			servconn->payload_len = servconn->cmdproc->last_cmd->payload_len;
 		}
 	} while (servconn->connected && !servconn->wasted && servconn->rx_len > 0);
 
--- a/libpurple/protocols/msn/session.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/session.c	Wed May 02 06:06:56 2007 +0000
@@ -42,8 +42,11 @@
 
 	session->user = msn_user_new(session->userlist,
 								 purple_account_get_username(account), NULL);
-
-	session->protocol_ver = 9;
+	session->bnode = NULL;
+	
+	/*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 +73,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 +92,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 +164,38 @@
 	return NULL;
 }
 
+/*get the conversation*/
+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)
 {
@@ -219,6 +261,51 @@
 	return swboard;
 }
 
+/*setup the bnode, for MSN SOAP contact/address book op*/
+void 
+msn_session_set_bnode(MsnSession *session)
+{
+	PurpleBlistNode *gnode, *cnode, *bnode;
+	PurpleConnection *gc = purple_account_get_connection(session->account);
+
+	g_return_if_fail(gc != NULL);
+
+	/* 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_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){
+					session->bnode = bnode;
+					return;
+				}
+			}
+		}
+	}
+	session->bnode = NULL;
+}
+
+/*get bnode*/
+PurpleBlistNode *
+msn_session_get_bnode(MsnSession *session)
+{
+#if 1
+	return session->bnode;
+#else
+	return purple_get_blist()->root;
+#endif
+}
+
 static void
 msn_session_sync_users(MsnSession *session)
 {
@@ -229,13 +316,17 @@
 
 	/* 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;
 		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
+		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,28 +343,33 @@
 
 					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)
 					{
 						/* The user was not on the server list or not in that group
 						 * on the server list */
-						msn_show_sync_issue(session, purple_buddy_get_name(b), group_name);
+						msn_show_sync_issue(session, b->name, group_name);
 					}
 				}
 			}
@@ -405,3 +501,4 @@
 	/* Sync users */
 	msn_session_sync_users(session);
 }
+
--- a/libpurple/protocols/msn/session.h	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/session.h	Wed May 02 06:06:56 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,18 @@
 
 	int conv_seq; /**< The current conversation sequence number. */
 
+	/*psm info*/
+	char *psm;
+	
+	/*first blist contact node*/
+	PurpleBlistNode *bnode;
+	
 	struct
 	{
+		/*t and p, get via USR TWN*/
+		char *t;
+		char *p;
+
 		char *kv;
 		char *sid;
 		char *mspauth;
@@ -114,7 +128,6 @@
 		char *file;
 		char *client_ip;
 		int client_port;
-
 	} passport_info;
 };
 
@@ -224,4 +237,17 @@
  */
 void msn_session_finish_login(MsnSession *session);
 
+/*get conversation via session,
+ * If has one, return that,else create a new one;
+ */
+PurpleConversation *msn_session_get_conv(MsnSession *session,const char *passport);
+
+/*post message to User*/
+void msn_session_report_user(MsnSession *session,const char *passport,
+							char *msg,PurpleMessageFlags flags);
+
+void msn_session_set_bnode(MsnSession *session);
+
+PurpleBlistNode *msn_session_get_bnode(MsnSession *session);
+
 #endif /* _MSN_SESSION_H_ */
--- a/libpurple/protocols/msn/slp.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/slp.c	Wed May 02 06:06:56 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);
@@ -751,7 +753,7 @@
 		 * reporting bugs. Hopefully this doesn't cause more crashes. Stu.
 		 */
 		if (slplink->swboard != NULL)
-			slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
+			slplink->swboard->slplink = slplink;
 		else
 			purple_debug_error("msn", "msn_p2p_msg, swboard is NULL, ouch!\n");
 	}
@@ -771,15 +773,14 @@
 	gc = slpcall->slplink->session->account->gc;
 	who = slpcall->slplink->remote_user;
 
-	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) {
+	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
-		*/
-		purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
-		purple_conv_custom_smiley_close(conv, slpcall->data_info);
-	}
+	/* 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
+	*/
+	purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
+	purple_conv_custom_smiley_close(conv, slpcall->data_info );
 #ifdef MSN_DEBUG_UD
 	purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
 #endif
--- a/libpurple/protocols/msn/slpcall.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/slpcall.c	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/slplink.c	Wed May 02 06:06:56 2007 +0000
@@ -102,7 +102,7 @@
 	g_return_if_fail(slplink != NULL);
 
 	if (slplink->swboard != NULL)
-		slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
+		slplink->swboard->slplink = NULL;
 
 	session = slplink->session;
 
@@ -259,7 +259,7 @@
 				return;
 
 			/* If swboard is destroyed we will be too */
-			slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
+			slplink->swboard->slplink = slplink;
 		}
 
 		msn_switchboard_send_msg(slplink->swboard, msg, TRUE);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/soap.c	Wed May 02 06:06:56 2007 +0000
@@ -0,0 +1,597 @@
+/**
+ * @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);
+	}
+	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	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/state.c	Wed May 02 06:06:56 2007 +0000
@@ -38,6 +38,208 @@
 	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;
+
+	purple_debug_info("msn", "Parsing currentmedia string: \"%s\"\n", cmedia);
+	if( (cmedia == NULL) || (!strcmp(cmedia, ""))) {
+		purple_debug_info("msn", "No currentmedia string\n");
+		return NULL;
+	}
+
+	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)
 {
@@ -63,13 +265,10 @@
 
 	msnobj = msn_user_get_object(user);
 
-	if (msnobj == NULL)
-	{
+	if (msnobj == NULL){
 		msn_cmdproc_send(cmdproc, "CHG", "%s %d", state_text,
 						 MSN_CLIENT_ID);
-	}
-	else
-	{
+	}else{
 		char *msnobj_str;
 
 		msnobj_str = msn_object_to_string(msnobj);
@@ -79,6 +278,7 @@
 
 		g_free(msnobj_str);
 	}
+	msn_set_psm(session);
 }
 
 const char *
--- a/libpurple/protocols/msn/state.h	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/state.h	Wed May 02 06:06:56 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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Wed May 02 06:06:56 2007 +0000
@@ -25,10 +25,12 @@
 #include "prefs.h"
 #include "switchboard.h"
 #include "notification.h"
-#include "msn-utils.h"
+#include "msnutils.h"
 
 #include "error.h"
 
+#define MSN_DEBUG_SB
+
 static MsnTable *cbs_table;
 
 static void msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg,
@@ -82,8 +84,8 @@
 	swboard->destroying = TRUE;
 
 	/* If it linked us is because its looking for trouble */
-	while (swboard->slplinks != NULL)
-		msn_slplink_destroy(swboard->slplinks->data);
+	if (swboard->slplink != NULL)
+		msn_slplink_destroy(swboard->slplink);
 
 	/* Destroy the message queue */
 	while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
@@ -527,6 +529,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
 
@@ -614,6 +617,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)
@@ -720,7 +724,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
@@ -742,6 +747,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;
@@ -955,7 +968,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);
 }
 
@@ -976,16 +989,13 @@
 	swboard = cmdproc->data;
 	g_return_if_fail(swboard != NULL);
 
-	if (msn_switchboard_is_invited(swboard))
-	{
+	if (msn_switchboard_is_invited(swboard)){
 		swboard->empty = FALSE;
 
 		msn_cmdproc_send(cmdproc, "ANS", "%s %s %s",
 						 purple_account_get_username(account),
 						 swboard->auth_key, swboard->session_id);
-	}
-	else
-	{
+	}else{
 		msn_cmdproc_send(cmdproc, "USR", "%s %s",
 						 purple_account_get_username(account),
 						 swboard->auth_key);
@@ -1054,6 +1064,8 @@
 cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
 {
 	int reason = MSN_SB_ERROR_UNKNOWN;
+	MsnMessage *msg;
+	MsnSwitchBoard *swboard = trans->data;
 
 	if (error == 215)
 	{
@@ -1066,7 +1078,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);
 }
 
@@ -1110,6 +1134,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);
@@ -1222,6 +1247,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/switchboard.h	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.h	Wed May 02 06:06:56 2007 +0000
@@ -102,7 +102,7 @@
 
 	MsnSBErrorType error; /**< The error that occurred in this switchboard
 							(if applicable). */
-	GList *slplinks; /**< The list of slplinks that are using this switchboard. */
+	MsnSlpLink *slplink; /**< The slplink that is using this switchboard. */
 };
 
 /**
--- a/libpurple/protocols/msn/sync.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/sync.c	Wed May 02 06:06:56 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);
--- a/libpurple/protocols/msn/table.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/table.c	Wed May 02 06:06:56 2007 +0000
@@ -77,20 +77,14 @@
 
 	cbs = NULL;
 
-	if (command == NULL)
-	{
+	if (command == NULL){
 		cbs = table->async;
-	}
-	else if (strcmp(command, "fallback") == 0)
-	{
+	}else if (strcmp(command, "fallback") == 0)	{
 		cbs = table->fallback;
-	}
-	else
-	{
+	}else{
 		cbs = g_hash_table_lookup(table->cmds, command);
 
-		if (cbs == NULL)
-		{
+		if (cbs == NULL){
 			cbs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
 			g_hash_table_insert(table->cmds, command, cbs);
 		}
--- a/libpurple/protocols/msn/transaction.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/transaction.c	Wed May 02 06:06:56 2007 +0000
@@ -205,8 +205,7 @@
 void
 msn_transaction_set_timeout_cb(MsnTransaction *trans, MsnTimeoutCb cb)
 {
-	if (trans->timer)
-	{
+	if (trans->timer){
 		purple_debug_error("msn", "This shouldn't be happening\n");
 		purple_timeout_remove(trans->timer);
 	}
--- a/libpurple/protocols/msn/user.c	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Wed May 02 06:06:56 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)
 {
@@ -58,8 +60,13 @@
 	if (user->clientcaps != NULL)
 		g_hash_table_destroy(user->clientcaps);
 
-	if (user->group_ids != NULL)
+	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 +74,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 +89,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, "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, "available", NULL);
 			purple_prpl_got_user_status(account, user->passport, "mobile", NULL);
@@ -142,12 +161,56 @@
 }
 
 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,int type)
+{
+	g_return_if_fail(user != NULL);
+
+	user->type = type;
+}
+
+void
+msn_user_set_op(MsnUser *user,int list_op)
+{
+	g_return_if_fail(list_op != 0);
+	user->list_op |= list_op;
 }
 
 void
@@ -232,54 +295,79 @@
 	}
 }
 
+/*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);
+}
+
+/*check to see if user is yahoo user?
+ * TODO: we need to identify it via contact  parse
+ */
+gboolean
+msn_user_is_yahoo(PurpleAccount *account ,const char *name)
+{
+	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)
 {
 	g_return_if_fail(user != NULL);
-	g_return_if_fail(id >= 0);
+	g_return_if_fail(id != NULL);
 
-	user->group_ids = g_list_remove(user->group_ids, GINT_TO_POINTER(id));
+	user->group_ids = g_list_remove(user->group_ids, id);
+	/* khc need to use g_list_find_custom here to find the right link */
+	//g_free(id);
 }
 
 void
--- a/libpurple/protocols/msn/user.h	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/user.h	Wed May 02 06:06:56 2007 +0000
@@ -45,7 +45,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,6 +70,7 @@
 
 	GHashTable *clientcaps; /**< The client's capabilities.     */
 
+	int type;
 	int list_op;
 };
 
@@ -102,6 +108,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 +178,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 +186,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 +204,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,int type);
+
 /**
  * Sets the mobile phone number for a user.
  *
@@ -279,6 +304,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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/userlist.c	Wed May 02 06:06:56 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(user->group_ids, group_id))
 		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;
 
@@ -110,9 +114,8 @@
 	if (!(user->list_op & list_op))
 		return FALSE;
 
-	if (list_id == MSN_LIST_FL)
-	{
-		if (group_id >= 0)
+	if (list_id == MSN_LIST_FL){
+		if (group_id != NULL)
 			return user_is_in_group(user, group_id);
 	}
 
@@ -151,25 +154,25 @@
 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)
+	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 +196,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 +216,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 +265,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 +275,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 +287,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 +335,6 @@
 	{
 		purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n",
 						passport);
-
 	}
 }
 
@@ -353,10 +356,9 @@
 	if (list_op & MSN_LIST_FL_OP)
 	{
 		GSList *c;
-		for (c = group_ids; c != NULL; c = g_slist_next(c))
-		{
-			int group_id;
-			group_id = GPOINTER_TO_INT(c->data);
+		for (c = group_ids; c != NULL; c = g_slist_next(c))	{
+			char *group_id;
+			group_id = c->data;
 			msn_user_add_group_id(user, group_id);
 		}
 
@@ -391,11 +393,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 +427,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 +449,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 +486,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 +511,17 @@
 }
 
 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)
-	{
+	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 +540,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 +557,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 +574,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 +586,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,18 +604,22 @@
 					   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);
 
-	if (group_name != 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);
 			return;
@@ -619,25 +641,26 @@
 	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))
 	{
 		/* only notify the user about problems adding to the friends list
 		 * maybe we should do something else for other lists, but it probably
 		 * won't cause too many problems if we just ignore it */
-		if (list_id == MSN_LIST_FL)
-		{
+		if (list_id == MSN_LIST_FL)	{
 			char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
 			purple_notify_error(NULL, NULL, str,
 							  _("The screen name specified is invalid."));
@@ -651,7 +674,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);
@@ -674,6 +697,8 @@
 	/* 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 +707,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 +720,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	Mon Apr 23 17:19:11 2007 +0000
+++ b/libpurple/protocols/msn/userlist.h	Wed May 02 06:06:56 2007 +0000
@@ -67,30 +67,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 +103,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_ */