view libpurple/protocols/msn/contact.c @ 20477:9a2a4a0c0003

Add the possibility to create an Address Book, useful for newly registered MSN users. When changing friendly name, send the new one to the SOAP server in the PRP msn command callback, with escaped html entity chars. Fixes #1294 . Handle EBADF error sometimes received in SOAP server read callback (observed in win32). Misc cleanups.
author Carlos Silva <typ0@pidgin.im>
date Tue, 07 Aug 2007 02:37:58 +0000
parents 530a92d50c5e
children 6a8463be5b23
line wrap: on
line source

/**
 * @file contact.c 
 * 	get MSN contacts via SOAP request
 *	created by MaYuan<mayuan2006@gmail.com>
 *
 * purple
 *
 * Purple is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "msn.h"
#include "soap.h"
#include "contact.h"
#include "xmlnode.h"
#include "group.h"


/*new a contact*/
MsnContact *
msn_contact_new(MsnSession *session)
{
	MsnContact *contact;

	contact = g_new0(MsnContact, 1);
	contact->session = session;
	contact->soapconn = msn_soap_new(session,contact,1);

	return contact;
}

/*destroy the contact*/
void
msn_contact_destroy(MsnContact *contact)
{
	msn_soap_destroy(contact->soapconn);
	g_free(contact);
}

/*contact SOAP server login error*/
static void
msn_contact_login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
{
	MsnSoapConn *soapconn = data;
	MsnSession *session;

	session = soapconn->session;
	g_return_if_fail(session != NULL);

	msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to contact server"));
}

/*msn contact SOAP server connect process*/
static void
msn_contact_login_connect_cb(gpointer data, PurpleSslConnection *gsc,
				 PurpleInputCondition cond)
{
	MsnSoapConn *soapconn = data;
	MsnSession * session;
	MsnContact *contact;

	contact = soapconn->parent;
	g_return_if_fail(contact != NULL);

	session = contact->session;
	g_return_if_fail(session != NULL);

	/*login ok!We can retrieve the contact list*/
//	msn_get_contact_list(contact,NULL);
}

/*get MSN member role utility*/
static int
msn_get_memberrole(char * role)
{
	
	purple_debug_info("::","msn_get_memberrole()\n");

	if(!strcmp(role,"Allow")){
		return MSN_LIST_AL_OP;
	}else if(!strcmp(role,"Block")){
		return MSN_LIST_BL_OP;
	}else if(!strcmp(role,"Reverse")){
		return MSN_LIST_RL_OP;
	}else if(!strcmp(role,"Pending")){
		return MSN_LIST_PL_OP;
	}
	return 0;
}

/*get User Type*/
static int 
msn_get_user_type(char * type)
{
	if(!strcmp(type,"Regular")){
		return MSN_USER_TYPE_PASSPORT;
	}
	if(!strcmp(type,"Live")){
		return MSN_USER_TYPE_PASSPORT;
	}
	if(!strcmp(type,"LivePending")){
		return MSN_USER_TYPE_PASSPORT;
	}

	return MSN_USER_TYPE_UNKNOWN;
}

/* Create the AddressBook in the server, if we don't have one */
static void
msn_create_address_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn *soapconn = data;
	MsnContact *contact;

	contact = soapconn->parent;
	g_return_if_fail(contact != NULL);

	if ( g_strstr_len(soapconn->read_buf, soapconn->read_len, "HTTP/1.1 200") ) {
		purple_debug_info("MSN AddressBook", "Address Book successfully created!\n");
		msn_get_address_book(contact, NULL, NULL);
	}
	else {
		 purple_debug_warning("MSN AddressBook", "Failed to create the Address Book!\n");
	}

	msn_soap_free_read_buf(soapconn);
	return;
}

static void
msn_create_address_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;

	purple_debug_info("MSN AddressBook","AddressBookAdd written\n");
	soapconn->read_cb = msn_create_address_cb;

	return;
}

static void
msn_create_address_book(MsnContact * contact)
{
	MsnSoapReq *soap_request;
	gchar *body;

	purple_debug_info("MSN AddressBook","Creating an Address Book.\n");

	body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport);

	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
					MSN_ADDRESS_BOOK_POST_URL,MSN_ADD_ADDRESSBOOK_SOAP_ACTION,
					body,
					msn_create_address_cb,
					msn_create_address_written_cb);
	msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);

	g_free(body);
	
	return;
}

