changeset 20399:6f986caeab59

merge of 'b4911943cba6f29cf0694dfd563cac17612236dc' and 'c28931a50bfa21e0be81777bcd91fdeab9116d77'
author Richard Laager <rlaager@wiktel.com>
date Sun, 15 Apr 2007 04:29:56 +0000
parents 60bc06498746 (current diff) 61d6a3dfbb3c (diff)
children ea9a5566a156
files libpurple/protocols/msn/cmdproc.c libpurple/protocols/msn/command.c libpurple/protocols/msn/dialog.c libpurple/protocols/msn/msg.c libpurple/protocols/msn/msn.c libpurple/protocols/msn/msn.h libpurple/protocols/msn/notification.c libpurple/protocols/msn/servconn.c libpurple/protocols/msn/session.c libpurple/protocols/msn/session.h libpurple/protocols/msn/state.c libpurple/protocols/msn/state.h libpurple/protocols/msn/switchboard.c libpurple/protocols/msn/sync.c libpurple/protocols/msn/user.c libpurple/protocols/msn/user.h libpurple/protocols/msn/userlist.c
diffstat 18 files changed, 2836 insertions(+), 100 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/internal.h	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/internal.h	Sun Apr 15 04:29: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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/README	Sun Apr 15 04:29: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/command.c	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/command.c	Sun Apr 15 04:29:56 2007 +0000
@@ -138,7 +138,9 @@
 		param = cmd->params[0];
 
 		cmd->trId = is_num(param) ? atoi(param) : 0;
-	}else{
+	}
+	else
+	{
 		cmd->trId = 0;
 	}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/contact.c	Sun Apr 15 04:29:56 2007 +0000