/*parse contact list*/
static void
msn_parse_contact_list(MsnContact * contact)
{
	MsnSession * session;
	int list_op = 0;
	char * passport, *debugdata, *typedata;
	xmlnode *fault, *faultstringnode, *faultdetail, *errorcode;
	xmlnode *node, *body, *response, *result, *services;
	xmlnode *service, *memberships, *info, *handle, *handletype;
	xmlnode *LastChangeNode;
	xmlnode *membershipnode, *members, *member, *passportNode;
	char *LastChangeStr;

	purple_debug_info("::","msn_parse_contact_list()\n");

	session = contact->session;
	node = xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len);

	if (node == NULL) {
		purple_debug_error("MSNCL","Unable to parse SOAP data!\n");
		return;
	}

#ifdef MSN_SOAP_DEBUG
	debugdata = xmlnode_to_formatted_str(node, NULL);
	purple_debug_info("MSNCL","Received contact list, parsing:\n%s", debugdata);
	g_free(debugdata);
#endif


	purple_debug_info("MSNCL","Root node @ %p: Name: '%s', child: '%s', lastchild: '%s'\n",node,node->name,node->child->name,node->lastchild->name);
	body = xmlnode_get_child(node,"Body");

	if (body == NULL) {
		purple_debug_warning("MSNCL", "Failed to parse contact list Body node\n");
		xmlnode_free(node);
		return;
	}
	purple_debug_info("MSNCL","Body @ %p:  Name: '%s'\n",body,body->name);

	/* Did we receive a <Fault> ? */
	if ( (fault = xmlnode_get_child(body, "Fault")) != NULL) {
	        purple_debug_info("MSNCL","Fault received from SOAP server!\n");

		if ( (faultstringnode = xmlnode_get_child(fault, "faultstring")) != NULL ) {
			gchar * faultstring = xmlnode_get_data(faultstring);
			purple_debug_info("MSNCL","Faultstring: %s\n", faultstring);
			g_free(faultstring);
		}
		if ( (faultdetail = xmlnode_get_child(fault, "detail")) != NULL ) {
			purple_debug_info("MSNCL","detail @ %p, name: %s\n",faultdetail, faultdetail->name);

			if ( (errorcode = xmlnode_get_child(faultdetail, "errorcode")) != NULL ) {
				purple_debug_info("MSNCL","errorcode @ %p, name: %s\n",errorcode, errorcode->name);

				if (errorcode->child != NULL) {
					gchar *errorcodestring = xmlnode_get_data(errorcode);
					purple_debug_info("MSNCL", "Error Code: %s\n", errorcodestring);

					if ( !strncmp(errorcodestring, "ABDoesNotExist", 14) ) {
						xmlnode_free(node);
						g_free(errorcodestring);
						msn_create_address_book(contact);
						return;
					}
					g_free(errorcodestring);
				}
			}
		}
		xmlnode_free(node);
		msn_get_contact_list(contact, NULL);
		return;
	}

	response = xmlnode_get_child(body,"FindMembershipResponse");

	if (response == NULL) {
		/* we may get a response if our cache data is too old:
		 *
		 * <faultstring>Need to do full sync. Can't sync deltas Client
		 * has too old a copy for us to do a delta sync</faultstring>
		 */
		xmlnode_free(node);
		msn_get_contact_list(contact, NULL);
		return;
	}
	purple_debug_info("MSNCL","FindMembershipResponse @ %p: Name: '%s'\n",response,response->name);

	result = xmlnode_get_child(response,"FindMembershipResult");
	if (result == NULL) {
		purple_debug_warning("MSNCL","Received No Update!\n");
		xmlnode_free(node);
		return;
	}
	purple_debug_info("MSNCL","Result @ %p: Name: '%s'\n", result, result->name);

	if ( (services = xmlnode_get_child(result,"Services")) == NULL) {
		purple_debug_misc("MSNCL","No <Services> received.\n");
		xmlnode_free(node);
		return;
	}

	purple_debug_info("MSNCL","Services @ %p\n",services);
	
	for (service = xmlnode_get_child(services, "Service"); service;
	                                service = xmlnode_get_next_twin(service)) {
		purple_debug_info("MSNCL","Service @ %p\n",service);
	
		if ( (info = xmlnode_get_child(service,"Info")) == NULL ) {
			purple_debug_error("MSNCL","Error getting 'Info' child node\n");
			continue;
		}
		if ( (handle = xmlnode_get_child(info,"Handle")) == NULL ) {
			purple_debug_error("MSNCL","Error getting 'Handle' child node\n");
			continue;
		}
		if ( (handletype = xmlnode_get_child(handle,"Type")) == NULL ) {
			purple_debug_error("MSNCL","Error getting 'Type' child node\n");
			continue;
		}

		if ( (typedata = xmlnode_get_data(handletype)) == NULL) {
			purple_debug_error("MSNCL","Error retrieving data from 'Type' child node\n");
			continue;
		}

		purple_debug_info("MSNCL","processing '%s' Service\n", typedata);

		if ( !g_strcasecmp(typedata, "Profile") ) {
			/* Process Windows Live 'Messenger Roaming Identity' */
			g_free(typedata);
			continue;
		}

		if ( !g_strcasecmp(typedata, "Messenger") ) {

			/*Last Change Node*/
			LastChangeNode = xmlnode_get_child(service, "LastChange");
			LastChangeStr = xmlnode_get_data(LastChangeNode);
			purple_debug_info("MSNCL","LastChangeNode: '%s'\n",LastChangeStr);	
			purple_account_set_string(session->account, "CLLastChange", LastChangeStr);
			g_free(LastChangeStr);

			memberships = xmlnode_get_child(service,"Memberships");
			if (memberships == NULL) {
				purple_debug_warning("MSNCL","Memberships = NULL, cleaning up and returning.\n");
				g_free(typedata);
				xmlnode_free(node);
				return;
			}
			purple_debug_info("MSNCL","Memberships @ %p: Name: '%s'\n",memberships,memberships->name);
			for (membershipnode = xmlnode_get_child(memberships, "Membership"); membershipnode;
							membershipnode = xmlnode_get_next_twin(membershipnode)){
				xmlnode *roleNode;
				char *role;

				roleNode = xmlnode_get_child(membershipnode,"MemberRole");
				role = xmlnode_get_data(roleNode);
				list_op = msn_get_memberrole(role);
				purple_debug_info("MSNCL","MemberRole role: %s, list_op: %d\n",role,list_op);
				
				g_free(role);
				
				members = xmlnode_get_child(membershipnode,"Members");
				for (member = xmlnode_get_child(members, "Member"); member;
						member = xmlnode_get_next_twin(member)){
					MsnUser *user;
					xmlnode * typeNode;
					char * type;

					purple_debug_info("MSNCL","Member type: %s\n", xmlnode_get_attrib(member,"type"));
					
					if( !g_strcasecmp(xmlnode_get_attrib(member,"type"), "PassportMember") ) {
						passportNode = xmlnode_get_child(member,"PassportName");
						passport = xmlnode_get_data(passportNode);
						typeNode = xmlnode_get_child(member,"Type");
						type = xmlnode_get_data(typeNode);
						purple_debug_info("MSNCL","Passport name: '%s', Type: %s\n",passport,type);
						g_free(type);

						user = msn_userlist_find_add_user(session->userlist,passport,NULL);
						msn_got_lst_user(session, user, list_op, NULL);
						g_free(passport);
					}
					
					if (!g_strcasecmp(xmlnode_get_attrib(member,"type"),"PhoneMember")) {
					}
					
					if (!g_strcasecmp(xmlnode_get_attrib(member,"type"),"EmailMember")) {
						xmlnode *emailNode;

						emailNode = xmlnode_get_child(member,"Email");
						passport = xmlnode_get_data(emailNode);
						purple_debug_info("MSNCL","Email Member: Name: '%s', list_op: %d\n", passport, list_op);
						user = msn_userlist_find_add_user(session->userlist, passport, NULL);
						msn_got_lst_user(session, user, list_op, NULL);
						g_free(passport);
					}
				}
			}
			g_free(typedata);	/* Free 'Type' node data after processing 'Messenger' Service */
		}
	}

	xmlnode_free(node);	/* Free the whole XML tree */
}

static void
msn_get_contact_list_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn *soapconn = data;	
	MsnContact *contact;
	MsnSession *session;
	const char *abLastChange;
	const char *dynamicItemLastChange;

	purple_debug_info("::","msn_get_contact_list_cb()\n");

	contact = soapconn->parent;
	g_return_if_fail(contact != NULL);
	session = soapconn->session;
	g_return_if_fail(session != NULL);

#ifdef  MSN_SOAP_DEBUG
	purple_debug_info("MSNCL", "SOAP server reply: \n%s\n", soapconn->read_buf);
#endif
	msn_parse_contact_list(contact);
	/*free the read buffer*/
	msn_soap_free_read_buf(soapconn);

	abLastChange = purple_account_get_string(session->account, "ablastChange", NULL);
	dynamicItemLastChange = purple_account_get_string(session->account, "dynamicItemLastChange", NULL);

#ifdef MSN_PARTIAL_LISTS
	/* XXX: this should be enabled when we can correctly do partial
	   syncs with the server. Currently we need to retrieve the whole
	   list to detect sync issues */
	msn_get_address_book(contact, abLastChange, dynamicItemLastChange);
#else
	msn_get_address_book(contact, NULL, NULL);
#endif
	msn_soap_free_read_buf(soapconn);
}

static void
msn_get_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	

	purple_debug_info("MSNP14","finish contact written\n");
	soapconn->read_cb = msn_get_contact_list_cb;