@@ -0,0 +1,879 @@
+/**
+ * @file contact.c 
+ * 	get MSN contacts via SOAP request
+ *	created by MaYuan<mayuan2006@gmail.com>
+ *
+ * gaim
+ *
+ * Gaim 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(GaimSslConnection *gsc, GaimSslErrorType 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, GaimSslConnection *gsc,
+				 GaimInputCondition 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;
+	gaim_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){
+		gaim_debug_misc("MSNCL","parse contact from str err!\n");
+		return;
+	}
+	gaim_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");
+	gaim_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;
+	}
+
+	gaim_debug_misc("MSNCL","response{%p},name:%s\n",response,response->name);
+	result =xmlnode_get_child(response,"FindMembershipResult");
+	if(result == NULL){
+		gaim_debug_misc("MSNCL","receive No Update!\n");
+		return;
+	}
+	gaim_debug_misc("MSNCL","result{%p},name:%s\n",result,result->name);
+	services =xmlnode_get_child(result,"Services");
+	gaim_debug_misc("MSNCL","services{%p},name:%s\n",services,services->name);
+	service =xmlnode_get_child(services,"Service");
+	gaim_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);
+	gaim_debug_misc("MSNCL","LastChangeNode0 %s\n",LastChangeStr);	
+	gaim_blist_node_set_string(msn_session_get_bnode(contact->session),"CLLastChange",LastChangeStr);
+	gaim_debug_misc("MSNCL","LastChangeNode %s\n",LastChangeStr);
+	
+	memberships =xmlnode_get_child(service,"Memberships");
+	gaim_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);
+		gaim_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;
+
+			gaim_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);
+				gaim_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);
+				gaim_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, GaimInputCondition 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
+	gaim_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 = gaim_blist_node_get_string(msn_session_get_bnode(contact->session),"ablastChange");
+	dynamicItemLastChange = gaim_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, GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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;
+	
+	gaim_debug_info("MaYuan","Getting Contact List...\n");
+	if(update_time != NULL){
+		gaim_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;
+	gaim_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){
+		gaim_debug_misc("xml","parse from str err!\n");
+		return FALSE;
+	}
+	gaim_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");
+	gaim_debug_misc("xml","body{%p},name:%s\n",body,body->name);
+	response = xmlnode_get_child(body,"ABFindAllResponse");
+
+	if (response == NULL) {
+		return FALSE;
+	}
+
+	gaim_debug_misc("xml","response{%p},name:%s\n",response,response->name);
+	result =xmlnode_get_child(response,"ABFindAllResult");
+	if(result == NULL){
+		gaim_debug_misc("MSNAB","receive no address book update\n");
+		return TRUE;
+	}
+	gaim_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;
+		}
+
+		gaim_debug_misc("MsnAB","group_id:%s name:%s\n",group_id,group_name);
+		if ((gaim_find_group(group_name)) == NULL){
+			GaimGroup *g = gaim_group_new(group_name);
+			gaim_blist_node_set_string(&(g->node),"groupId",group_id);
+			gaim_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){
+		gaim_debug_misc("MsnAB","group_id:%s name:%s,value:%d\n",group_id,group_name,*group_name=='\0');
+		if ((gaim_find_group(group_name)) == NULL){
+			GaimGroup *g = gaim_group_new(group_name);
+			gaim_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){
+		gaim_debug_misc("MsnAB","group_id:%s name:%s,value:%d\n",group_id,group_name,*group_name=='\0');
+		if ((gaim_find_group(group_name)) == NULL){
+			GaimGroup *g = gaim_group_new(group_name);
+			gaim_blist_add_group(g, NULL);
+		}
+	}
+	g_free(group_name);
+	g_free(group_id);
+
+	/*Process contact List*/
+	gaim_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"));
+			gaim_connection_set_display_name(session->account->gc, gaim_url_decode(friendly));
+			g_free(friendly);
+		}
+
+		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);
+					gaim_debug_info("MsnAB","Yahoo User %s\n",passport);
+					break;
+				}else{
+					/*TODO maybe we can just ignore it in Gaim?*/
+					emailNode = xmlnode_get_child(contactEmailNode,"email");
+					passport = xmlnode_get_data(emailNode);
+					gaim_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);	
+		}
+
+		gaim_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);
+
+		gaim_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);
+				gaim_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);
+		gaim_debug_info("MsnAB"," lastchanged Time:{%s}\n",lastchange);
+		gaim_blist_node_set_string(msn_session_get_bnode(contact->session),"ablastChange",lastchange);
+		
+		DynamicItemLastChangedNode = xmlnode_get_child(abNode,"DynamicItemLastChanged");
+		dynamicChange = xmlnode_get_data(DynamicItemLastChangedNode);
+		gaim_debug_info("MsnAB"," DynamicItemLastChanged :{%s}\n",dynamicChange);
+		gaim_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, GaimInputCondition 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);
+
+//	gaim_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, GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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;
+
+	gaim_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, GaimInputCondition cond)
+{
+	gaim_debug_info("MaYuan","add contact read done\n");
+}
+
+static void
+msn_add_contact_written_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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;
+
+	gaim_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, GaimInputCondition cond)
+{
+	gaim_debug_info("MaYuan","delete contact read done\n");
+}
+
+static void
+msn_delete_contact_written_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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);
+	gaim_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, GaimInputCondition cond)
+{
+	gaim_debug_info("MaYuan","update contact read done\n");
+}
+
+static void
+msn_update_contact_written_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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;
+
+	gaim_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, GaimInputCondition cond)
+{
+	gaim_debug_info("MaYuan","block read done\n");
+}
+
+static void
+msn_block_written_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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;
+
+	gaim_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, GaimInputCondition cond)
+{
+	gaim_debug_info("MaYuan","unblock read done\n");
+}
+
+static void
+msn_unblock_written_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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;
+
+	gaim_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, GaimInputCondition cond)
+{
+	gaim_debug_info("MaYuan","Gleams read done\n");
+}
+
+static void
+msn_gleams_written_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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;
+
+	gaim_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, GaimInputCondition cond)
+{
+	gaim_debug_info("MaYuan","Group read \n");
+}
+
+static void
+msn_group_written_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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;
+	gaim_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;
+	gaim_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. */
+	gaim_debug_info("MaYuan","msn_contact_connect...\n");
+
+	msn_soap_init(soapconn,MSN_CONTACT_SERVER,1,
+					msn_contact_login_connect_cb,
+					msn_contact_login_error_cb);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/contact.h	Sun Apr 15 04:29:56 2007 +0000
@@ -0,0 +1,236 @@
+/**
+ * @file contact.h			Header file for contact.c
+ *	Author
+ * 		MaYuan<mayuan2006@gmail.com>
+ * gaim
+ *
+ * Gaim 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	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/dialog.c	Sun Apr 15 04:29:56 2007 +0000
@@ -45,9 +45,7 @@
 		msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, data->group);
 	}
 
-	if (data->group != NULL)
-		g_free(data->group);
-
+	g_free(data->group);
 	g_free(data->who);
 	g_free(data);
 }
@@ -63,9 +61,7 @@
 		msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->group);
 	}
 
-	if (data->group != NULL)
-		g_free(data->group);
-
+	g_free(data->group);
 	g_free(data->who);
 	g_free(data);
 }
@@ -93,13 +89,16 @@
 						  purple_account_get_username(account),
 						  purple_account_get_protocol_name(account));
 
-	if (group_name != NULL){
+	if (group_name != NULL)
+	{
 		reason = g_strdup_printf(_("%s on the local list is "
 								   "inside the group \"%s\" but not on "
 								   "the server list. "
 								   "Do you want this buddy to be added?"),
 								 passport, group_name);
-	}else{
+	}
+	else
+	{
 		reason = g_strdup_printf(_("%s is on the local list but "
 								   "not on the server list. "
 								   "Do you want this buddy to be added?"),
@@ -114,9 +113,9 @@
 	if (group_name != NULL)
 		group = purple_find_group(group_name);
 
-	if (group != NULL){
+	if (group != NULL)
 		buddy = purple_find_buddy_in_group(account, passport, group);
-	}else{
+	else
 		buddy = purple_find_buddy(account, passport);
 	}
 
--- a/libpurple/protocols/msn/msn.c	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Sun Apr 15 04:29:56 2007 +0000
@@ -451,11 +451,14 @@
 
 	xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
 
-	slplink = msn_session_get_slplink(session, who);
+	if (xfer)
+	{
+		slplink = msn_session_get_slplink(session, who);
 
-	xfer->data = slplink;
+		xfer->data = slplink;
 
-	purple_xfer_set_init_fnc(xfer, t_msn_xfer_init);
+		purple_xfer_set_init_fnc(xfer, t_msn_xfer_init);
+	}
 
 	return xfer;
 }
--- a/libpurple/protocols/msn/notification.c	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Sun Apr 15 04:29:56 2007 +0000
@@ -311,9 +311,11 @@
 
 	g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver);
 
-	for (i = 1; i < cmd->param_count -1; i++){
+	for (i = 1; i < cmd->param_count -1; i++)
+	{
 		purple_debug_info("MaYuan","%s,proto_str:%s\n",cmd->params[i],proto_str);
-		if (strcmp(cmd->params[i], proto_str) >= 0)	{
+		if (strcmp(cmd->params[i], proto_str) >= 0)
+		{
 			protocol_supported = TRUE;
 			break;
 		}
@@ -395,10 +397,13 @@
 	}
 	/* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued
 	 * command and we are processing it */