//	msn_soap_read_cb(data,source,cond);
}

/*SOAP  get contact list*/
void
msn_get_contact_list(MsnContact * contact, const char *update_time)
{
	MsnSoapReq *soap_request;
	char *body = NULL;
	char * update_str;

	purple_debug_info("::","msn_get_contact_list()\n");

	purple_debug_info("MSNP14","Getting Contact List.\n");
	if ( update_time != NULL ) {
		purple_debug_info("MSNCL","last update time:{%s}\n",update_time);
		update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML,update_time);
	} else {
		update_str = g_strdup("");
	}
	body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, update_str);
	g_free(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(body);
}

static void
msn_parse_addressbook_groups(MsnContact *contact, xmlnode *node)
{
	MsnSession *session = contact->session;
	xmlnode *group;

	purple_debug_info("::","msn_parse_addressbook_groups()\n");

	for(group = xmlnode_get_child(node, "Group"); group;
					group = xmlnode_get_next_twin(group)){
		xmlnode *groupId, *groupInfo, *groupname;
		char *group_id, *group_name;

		groupId = xmlnode_get_child(group,"groupId");
		group_id = xmlnode_get_data(groupId);
		groupInfo = xmlnode_get_child(group,"groupInfo");
		groupname = xmlnode_get_child(groupInfo,"name");
		group_name = xmlnode_get_data(groupname);

		msn_group_new(session->userlist, group_id, group_name);

		if (group_id == NULL){
			/* Group of ungroupped buddies */
			g_free(group_name);
			continue;
		}

		purple_debug_info("MsnAB","group_id: %s, name: %s\n",group_id,group_name);
		if ((purple_find_group(group_name)) == NULL){
			PurpleGroup *g = purple_group_new(group_name);
			purple_blist_add_group(g, NULL);
		}
		g_free(group_id);
		g_free(group_name);
	}
}