-	if (cmd->payload == NULL){
+	if (cmd->payload == NULL)
+	{
 		cmdproc->last_cmd->payload_cb  = msg_cmd_post;
 		cmdproc->servconn->payload_len = atoi(cmd->params[2]);
-	}else{
+	}
+	else
+	{
 		g_return_if_fail(cmd->payload_cb != NULL);
 
 		purple_debug_info("MaYuan","MSG payload:{%s}\n",cmd->payload);
@@ -899,7 +904,8 @@
 
 	msn_user_set_friendly_name(user, friendly);
 
-	if (session->protocol_ver >= 9 && cmd->param_count == 8){
+	if (session->protocol_ver >= 9 && cmd->param_count == 8)
+	{
 		msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6]));
 		msn_user_set_object(user, msnobj);
 	}
@@ -953,11 +959,15 @@
 		msn_user_set_friendly_name(user, friendly);
 	}
 
-	if (session->protocol_ver >= 9){
-		if (cmd->param_count == 7){
+	if (session->protocol_ver >= 9)
+	{
+		if (cmd->param_count == 7)
+		{
 			msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
 			msn_user_set_object(user, msnobj);
-		}else{
+		}
+		else
+		{
 			msn_user_set_object(user, NULL);
 		}
 	}
@@ -1374,9 +1384,12 @@
 
 	msn_parse_socket(cmd->params[2], &host, &port);
 
-	if (!strcmp(cmd->params[1], "SB")){
+	if (!strcmp(cmd->params[1], "SB"))
+	{
 		purple_debug_error("msn", "This shouldn't be handled here.\n");
-	}else if (!strcmp(cmd->params[1], "NS")){
+	}
+	else if (!strcmp(cmd->params[1], "NS"))
+	{
 		MsnSession *session;
 
 		session = cmdproc->session;
@@ -1501,7 +1514,8 @@
 		session->passport_info.sid = g_strdup(value);
 	}
 
-	if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL){
+	if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL)
+	{
 		if (session->passport_info.mspauth != NULL)
 			g_free(session->passport_info.mspauth);
 
@@ -1509,14 +1523,16 @@
 		session->passport_info.mspauth = g_strdup(value);
 	}
 
-	if ((value = msn_message_get_attr(msg, "ClientIP")) != NULL){
+	if ((value = msn_message_get_attr(msg, "ClientIP")) != NULL)
+	{
 		if (session->passport_info.client_ip != NULL)
 			g_free(session->passport_info.client_ip);
 
 		session->passport_info.client_ip = g_strdup(value);
 	}
 
-	if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL){
+	if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL)
+	{
 		session->passport_info.client_port = ntohs(atoi(value));
 	}
 
@@ -1766,9 +1782,6 @@
 
 	cmdproc = notification->servconn->cmdproc;
 
-	if (strcmp(list, "FL") != 0){
-	}
-
 	adl_node = xmlnode_new("ml");
 	adl_node->child = NULL;
 
@@ -1776,9 +1789,12 @@
 
 	payload = xmlnode_to_str(adl_node,&payload_len);
 	xmlnode_free(adl_node);
-	if(msn_user_is_yahoo(notification->session->account,who)){
+	if (msn_user_is_yahoo(notification->session->account,who))
+	{
 		msn_notification_fqy_yahoo(notification->session, who);
-	}else{
+	}
+	else
+	{
 		msn_notification_post_adl(notification->servconn->cmdproc,
 							payload,payload_len);
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/oim.c	Sun Apr 15 04:29: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>
+ * gaim
+ *
+ * Gaim 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;
+	
+	gaim_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;
+	
+	gaim_debug_info("MaYuan","encode OIM Message...\n");	
+	oim_base64 = gaim_base64_encode((const guchar *)body, strlen(body));
+	gaim_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(GaimSslConnection *gsc, GaimSslErrorType 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, GaimSslConnection *gsc,
+				 GaimInputCondition 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;
+		
+		gaim_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){
+		gaim_debug_info("MaYuan","faultcode Node is NULL\n");
+		goto oim_send_process_fail;
+	}
+	faultCodeStr = xmlnode_get_data(faultCodeNode);
+	gaim_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);
+	gaim_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);
+	gaim_debug_info("MaYuan","lockkey:{%s}\n",oim->challenge);
+
+	/*repost the send*/
+	gaim_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, GaimSslConnection *gsc,
+				 GaimInputCondition 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);
+
+	gaim_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, GaimInputCondition 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);
+
+	gaim_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{
+		gaim_debug_info("MaYuan","no lock key challenge,wait for SOAP Fault and Resend\n");
+		buf[0]='\0';
+	}
+	gaim_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, GaimSslConnection *gsc,
+				 GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	gaim_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, GaimInputCondition 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);
+
+	gaim_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(GaimSslConnection *gsc, GaimSslErrorType 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, GaimSslConnection *gsc,
+				 GaimInputCondition 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);
+
+	gaim_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);
+	gaim_debug_info("MaYuan","oim body:{%s}\n",message->body);
+	decode_msg = gaim_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]);
+		gaim_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);
+		gaim_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);
+	gaim_debug_info("MaYuan","oim Date:{%s},passport{%s}\n",date,passport);
+
+	msn_session_report_user(oim->session,passport,decode_msg,GAIM_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);
+	gaim_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, GaimSslConnection *gsc,
+				 GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+	MsnOim * oim = soapconn->session->oim;
+
+	gaim_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, GaimInputCondition 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);
+/*		gaim_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;
+
+	gaim_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)
+{
+	gaim_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)
+{
+	gaim_debug_info("MaYuan","msn oim send connect init...\n");
+	msn_soap_init(sendconn,MSN_OIM_SEND_HOST,1,
+					msn_oim_send_connect_cb,
+					msn_oim_send_error_cb);
+}
+
+/*endof oim.c*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/oim.h	Sun Apr 15 04:29:56 2007 +0000
@@ -0,0 +1,148 @@
+/**
+ * @file oim.h			Header file for oim.c
+ *	Author
+ * 		MaYuan<mayuan2006@gmail.com>
+ * gaim
+ *
+ * Gaim 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	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/servconn.c	Sun Apr 15 04:29:56 2007 +0000
@@ -240,7 +240,9 @@
 	{
 		servconn->processing = TRUE;
 		return TRUE;
-	}else{
+	}
+	else
+	{
 		return FALSE;
 	}
 }
@@ -435,10 +437,13 @@
 
 		servconn->rx_len -= cur_len;
 
-		if (servconn->payload_len){
+		if (servconn->payload_len)
+		{
 			msn_cmdproc_process_payload(servconn->cmdproc, cur, cur_len);
 			servconn->payload_len = 0;
-		}else{
+		}
+		else
+		{
 			msn_cmdproc_process_cmd_text(servconn->cmdproc, cur);
 			servconn->payload_len = servconn->cmdproc->last_cmd->payload_len;
 		}
--- a/libpurple/protocols/msn/session.c	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/session.c	Sun Apr 15 04:29:56 2007 +0000
@@ -270,7 +270,11 @@
 
 	g_return_if_fail(gc != NULL);
 
-	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next){
+	/* 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) {
@@ -314,7 +318,7 @@
 	 * 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){
+	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))
@@ -331,14 +335,14 @@
 				if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 				b = (PurpleBuddy *)bnode;
-				if(b->account == gc->account){
+				if(purple_buddy_get_account(b) == purple_connection_get_account(gc)) {
 					MsnUser *remote_user;
 					gboolean found = FALSE;
 
-					purple_debug_info("MaYuan","buddy name:%s,group name:%s\n",b->name,group_name);
-					remote_user = msn_userlist_find_user(session->userlist, b->name);
+					remote_user = msn_userlist_find_user(session->userlist, purple_buddy_get_name(b));
 
-					if ((remote_user != NULL) && (remote_user->list_op & MSN_LIST_FL_OP)){
+					if ((remote_user != NULL) && (remote_user->list_op & MSN_LIST_FL_OP))
+					{
 						const char *group_id;
 						GList *l;
 
@@ -350,16 +354,19 @@
 						}
 						purple_debug_info("MaYuan","group_id:{%s}\n",group_id);
 
-						for (l = remote_user->group_ids; l != NULL; l = l->next){
+						for (l = remote_user->group_ids; l != NULL; l = l->next)
+						{
 							purple_debug_info("MaYuan","l->data:{%s}\n",l->data);
-							if (!g_strcasecmp(group_id ,l->data)){
+							if (!g_strcasecmp(group_id ,l->data))
+							{
 								found = TRUE;
 								break;
 							}
 						}
 					}
 
-					if (!found){
+					if (!found)
+					{
 						/* The user was not on the server list or not in that group
 						 * on the server list */
 						msn_show_sync_issue(session, b->name, group_name);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/soap.c	Sun Apr 15 04:29:56 2007 +0000