static void
msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
{
	MsnSession *session = contact->session;
	xmlnode *contactNode;

	for(contactNode = xmlnode_get_child(node, "Contact"); contactNode;
				contactNode = xmlnode_get_next_twin(contactNode)){
		xmlnode *contactId,*contactInfo,*contactType,*passportName,*displayName,*guid;
		xmlnode *groupIds;
		MsnUser *user;
		MsnUserType usertype;
		char *passport,*Name,*uid,*type;

		passport = NULL;

		contactId= xmlnode_get_child(contactNode,"contactId");
		uid = xmlnode_get_data(contactId);

		contactInfo = xmlnode_get_child(contactNode,"contactInfo");
		contactType = xmlnode_get_child(contactInfo,"contactType");
		type = xmlnode_get_data(contactType);

		/*setup the Display Name*/
		if (!strcmp(type, "Me")){
			char *friendly;
			friendly = xmlnode_get_data(xmlnode_get_child(contactInfo, "displayName"));
			purple_connection_set_display_name(session->account->gc, purple_url_decode(friendly));
			g_free(friendly);
			g_free(uid);
			g_free(type);
			continue; /* Not adding own account as buddy to buddylist */
		}
		usertype = msn_get_user_type(type);
		passportName = xmlnode_get_child(contactInfo,"passportName");
		if (passportName == NULL) {
			xmlnode *emailsNode, *contactEmailNode, *emailNode;
			xmlnode *messengerEnabledNode;
			char *msnEnabled;

			/*TODO: add it to the none-instant Messenger group and recognize as email Membership*/
			/*Yahoo User?*/
			emailsNode = xmlnode_get_child(contactInfo,"emails");
			if (emailsNode == NULL) {
				/*TODO:  need to support the Mobile type*/
				g_free(uid);
				g_free(type);
				continue;
			}
			for(contactEmailNode = xmlnode_get_child(emailsNode,"ContactEmail");contactEmailNode;
					contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){
				messengerEnabledNode = xmlnode_get_child(contactEmailNode,"isMessengerEnabled");
				if(messengerEnabledNode == NULL){
					break;
				}
				msnEnabled = xmlnode_get_data(messengerEnabledNode);
				if(!strcmp(msnEnabled,"true")){
					/*Messenger enabled, Get the Passport*/
					emailNode = xmlnode_get_child(contactEmailNode,"email");
					passport = xmlnode_get_data(emailNode);
					purple_debug_info("MsnAB","Yahoo User %s\n",passport);
					usertype = MSN_USER_TYPE_YAHOO;
					break;
				}else{
					/*TODO maybe we can just ignore it in Purple?*/
					emailNode = xmlnode_get_child(contactEmailNode,"email");
					passport = xmlnode_get_data(emailNode);
					purple_debug_info("MSNAB","Other type user\n");
				}
				g_free(msnEnabled);
			}
		} else {
			passport = xmlnode_get_data(passportName);
		}

		if (passport == NULL) {
			g_free(uid);
			g_free(type);
			continue;
		}

		displayName = xmlnode_get_child(contactInfo,"displayName");
		if (displayName == NULL) {
			Name = g_strdup(passport);
		} else {
			Name =xmlnode_get_data(displayName);
		}

		purple_debug_misc("MsnAB","passport:{%s} uid:{%s} display:{%s}\n",
						passport,uid,Name);

		user = msn_userlist_find_add_user(session->userlist, passport,Name);
		msn_user_set_uid(user,uid);
		msn_user_set_type(user, usertype);
		g_free(Name);
		g_free(passport);
		g_free(uid);
		g_free(type);

		purple_debug_misc("MsnAB","parse guid...\n");
		groupIds = xmlnode_get_child(contactInfo,"groupIds");
		if (groupIds) {
			for (guid = xmlnode_get_child(groupIds, "guid");guid;
							guid = xmlnode_get_next_twin(guid)){
				char *group_id;
				group_id = xmlnode_get_data(guid);
				msn_user_add_group_id(user,group_id);
				purple_debug_misc("MsnAB","guid:%s\n",group_id);
				g_free(group_id);
			}
		} else {
			/*not in any group,Then set default group*/
			msn_user_add_group_id(user, MSN_INDIVIDUALS_GROUP_ID);
		}

		msn_got_lst_user(session, user, MSN_LIST_FL_OP, NULL);
	}
}

static gboolean
msn_parse_addressbook(MsnContact * contact)
{
	MsnSession * session;
	xmlnode * node,*body,*response,*result;
	xmlnode *groups;
	xmlnode	*contacts;
	xmlnode *abNode;
	xmlnode *fault, *faultstringnode, *faultdetail, *errorcode;
	gchar *printabledata;

	session = contact->session;

	

	node = xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len);
	if ( node == NULL ) {
		purple_debug_error("MSN AddressBook","Error parsing received Address Book with size %d:\n \"%s\"\n", contact->soapconn->body_len, contact->soapconn->body);
		return FALSE;
	}

	printabledata = xmlnode_to_formatted_str(node, NULL);
	purple_debug_misc("MSN AddressBook","Received Address Book with size %d:\n %s\n", contact->soapconn->body_len, (char *) printabledata);
	g_free(printabledata);

	purple_debug_misc("MSN AddressBook","node{%p},name:%s,child:%s,last:%s\n",node,node->name,node->child->name,node->lastchild->name);
	
	body = xmlnode_get_child(node,"Body");
	purple_debug_misc("MSN AddressBook","body{%p},name:%s\n",body,body->name);
	
	if ( (fault = xmlnode_get_child(body, "Fault")) != NULL) {
		purple_debug_info("MSN AddressBook","Fault received from SOAP server!\n");
		
		if ( (faultstringnode = xmlnode_get_child(fault, "faultstring")) != NULL ) {
			gchar *faultstring = xmlnode_get_data(faultstring);
			purple_debug_info("MSN AddressBook","Faultstring: %s\n", faultstring);
			g_free(faultstring);
		}
		if ( (faultdetail = xmlnode_get_child(fault, "detail")) != NULL ) {
			purple_debug_info("MSN AddressBook","detail @ %p, name: %s\n",faultdetail, faultdetail->name);

			if ( (errorcode = xmlnode_get_child(faultdetail, "errorcode")) != NULL ) {
				gchar *errorcodestring;
				purple_debug_info("MSN AddressBook","errorcode @ %p, name: %s\n",errorcode, errorcode->name);

				errorcodestring = xmlnode_get_data(errorcode);
				purple_debug_info("MSN AddressBook", "Error Code: %s\n", errorcodestring);
						
				if ( !strncmp(errorcodestring, "ABDoesNotExist", 14) ) {
					g_free(errorcodestring);
					return TRUE;
				}
				g_free(errorcodestring);
			}
		}
		return FALSE;
	}


	response = xmlnode_get_child(body,"ABFindAllResponse");

	if (response == NULL) {
		return FALSE;
	}

	purple_debug_misc("MSN SOAP","response{%p},name:%s\n",response,response->name);
	result = xmlnode_get_child(response,"ABFindAllResult");
	if(result == NULL){
		purple_debug_misc("MSNAB","receive no address book update\n");
		return TRUE;
	}
	purple_debug_info("MSN SOAP","result{%p},name:%s\n",result,result->name);

	/*Process Group List*/
	groups = xmlnode_get_child(result,"groups");
	if (groups != NULL) {
		msn_parse_addressbook_groups(contact, groups);
	}

	/*add a default No group to set up the no group Membership*/
	msn_group_new(session->userlist, MSN_INDIVIDUALS_GROUP_ID,
				  MSN_INDIVIDUALS_GROUP_NAME);
	purple_debug_misc("MsnAB","group_id:%s name:%s\n",
					  MSN_INDIVIDUALS_GROUP_ID, MSN_INDIVIDUALS_GROUP_NAME);
	if ((purple_find_group(MSN_INDIVIDUALS_GROUP_NAME)) == NULL){
		PurpleGroup *g = purple_group_new(MSN_INDIVIDUALS_GROUP_NAME);
		purple_blist_add_group(g, NULL);
	}

	/*add a default No group to set up the no group Membership*/
	msn_group_new(session->userlist, MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME);
	purple_debug_misc("MsnAB","group_id:%s name:%s\n", MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME);
	if ((purple_find_group(MSN_NON_IM_GROUP_NAME)) == NULL){
		PurpleGroup *g = purple_group_new(MSN_NON_IM_GROUP_NAME);
		purple_blist_add_group(g, NULL);
	}

	/*Process contact List*/
	purple_debug_info("MSNAB","process contact list...\n");
	contacts =xmlnode_get_child(result,"contacts");
	if (contacts != NULL) {
		msn_parse_addressbook_contacts(contact, contacts);
	}

	abNode =xmlnode_get_child(result,"ab");
	if(abNode != NULL){
		xmlnode *LastChangeNode, *DynamicItemLastChangedNode;
		char *lastchange, *dynamicChange;

		LastChangeNode = xmlnode_get_child(abNode,"lastChange");
		lastchange = xmlnode_get_data(LastChangeNode);
		purple_debug_info("MsnAB"," lastchanged Time:{%s}\n",lastchange);
		purple_account_set_string(session->account, "ablastChange", lastchange);

		DynamicItemLastChangedNode = xmlnode_get_child(abNode,"DynamicItemLastChanged");
		dynamicChange = xmlnode_get_data(DynamicItemLastChangedNode);
		purple_debug_info("MsnAB"," DynamicItemLastChanged :{%s}\n",dynamicChange);
		purple_account_set_string(session->account, "DynamicItemLastChanged", lastchange);
		g_free(dynamicChange);
		g_free(lastchange);
	}

	xmlnode_free(node);
	msn_soap_free_read_buf(contact->soapconn);
	return TRUE;
}

static void
msn_get_address_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	
	MsnContact *contact;
	MsnSession *session;

	contact = soapconn->parent;
	g_return_if_fail(contact != NULL);
	session = soapconn->session;
	g_return_if_fail(session != NULL);

//	purple_debug_misc("msn", "soap contact server Reply: {%s}\n", soapconn->read_buf);
	if ( msn_parse_addressbook(contact) ) {
		msn_soap_free_read_buf(soapconn);

		msn_send_privacy(session->account->gc);
		msn_notification_dump_contact(session);
	} else {
		/* This is making us loop infinitely when we fail to parse the address book,
		  disable for now (we should re-enable when we send timestamps)
		*/
		/*
		msn_get_address_book(contact, NULL, NULL);
		*/
		msn_session_disconnect(session);
		purple_connection_error(session->account->gc, _("Unable to retrieve MSN Address Book"));
	}

	/*free the read buffer*/
	msn_soap_free_read_buf(soapconn);
}