@@ -0,0 +1,593 @@
+/**
+ * @file soap.c 
+ * 	SOAP connection related process
+ *	Author
+ * 		MaYuan<mayuan2006@gmail.com>
+ * gaim
+ *
+ * Gaim 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, GaimSslConnection *gsc,
+				 GaimInputCondition cond)
+{
+	MsnSoapConn * soapconn;
+	MsnSession *session;
+
+	gaim_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(GaimSslConnection *gsc, GaimSslErrorType error, void *data)
+{	
+	MsnSoapConn * soapconn = data;
+
+	g_return_if_fail(data != NULL);
+	gaim_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,
+				GaimSslInputFunction	connect_cb,
+				GaimSslErrorFunction	error_cb)
+{
+	gaim_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){
+		gaim_ssl_connect(soapconn->session->account, soapconn->login_host,
+				GAIM_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){
+			gaim_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){
+		gaim_input_remove(soapconn->output_handler);
+	}
+	/*remove the read handler*/
+	if (soapconn->input_handler > 0){
+		gaim_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 = gaim_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
+	gaim_debug_info("MaYuan","++soap ssl read:{%d}\n",len);
+	gaim_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, GaimInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+	MsnSession *session;
+	int len;
+	char * body_start,*body_len;
+	char *length_start,*length_end;
+
+//	gaim_debug_misc("MaYuan", "soap read cb\n");
+	session = soapconn->session;
+	g_return_if_fail(session != NULL);
+
+	if (soapconn->input_handler == -1){
+		soapconn->input_handler = gaim_input_add(soapconn->gsc->fd,
+			GAIM_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) {
+		gaim_debug_error("msn", "read Error!len:%d\n",len);
+		gaim_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;
+
+		gaim_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);
+
+		gaim_ssl_connect(session->account, soapconn->login_host,
+			GAIM_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;
+
+		gaim_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 = gaim_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 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;
+
+			//	gaim_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
+			gaim_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*/
+			gaim_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, GaimInputCondition cond)
+{
+	MsnSoapConn *soapconn = data;
+	int len, total_len;
+
+	g_return_if_fail(soapconn != NULL);
+	if(soapconn->write_buf == NULL){
+		gaim_debug_error("MaYuan","soap buffer is NULL\n");
+		gaim_input_remove(soapconn->output_handler);
+		soapconn->output_handler = -1;
+		return;
+	}
+	total_len = strlen(soapconn->write_buf);
+
+	/* 
+	 * write the content to SSL server,
+	 */
+	len = gaim_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!*/
+		gaim_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;
+
+	gaim_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, GaimInputFunction 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 = gaim_input_add(soapconn->gsc->fd, GAIM_INPUT_WRITE,
+													msn_soap_write_cb, soapconn);
+	msn_soap_write_cb(soapconn, soapconn->gsc->fd, GAIM_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,
+				GaimInputFunction read_cb,GaimInputFunction 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*/
+		gaim_debug_info("Ma Yuan","soap is not connected!\n");
+		msn_soap_init_func(soapconn);
+		msn_soap_connect(soapconn);
+		return;
+	}
+	gaim_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;
+
+	gaim_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
+	gaim_debug_info("MaYuan","send to  server{%s}\n",request_str);
+#endif
+
+	/*free read buffer*/
+	msn_soap_free_read_buf(soapconn);
+	/*post it to server*/
+	msn_soap_write(soapconn,request_str,request->written_cb);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/msn/soap.h	Sun Apr 15 04:29:56 2007 +0000
@@ -0,0 +1,145 @@
+/**
+ * @file soap.h
+ * 	header file for SOAP connection related process
+ *	Author
+ * 		MaYuan<mayuan2006@gmail.com>
+ * gaim
+ *
+ * Gaim 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;
+
+	GaimInputFunction read_cb;
+	GaimInputFunction 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*/
+	GaimSslConnection *gsc;
+	/*ssl connection callback*/
+	GaimSslInputFunction	connect_cb;
+	/*ssl error callback*/
+	GaimSslErrorFunction	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;
+	GaimInputFunction written_cb;
+
+	/*read buffer*/
+	char *read_buf;
+	gsize read_len;
+	gsize need_to_read;
+	GaimInputFunction 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,
+								 GaimInputFunction read_cb,
+								 GaimInputFunction 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,GaimSslInputFunction connect_cb,GaimSslErrorFunction 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, GaimInputFunction 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, GaimSslConnection *gsc, GaimInputCondition cond);
+void msn_soap_read_cb(gpointer data, gint source, GaimInputCondition 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/switchboard.c	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Sun Apr 15 04:29:56 2007 +0000
@@ -968,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);
 }
 
--- a/libpurple/protocols/msn/sync.c	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/sync.c	Sun Apr 15 04:29:56 2007 +0000
@@ -35,7 +35,8 @@
 
 	list_name = cmd->params[0];
 
-	if (!g_ascii_strcasecmp(list_name, "AL")){
+	if (!g_ascii_strcasecmp(list_name, "AL"))
+	{
 		/*
 		 * If the current setting is AL, messages from users who
 		 * are not in BL will be delivered.
@@ -43,7 +44,9 @@
 		 * In other words, deny some.
 		 */
 		gc->account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
-	}else{
+	}
+	else
+	{
 		/* If the current setting is BL, only messages from people
 		 * who are in the AL will be delivered.
 		 *
@@ -86,7 +89,8 @@
 lsg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	MsnSession *session = cmdproc->session;
-	const char *name, *group_id;
+	const char *name;
+	const char *group_id;
 
 	group_id = cmd->params[0];
 	name = purple_url_decode(cmd->params[1]);
--- a/libpurple/protocols/msn/user.c	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Sun Apr 15 04:29:56 2007 +0000
@@ -222,16 +222,20 @@
 
 	g_return_if_fail(user != NULL);
 
-	if (filename == NULL || g_stat(filename, &st) == -1){
+	if (filename == NULL || g_stat(filename, &st) == -1)
+	{
 		msn_user_set_object(user, NULL);
-	}else if ((fp = g_fopen(filename, "rb")) != NULL){
+	}
+	else if ((fp = g_fopen(filename, "rb")) != NULL)
+	{
 		PurpleCipherContext *ctx;
 		char *buf;
 		gsize len;
 		char *base64;
 		unsigned char digest[20];
 
-		if (msnobj == NULL)	{
+		if (msnobj == NULL)
+		{
 			msnobj = msn_object_new();
 			msn_object_set_local(msnobj);
 			msn_object_set_type(msnobj, MSN_OBJECT_USERTILE);
@@ -283,7 +287,9 @@
 		base64 = purple_base64_encode(digest, sizeof(digest));
 		msn_object_set_sha1c(msnobj, base64);
 		g_free(base64);
-	}else{
+	}
+	else
+	{
 		purple_debug_error("msn", "Unable to open buddy icon %s!\n", filename);
 		msn_user_set_object(user, NULL);
 	}
@@ -317,13 +323,15 @@
 
 	g = purple_find_group(group_name);
 
-	if ((id == NULL) && (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){
+	if (b == NULL)
+	{
 		b = purple_buddy_new(account, passport, NULL);
 		purple_blist_add_buddy(b, NULL, g, NULL);
 	}
--- a/libpurple/protocols/msn/userlist.c	Sun Apr 15 04:12:27 2007 +0000
+++ b/libpurple/protocols/msn/userlist.c	Sun Apr 15 04:29:56 2007 +0000
@@ -219,14 +219,22 @@
 		if (group_id != NULL)
 		{
 			msn_user_add_group_id(user, group_id);
-		}else{
+		}
+		else
+		{
 			/* session->sync->fl_users_count++; */
 		}
-	}else if (list_id == MSN_LIST_AL){
+	}
+	else if (list_id == MSN_LIST_AL)
+	{
 		purple_privacy_permit_add(account, passport, TRUE);
-	}else if (list_id == MSN_LIST_BL){
+	}
+	else if (list_id == MSN_LIST_BL)
+	{
 		purple_privacy_deny_add(account, passport, TRUE);
-	}else if (list_id == MSN_LIST_RL){
+	}
+	else if (list_id == MSN_LIST_RL)
+	{
 		PurpleConnection *gc;
 		PurpleConversation *convo;
 
@@ -250,7 +258,8 @@
  			g_free(msg);
  		}
  
-		if (!(user->list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP))){
+		if (!(user->list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
+		{
 			/*
 			 * TODO: The friendly name was NULL for me when I
 			 *       looked at this.  Maybe we should use the store
@@ -275,19 +284,29 @@
 
 	passport = msn_user_get_passport(user);
 
-	if (list_id == MSN_LIST_FL){
+	if (list_id == MSN_LIST_FL)
+	{
 		/* TODO: When is the user totally removed? */
-		if (group_id != NULL){
+		if (group_id >= 0)
+		{
 			msn_user_remove_group_id(user, group_id);
 			return;
-		}else{
+		}
+		else
+		{
 			/* session->sync->fl_users_count--; */
 		}
-	}else if (list_id == MSN_LIST_AL){
+	}
+	else if (list_id == MSN_LIST_AL)
+	{
 		purple_privacy_permit_remove(account, passport, TRUE);
-	}else if (list_id == MSN_LIST_BL){
+	}
+	else if (list_id == MSN_LIST_BL)
+	{
 		purple_privacy_deny_remove(account, passport, TRUE);
-	}else if (list_id == MSN_LIST_RL){
+	}
+	else if (list_id == MSN_LIST_RL)
+	{
 		PurpleConversation *convo;
 
 		purple_debug_info("msn",
@@ -312,7 +331,8 @@
 	user->list_op &= ~(1 << list_id);
 	/* purple_user_remove_list_id (user, list_id); */
 
-	if (user->list_op == 0){
+	if (user->list_op == 0)
+	{
 		purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n",
 						passport);
 	}
@@ -333,7 +353,8 @@
 	passport = msn_user_get_passport(user);
 	store = msn_user_get_store_name(user);
 
-	if (list_op & MSN_LIST_FL_OP){
+	if (list_op & MSN_LIST_FL_OP)
+	{
 		GSList *c;
 		for (c = group_ids; c != NULL; c = g_slist_next(c))	{
 			char *group_id;
@@ -346,19 +367,22 @@
 		serv_got_alias(gc, passport, store);
 	}
 
-	if (list_op & MSN_LIST_AL_OP){
+	if (list_op & MSN_LIST_AL_OP)
+	{
 		/* These are users who are allowed to see our status. */
 		purple_privacy_deny_remove(account, passport, TRUE);
 		purple_privacy_permit_add(account, passport, TRUE);
 	}
 
-	if (list_op & MSN_LIST_BL_OP){
+	if (list_op & MSN_LIST_BL_OP)
+	{
 		/* These are users who are not allowed to see our status. */
 		purple_privacy_permit_remove(account, passport, TRUE);
 		purple_privacy_deny_add(account, passport, TRUE);
 	}
 
-	if (list_op & MSN_LIST_RL_OP){
+	if (list_op & MSN_LIST_RL_OP)
+	{
 		/* These are users who have us on their buddy list. */
 		/*
 		 * TODO: What is store name set to when this happens?
@@ -367,8 +391,9 @@
 		 *       should use the friendly name, instead? --KingAnt
 		 */
 
-		if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP))){
-//			got_new_entry(gc, passport, store);
+		if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
+		{
+			got_new_entry(gc, passport, store);
 		}
 	}
 
@@ -403,13 +428,15 @@
 	GList *l;
 
 	/*destroy userlist*/
-	for (l = userlist->users; l != NULL; l = l->next){
+	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){
+	for (l = userlist->groups; l != NULL; l = l->next)
+	{
 		msn_group_destroy(l->data);
 	}
 	g_list_free(userlist->groups);
@@ -428,7 +455,8 @@
 	MsnUser *user;
 
 	user = msn_userlist_find_user(userlist, passport);
-	if (user == NULL){
+	if (user == NULL)
+	{
 		user = msn_user_new(userlist, passport, userName);
 		msn_userlist_add_user(userlist, user);
 	}
@@ -455,7 +483,8 @@
 
 	g_return_val_if_fail(passport != NULL, NULL);
 
-	for (l = userlist->users; l != NULL; l = l->next){
+	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);
@@ -507,7 +536,8 @@
 	g_return_val_if_fail(userlist != NULL, NULL);
 	g_return_val_if_fail(name     != 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->name != NULL) && !g_strcasecmp(name, group->name))
@@ -524,11 +554,10 @@
 
 	group = msn_userlist_find_group_with_name(userlist, group_name);
 
-	if (group != NULL){
+	if (group != NULL)
 		return msn_group_get_id(group);
-	}else{
+	else
 		return NULL;
-	}
 }
 
 const char *
@@ -538,11 +567,10 @@
 
 	group = msn_userlist_find_group_with_id(userlist, group_id);
 
-	if (group != NULL){
+	if (group != NULL)
 		return msn_group_get_name(group);
-	}else{
+	else
 		return NULL;
-	}
 }
 
 void
@@ -599,7 +627,8 @@
 	}
 
 	/* First we're going to check if not there. */
-	if (!(user_is_there(user, list_id, group_id))){
+	if (!(user_is_there(user, list_id, group_id)))
+	{
 		list = lists[list_id];
 		purple_debug_error("msn", "User '%s' is not there: %s\n",
 						 who, list);
@@ -626,7 +655,8 @@
 	purple_debug_info("MaYuan", "userlist add buddy,name:{%s},group:{%s}\n",who ,group_name);
 	group_id = NULL;
 
-	if (!purple_email_is_valid(who)){
+	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 */
@@ -640,10 +670,12 @@
 		return;
 	}
 
-	if (group_name != NULL){
+	if (group_name != NULL)
+	{
 		group_id = msn_userlist_find_group_id(userlist, group_name);
 
-		if (group_id == NULL){
+		if (group_id == NULL)
+		{
 			/* Whoa, we must add that group first. */
 			msn_request_add_group(userlist, who, NULL, group_name);
 			return;
@@ -653,7 +685,8 @@
 	user = msn_userlist_find_user(userlist, who);
 
 	/* First we're going to check if it's already there. */
-	if (user_is_there(user, list_id, group_id)){
+	if (user_is_there(user, list_id, group_id))
+	{
 		list = lists[list_id];
 		purple_debug_error("msn", "User '%s' is already there: %s\n", who, list);
 		return;
@@ -666,10 +699,8 @@
 
 	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);
-#if 1
 	msn_notification_add_buddy(userlist->session->notification, list, who,
 							   store_name, group_id);
-#endif
 }
 
 void
@@ -680,7 +711,8 @@
 
 	new_group_id = msn_userlist_find_group_id(userlist, new_group_name);
 
-	if (new_group_id == NULL){
+	if (new_group_id == NULL)
+	{
 		msn_request_add_group(userlist, who, old_group_name, new_group_name);
 		return;
 	}
@@ -700,18 +732,22 @@
 
 	g_return_if_fail(gc != NULL);
 
-	for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next){
-		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+	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))
+		for (cnode = gnode->child; cnode; cnode = cnode->next)
+		{
+			if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
-			for(bnode = cnode->child; bnode; bnode = bnode->next) {
+			for (bnode = cnode->child; bnode; bnode = bnode->next)
+			{
 				PurpleBuddy *b;
-				if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+				if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
 					continue;
 				b = (PurpleBuddy *)bnode;
-				if(b->account == gc->account){
+				if (b->account == gc->account)
+				{
 					user = msn_userlist_find_add_user(session->userlist,
 						b->name,NULL);
 					b->proto_data = user;
@@ -720,12 +756,14 @@
 			}
 		}
 	}
-	for (l = session->account->permit; l != NULL; l = l->next) {
+	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) {
+	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);