/**/
static void
msn_address_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	

	purple_debug_info("MSNP14","finish contact written\n");
	soapconn->read_cb = msn_get_address_cb;
}

/*get the address book*/
void
msn_get_address_book(MsnContact *contact, const char *LastChanged, const char *dynamicItemLastChange)
{
	MsnSoapReq *soap_request;
	char *body = NULL;
	char *ab_update_str,*update_str;

	purple_debug_info("::","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);
	g_free(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(body);
}

static void
msn_add_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	purple_debug_info("MSNCL","Add contact read done\n");
}

static void
msn_add_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	

	purple_debug_info("MSNCL","Add contact request written\n");
	soapconn->read_cb = msn_add_contact_read_cb;
//	msn_soap_read_cb(data,source,cond);
}

/*add a Contact */
void
msn_add_contact(MsnContact *contact,const char *passport,const char *groupId)
{
	MsnSoapReq *soap_request;
	char *body = NULL;
	char *contact_xml = NULL;
	char *soap_action;

	purple_debug_info("::","msn_add_contact()\n");
	contact_xml = g_strdup_printf(MSN_CONTACT_XML,passport);
	if ( groupId == NULL ) {
		body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE,contact_xml);
		g_free(contact_xml);
		/*build SOAP and POST it*/
		soap_action = g_strdup(MSN_CONTACT_ADD_SOAP_ACTION);
	} else {
		body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE,groupId,contact_xml);
		g_free(contact_xml);
		/*build SOAP and POST it*/
		soap_action = g_strdup(MSN_ADD_CONTACT_GROUP_SOAP_ACTION);
	}
	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
					MSN_ADDRESS_BOOK_POST_URL,soap_action,
					body,
					msn_add_contact_read_cb,
					msn_add_contact_written_cb);
	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);

	g_free(soap_action);
	g_free(body);
}

static void
msn_delete_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	purple_debug_info("MSNCL","Delete contact read done\n");
}

static void
msn_delete_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	

	purple_debug_info("MSNCL","Delete contact request written\n");
	soapconn->read_cb = msn_delete_contact_read_cb;
//	msn_soap_read_cb(data,source,cond);
}

/*delete a Contact*/
void
msn_delete_contact(MsnContact *contact,const char *contactId)
{	
	char *body = NULL;
	char *contact_xml = NULL ;
	MsnSoapReq *soap_request;

	g_return_if_fail(contactId != NULL);
	purple_debug_info("MSNP14","msn delete a contact,contactId:{%s}...\n",contactId);
	contact_xml = g_strdup_printf(MSN_CONTACTS_DEL_XML,contactId);
	body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE,contact_xml);
	g_free(contact_xml);
	/*build SOAP and POST it*/
	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
					MSN_ADDRESS_BOOK_POST_URL,MSN_CONTACT_DEL_SOAP_ACTION,
					body,
					msn_delete_contact_read_cb,
					msn_delete_contact_written_cb);
	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);

	g_free(body);
}

#if 0
static void
msn_update_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	purple_debug_info("MSNP14","update contact read done\n");
}

static void
msn_update_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	

	purple_debug_info("MSNP14","update contact written\n");
	soapconn->read_cb = msn_update_contact_read_cb;
//	msn_soap_read_cb(data,source,cond);
}

/*update a contact's Nickname*/
void
msn_update_contact(MsnContact *contact,const char* nickname)
{
	MsnSoapReq *soap_request;
	char *body = NULL;

	purple_debug_info("MSNP14","msn unblock a contact...\n");

	body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE,nickname);
	/*build SOAP and POST it*/
	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
					MSN_ADDRESS_BOOK_POST_URL,MSN_CONTACT_UPDATE_SOAP_ACTION,
					body,
					msn_update_contact_read_cb,
					msn_update_contact_written_cb);
	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);

	g_free(body);
}
#endif

static void
msn_block_read_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	purple_debug_info("MSNP14","block read done\n");
}

static void
msn_block_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	

	purple_debug_info("MSNP14","finish unblock written\n");
	soapconn->read_cb = msn_block_read_cb;
}

/*block a Contact*/
void
msn_block_contact(MsnContact *contact,const char* membership_id)
{
	MsnSoapReq *soap_request;
	char *body = NULL;

	purple_debug_info("MSNP14","msn block a contact...\n");
	body = g_strdup_printf(MSN_CONTACT_DELECT_FROM_ALLOW_TEMPLATE,membership_id);
	/*build SOAP and POST it*/
	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
					MSN_SHARE_POST_URL,MSN_CONTACT_BLOCK_SOAP_ACTION,
					body,
					msn_block_read_cb,
					msn_block_written_cb);
	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);

	g_free(body);
}

static void
msn_unblock_read_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	purple_debug_info("MSNP14","unblock read done\n");
}

static void
msn_unblock_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	

	purple_debug_info("MSNP14","finish unblock written\n");
	soapconn->read_cb = msn_unblock_read_cb;
}

/*unblock a contact*/
void
msn_unblock_contact(MsnContact *contact,const char* passport)
{
	MsnSoapReq *soap_request;
	char *body = NULL;

	purple_debug_info("MSNP14","msn unblock a contact...\n");

	body = g_strdup_printf(MSN_UNBLOCK_CONTACT_TEMPLATE,passport);
	/*build SOAP and POST it*/
	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
					MSN_SHARE_POST_URL,MSN_CONTACT_UNBLOCK_SOAP_ACTION,
					body,
					msn_unblock_read_cb,
					msn_unblock_written_cb);
	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);

	g_free(body);
}

#if 0
static void
msn_gleams_read_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	purple_debug_info("MSNP14","Gleams read done\n");
}

static void
msn_gleams_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	

	purple_debug_info("MSNP14","finish Group written\n");
	soapconn->read_cb = msn_gleams_read_cb;
//	msn_soap_read_cb(data,source,cond);
}

/*get the gleams info*/
void
msn_get_gleams(MsnContact *contact)
{
	MsnSoapReq *soap_request;

	purple_debug_info("MSNP14","msn get gleams info...\n");
	/*build SOAP and POST it*/
	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
					MSN_ADDRESS_BOOK_POST_URL,MSN_GET_GLEAMS_SOAP_ACTION,
					MSN_GLEAMS_TEMPLATE,
					msn_gleams_read_cb,
					msn_gleams_written_cb);
	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
}
#endif

/***************************************************************
 * Group Operation
 ***************************************************************/
static void
msn_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;
	
	purple_debug_info("MSNCL", "Add Group reply with the SOAP read_buf:\n%s\n",soapconn->read_buf);
	purple_debug_info("MSNCL", "Add Group reply with the SOAP body:\n%s\n",soapconn->body);

}

static void
msn_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
	MsnSoapConn * soapconn = data;	

	purple_debug_info("MSNCL","Finished sending Add Group\n");
	soapconn->read_cb = msn_group_read_cb;
//	msn_soap_read_cb(data,source,cond);
}

/*add group*/
void msn_add_group(MsnSession *session,const char* group_name)
{
	MsnSoapReq *soap_request;
	MsnContact *contact ;
	char *body = NULL;

	g_return_if_fail(session != NULL);
	contact = session->contact;
	purple_debug_info("::","msn_add_group()\n");

	body = g_strdup_printf(MSN_GROUP_ADD_TEMPLATE,group_name);
	/*build SOAP and POST it*/
	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
					MSN_ADDRESS_BOOK_POST_URL,MSN_GROUP_ADD_SOAP_ACTION,
					body,
					msn_group_read_cb,
					msn_group_written_cb);
	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
}

/*delete a group*/
void msn_del_group(MsnSession *session, const char *guid)
{
	MsnSoapReq *soap_request;
	MsnContact *contact;
	char *body = NULL;

	g_return_if_fail(session != NULL);
	/*if group uid we need to del is NULL, 
	 * we need to delete nothing
	 */
	g_return_if_fail(guid != NULL);
	contact = session->contact;
	purple_debug_info("::","msn_del_group()\n");

	body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE,guid);
	/*build SOAP and POST it*/
	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
					MSN_ADDRESS_BOOK_POST_URL,MSN_GROUP_DEL_SOAP_ACTION,
					body,
					msn_group_read_cb,
					msn_group_written_cb);
	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);

	g_free(body);
}

void
msn_contact_connect_init(MsnSoapConn *soapconn)
{
	/*  Authenticate via Windows Live ID. */
	purple_debug_info("::","msn_contact_connect_init()\n");

	msn_soap_init(soapconn,MSN_CONTACT_SERVER,1,
					msn_contact_login_connect_cb,
					msn_contact_login_error_cb);
}