changeset 20486:ff4ae9dde291

Make block/unblock work right, as well as being add by a buddy. Finally fixes #723
author Carlos Silva <typ0@pidgin.im>
date Mon, 03 Sep 2007 05:47:52 +0000
parents 87eea1c5be97
children 9812222e89d4
files libpurple/protocols/msn/contact.c libpurple/protocols/msn/contact.h libpurple/protocols/msn/msn.c libpurple/protocols/msn/msn.h libpurple/protocols/msn/notification.c libpurple/protocols/msn/soap.h libpurple/protocols/msn/user.c libpurple/protocols/msn/user.h libpurple/protocols/msn/userlist.c libpurple/protocols/msn/userlist.h
diffstat 10 files changed, 425 insertions(+), 117 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/msn/contact.c	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/contact.c	Mon Sep 03 05:47:52 2007 +0000
@@ -25,19 +25,27 @@
  */
 
 #include "msn.h"
-#include "soap.h"
 #include "contact.h"
 #include "xmlnode.h"
 #include "group.h"
 
-
 const char *MsnSoapPartnerScenarioText[] =
 {
         "Initial",
         "ContactSave",
-	"MessengerPendingList"
+	"MessengerPendingList",
+	"ContactMsgrAPI",
+	"BlockUnblock"
 };
 
+const char *MsnMemberRole[] =
+{
+	"Forward",
+	"Allow",
+	"Block",
+	"Reverse",
+	"Pending"
+};
 
 /* new a contact */
 MsnContact *
@@ -180,18 +188,18 @@
 }
 
 /*get MSN member role utility*/
-static int
+static MsnListId
 msn_get_memberrole(char * role)
 {
 	
 	if (!strcmp(role,"Allow")) {
-		return MSN_LIST_AL_OP;
+		return MSN_LIST_AL;
 	} else if (!strcmp(role,"Block")) {
-		return MSN_LIST_BL_OP;
+		return MSN_LIST_BL;
 	} else if (!strcmp(role,"Reverse")) {
-		return MSN_LIST_RL_OP;
+		return MSN_LIST_RL;
 	} else if (!strcmp(role,"Pending")) {
-		return MSN_LIST_PL_OP;
+		return MSN_LIST_PL;
 	}
 	return 0;
 }
@@ -274,7 +282,8 @@
 msn_parse_contact_list(MsnContact * contact)
 {
 	MsnSession * session;
-	int list_op = 0;
+	MsnListOp list_op = 0;
+	MsnListId list;
 	char * passport, *typedata;
 	xmlnode *fault, *faultstringnode, *faultdetail, *errorcode;
 	xmlnode *node, *body, *response, *result, *services;
@@ -421,7 +430,9 @@
 
 				roleNode = xmlnode_get_child(membershipnode,"MemberRole");
 				role = xmlnode_get_data(roleNode);
-				list_op = msn_get_memberrole(role);
+				list = msn_get_memberrole(role);
+				list_op = 1 << list;
+
 				purple_debug_info("MSNCL","MemberRole role: %s, list_op: %d\n",role,list_op);
 				
 				g_free(role);
@@ -429,9 +440,9 @@
 				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;
+					MsnUser *user = NULL;
+					xmlnode *typeNode, *membershipIdNode=NULL;
+					gchar *type, *membershipId = NULL;
 
 					purple_debug_info("MSNCL","Member type: %s\n", xmlnode_get_attrib(member,"type"));
 					
@@ -444,7 +455,18 @@
 						g_free(type);
 
 						user = msn_userlist_find_add_user(session->userlist,passport,NULL);
+
+						membershipIdNode = xmlnode_get_child(member,"MembershipId");
+						if (membershipIdNode != NULL) {
+							membershipId = xmlnode_get_data(membershipIdNode);
+							if (membershipId != NULL) {
+								user->membership_id[list] = atoi(membershipId);
+								g_free(membershipId);
+							}
+						}
+							
 						msn_got_lst_user(session, user, list_op, NULL);
+
 						g_free(passport);
 					}
 					
@@ -458,6 +480,16 @@
 						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);
+						
+						membershipIdNode = xmlnode_get_child(member,"MembershipId");
+						if (membershipIdNode != NULL) {
+							membershipId = xmlnode_get_data(membershipIdNode);
+							if (membershipId != NULL) {
+								user->membership_id[list] = atoi(membershipId);
+								g_free(membershipId);
+							}
+						}
+						
 						msn_got_lst_user(session, user, list_op, NULL);
 						g_free(passport);
 					}
@@ -473,11 +505,12 @@
 static void
 msn_get_contact_list_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
-	MsnSoapConn *soapconn = data;	
+	MsnSoapConn *soapconn = data;
 	MsnContact *contact;
 	MsnSession *session;
 	const char *abLastChange;
 	const char *dynamicItemLastChange;
+	gchar *partner_scenario;
 
 	purple_debug_misc("MSNCL","Got the contact list!\n");
 
@@ -485,6 +518,9 @@
 	g_return_if_fail(contact != NULL);
 	session = soapconn->session;
 	g_return_if_fail(session != NULL);
+	g_return_if_fail(soapconn->data_cb != NULL);
+
+	partner_scenario = soapconn->data_cb;
 
 	msn_parse_contact_list(contact);
 	/*free the read buffer*/
@@ -493,15 +529,19 @@
 	abLastChange = purple_account_get_string(session->account, "ablastChange", NULL);
 	dynamicItemLastChange = purple_account_get_string(session->account, "dynamicItemLastChange", NULL);
 
+	if (!strcmp(partner_scenario, MsnSoapPartnerScenarioText[MSN_PS_INITIAL])) {
+
 #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, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange);
+		/* 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, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange);
 #else
-	msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL);
+		msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL);
 #endif
-	msn_soap_free_read_buf(soapconn);
+	} else {
+		msn_soap_free_read_buf(soapconn);
+	}
 }
 
 static void
@@ -519,23 +559,27 @@
 msn_get_contact_list(MsnContact * contact, const MsnSoapPartnerScenario partner_scenario, const char *update_time)
 {
 	MsnSoapReq *soap_request;
-	char *body = NULL;
-	char * update_str;
+	gchar *body = NULL;
+	gchar * update_str;
+	const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario];
 
 	purple_debug_misc("MSNCL","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, MsnSoapPartnerScenarioText[partner_scenario], update_str);
+
+	body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, 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,
+					MSN_GET_CONTACT_POST_URL,
+					MSN_GET_CONTACT_SOAP_ACTION,
 					body,
-					NULL,
+					(gpointer) partner_scenario_str,
 					msn_get_contact_list_cb,
 					msn_get_contact_written_cb);
 	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
@@ -933,12 +977,17 @@
 		msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
 	}
 	msn_notification_send_fqy(soapconn->session, state->who);
-	
+
 	user = msn_userlist_find_add_user(userlist, state->who, state->who);
 	msn_user_add_group_id(user, state->guid);
 
+	if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
+		msn_del_contact_from_list(soapconn->session->contact, NULL, state->who, MSN_LIST_PL);
+	} else {
+		msn_soap_free_read_buf(soapconn);
+	}
+	
 	msn_callback_state_free(state);
-	msn_soap_free_read_buf(soapconn);
 }
 
 static void
@@ -1015,12 +1064,20 @@
 	}
 
 	if (state->action & MSN_ADD_BUDDY) {
+		MsnUser *user = msn_userlist_find_user(userlist, state->who);
+
         	if ( !msn_user_is_yahoo(soapconn->session->account, state->who) ) {
 
 		                msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
 		                msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
 	        }
 	        msn_notification_send_fqy(soapconn->session, state->who);
+
+		if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
+			msn_del_contact_from_list(soapconn->session->contact, NULL, state->who, MSN_LIST_PL);
+			msn_callback_state_free(state);
+			return;
+		}
 	}
 
 	if (state->action & MSN_MOVE_BUDDY) {
@@ -1295,79 +1352,192 @@
 	g_free(body);
 }
 
+
 static void
-msn_block_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_del_contact_from_list_read_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
-	purple_debug_info("MSNP14","block read done\n");
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state = NULL;
+
+	g_return_if_fail(soapconn->data_cb != NULL);
+	g_return_if_fail(soapconn->session != NULL);
+	g_return_if_fail(soapconn->session->contact != NULL);
+
+	state = (MsnCallbackState *) soapconn->data_cb;
+
+	purple_debug_info("MSN CL", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
+
+	if (state->list_id == MSN_LIST_PL) {
+		msn_add_contact_to_list(soapconn->session->contact, state, state->who, MSN_LIST_RL);
+		return;
+	}
+
+	if (state->list_id == MSN_LIST_AL) {
+		purple_privacy_permit_remove(soapconn->session->account, state->who, TRUE);
+		msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_BL);
+		msn_callback_state_free(state);
+		return;
+	}
+
+	if (state->list_id == MSN_LIST_BL) {
+		purple_privacy_deny_remove(soapconn->session->account, state->who, TRUE);
+		msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_AL);
+		msn_callback_state_free(state);
+		return;
+	}
+
+	msn_callback_state_free(state);
+	msn_soap_free_read_buf(soapconn);
 }
 
 static void
-msn_block_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_del_contact_from_list_written_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
-	MsnSoapConn * soapconn = data;	
+	MsnSoapConn * soapconn = data;
 
-	purple_debug_info("MSNP14","finish block written\n");
-	soapconn->read_cb = msn_block_read_cb;
+	purple_debug_info("MSN CL","Delete contact from list SOAP request sent!\n");
+	soapconn->read_cb = msn_del_contact_from_list_read_cb;
 }
 
-/*block a Contact*/
 void
-msn_block_contact(MsnContact *contact, const char* membership_id)
+msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state,
+			  const gchar *passport, const MsnListId list)
 {
 	MsnSoapReq *soap_request;
-	char *body = NULL;
+	gchar *body = NULL, *member = NULL;
+	MsnSoapPartnerScenario partner_scenario;
+	MsnUser *user;
+
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(passport != NULL);
+	g_return_if_fail(list < 5);
+
+	purple_debug_info("MSN CL", "Deleting contact %s from %s list\n", passport, MsnMemberRole[list]);
+
+	if (state == NULL) {
+		state = msn_callback_state_new();
+	}
+	msn_callback_state_set_list_id(state, list);
+	msn_callback_state_set_who(state, passport);
+
+	if (list == MSN_LIST_PL) {
+		g_return_if_fail(contact->session != NULL);
+		g_return_if_fail(contact->session->userlist != NULL);
+
+		user = msn_userlist_find_user(contact->session->userlist, passport);
+
+		partner_scenario = MSN_PS_CONTACT_API;
+		member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, user->membership_id[MSN_LIST_PL]);
+	} else {
+		/* list == MSN_LIST_AL || list == MSN_LIST_BL */
+		partner_scenario = MSN_PS_BLOCK_UNBLOCK;
+		
+		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
+	}
+
+	body = g_strdup_printf( MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE,
+			        MsnSoapPartnerScenarioText[partner_scenario],
+			        MsnMemberRole[list],
+			        member);
+	g_free(member);
+
+	soap_request = msn_soap_request_new( MSN_CONTACT_SERVER,
+					     MSN_SHARE_POST_URL,
+					     MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION,
+					     body,
+					     state,
+					     msn_del_contact_from_list_read_cb,
+					     msn_del_contact_from_list_written_cb);
+
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	
+	g_free(body);
+}
+
+static void
+msn_add_contact_to_list_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state = NULL;
+
+	g_return_if_fail(soapconn->data_cb != NULL);
+
+	state = (MsnCallbackState *) soapconn->data_cb;
+	
+	purple_debug_info("MSN CL", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
 
-	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,
-					NULL,
-					msn_block_read_cb,
-					msn_block_written_cb);
-	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	if (state->list_id == MSN_LIST_RL && (state->action & MSN_DENIED_BUDDY) ) {
+		g_return_if_fail(soapconn->session != NULL);
+		g_return_if_fail(soapconn->session->contact != NULL);
+
+		msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_BL);
+		return;
+	}
+
+	if (state->list_id == MSN_LIST_AL) {
+		purple_privacy_permit_add(soapconn->session->account, state->who, TRUE);
+	} else if (state->list_id == MSN_LIST_BL) {
+		purple_privacy_deny_add(soapconn->session->account, state->who, TRUE);
+	}
+
+	msn_callback_state_free(state);
+	msn_soap_free_read_buf(soapconn);
+}
+
+
+static void
+msn_add_contact_to_list_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+
+	purple_debug_info("MSN CL","Add contact to list SOAP request sent!\n");
+	soapconn->read_cb = msn_add_contact_to_list_read_cb;
+}
+
+void
+msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
+			const gchar *passport, const MsnListId list)
+{
+	MsnSoapReq *soap_request;
+	gchar *body = NULL, *member = NULL;
+	MsnSoapPartnerScenario partner_scenario;
+
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(passport != NULL);
+	g_return_if_fail(list < 5);
+
+	purple_debug_info("MSN CL", "Adding contact %s to %s list\n", passport, MsnMemberRole[list]);
+
+	if (state == NULL) {
+		state = msn_callback_state_new();
+	}
+	msn_callback_state_set_list_id(state, list);
+	msn_callback_state_set_who(state, passport);
+
+	partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK;
+
+	member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
+
+	body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE, 
+			       MsnSoapPartnerScenarioText[partner_scenario],
+			       MsnMemberRole[list], 
+			       member);
+
+	g_free(member);
+
+	soap_request = msn_soap_request_new( MSN_CONTACT_SERVER,
+					     MSN_SHARE_POST_URL,
+					     MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION,
+					     body,
+					     state,
+					     msn_add_contact_to_list_read_cb,
+					     msn_add_contact_to_list_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,
-					NULL,
-					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
@@ -1552,7 +1722,7 @@
 	}
 
 	if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) {
-		// add back PurpleGroup since it isn't really removed in the server?
+		// XXX add back PurpleGroup since it isn't really removed in the server?
 		return;
 	}
 
--- a/libpurple/protocols/msn/contact.h	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/contact.h	Mon Sep 03 05:47:52 2007 +0000
@@ -252,15 +252,90 @@
 
 /* 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"
+#define MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/AddMember"
+
+#define MSN_MEMBER_PASSPORT_XML	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+					"<Type>Passport</Type>"\
+					"<State>Accepted</State>"\
+					"<PassportName>%s</PassportName>"\
+				"</Member>"
+
+#define MSN_MEMBER_MEMBERSHIPID_XML	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+						"<Type>Passport</Type>"\
+						"<MembershipId>%u</MembershipId>"\
+						"<State>Accepted</State>"\
+					"</Member>"
 
 /* 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>"
+#define MSN_CONTACT_DELECT_FROM_LIST_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>%s</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>%s</MemberRole>"\
+					"<Members>"\
+						"%s"\
+					"</Members>"\
+				"</Membership>"\
+			"</memberships>"\
+		"</DeleteMember>"\
+	"</soap:Body>"\
+"</soap:Envelope>"
+
+#define MSN_CONTACT_ADD_TO_LIST_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>%s</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>%s</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_DELETE_MEMBER_FROM_LIST_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>"
 
 
@@ -280,17 +355,15 @@
 #define MSN_GROUP_RENAME_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupUpdate"
 #define MSN_GROUP_RENAME_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><ABGroupUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groups><Group><groupId>%s</groupId><groupInfo><name>%s</name></groupInfo><propertiesChanged>GroupName </propertiesChanged></Group></groups></ABGroupUpdate></soap:Body></soap:Envelope>"
 
-
 typedef enum 
 {
 	MSN_ADD_BUDDY			= 0x01,
-	MSN_ADD_BUDDY_TO_GROUP		= 0x02,
-	MSN_REMOVE_BUDDY		= 0x04,
-	MSN_REMOVE_BUDDY_FROM_GROUP	= 0x08,
-	MSN_MOVE_BUDDY			= 0x10,
-	MSN_ADD_GROUP			= 0x20,
-	MSN_DEL_GROUP			= 0x40,
-	MSN_RENAME_GROUP		= 0x80
+	MSN_MOVE_BUDDY			= 0x02,
+	MSN_ACCEPTED_BUDDY		= 0x04,
+	MSN_DENIED_BUDDY		= 0x08,
+	MSN_ADD_GROUP			= 0x10,
+	MSN_DEL_GROUP			= 0x20,
+	MSN_RENAME_GROUP		= 0x40,
 } MsnCallbackAction;
 
 typedef struct _MsnContact MsnContact;
@@ -318,10 +391,11 @@
 {
 	MSN_PS_INITIAL,
 	MSN_PS_SAVE_CONTACT,
-	MSN_PS_PENDING_LIST
+	MSN_PS_PENDING_LIST,
+	MSN_PS_CONTACT_API,
+	MSN_PS_BLOCK_UNBLOCK
 } MsnSoapPartnerScenario;
 
-
 /************************************************
  * function prototype
  ************************************************/
@@ -348,7 +422,7 @@
 			  const MsnSoapPartnerScenario partner_scenario,
 			  const char * update, const char * gupdate);
 
-/*contact SOAP Operation*/
+/* contact SOAP operations */
 void msn_update_contact(MsnContact *contact, const char* nickname);
 
 void msn_add_contact(MsnContact *contact, MsnCallbackState *state, 
@@ -359,17 +433,18 @@
 			      const char *passport, const char *groupId);
 void msn_del_contact_from_group(MsnContact *contact, const char *passport, 
 				const char *group_name);
-/*group operation*/
+/* group operations */
 void msn_add_group(MsnSession *session, MsnCallbackState *state, 
 					const char* group_name);
 void msn_del_group(MsnSession *session, const gchar *group_name);
 void msn_contact_rename_group(MsnSession *session, const char *old_group_name,
 						   const char *new_group_name);
 
-
-/*contact Block/unblock operation*/
-void msn_block_contact(MsnContact *contact,const char* membership_id);
-void msn_unblock_contact(MsnContact *contact,const char* passport);
+/* lists operations */
+void msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
+			     const gchar *passport, const MsnListId list);
+void msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state,
+			       const gchar *passport, const MsnListId list);
 
 void msn_contact_connect_init(MsnSoapConn *soapconn);
 
--- a/libpurple/protocols/msn/msn.c	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Mon Sep 03 05:47:52 2007 +0000
@@ -1162,12 +1162,22 @@
 	userlist = session->userlist;
 	user = msn_userlist_find_user(userlist, who);
 
+	purple_debug_info("MSN", "msn_add_permit()\n");
+
 	if (!session->logged_in)
 		return;
 
-	if (user != NULL && user->list_op & MSN_LIST_BL_OP)
+	if (user != NULL && user->list_op & MSN_LIST_BL_OP) {
 		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
 
+		/* delete contact from Block list and add it to Allow in the callback */
+		msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL);
+	} else {
+		/* just add the contact to Allow list */
+		msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_AL);
+	}
+
+
 	msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
 }
 
@@ -1182,12 +1192,21 @@
 	userlist = session->userlist;
 	user = msn_userlist_find_user(userlist, who);
 
+	purple_debug_info("MSN", "msn_add_deny()\n");
+	
 	if (!session->logged_in)
 		return;
 
-	if (user != NULL && user->list_op & MSN_LIST_AL_OP)
+	if (user != NULL && user->list_op & MSN_LIST_AL_OP) {
 		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
 
+		/* delete contact from Allow list and add it to Block in the callback */
+		msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL);
+	} else {
+		/* just add the contact to Block list */
+		msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_BL);
+	}
+
 	msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
 }
 
@@ -1201,6 +1220,8 @@
 	session = gc->proto_data;
 	userlist = session->userlist;
 
+	purple_debug_info("MSN", "msn_rem_permit()\n");
+
 	if (!session->logged_in)
 		return;
 
@@ -1208,6 +1229,8 @@
 
 	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
 
+	msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL);
+
 	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
 		msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
 }
@@ -1222,6 +1245,8 @@
 	session = gc->proto_data;
 	userlist = session->userlist;
 
+	purple_debug_info("MSN", "msn_rem_deny()\n");
+
 	if (!session->logged_in)
 		return;
 
@@ -1229,6 +1254,8 @@
 
 	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
 
+	msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL);
+
 	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
 		msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
 }
--- a/libpurple/protocols/msn/msn.h	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/msn.h	Mon Sep 03 05:47:52 2007 +0000
@@ -32,7 +32,7 @@
 /* #define MSN_DEBUG_SLP_VERBOSE 1 */
 /* #define MSN_DEBUG_SLP_FILES 1 */
 
-/* #define MSN_DEBUG_NS 1 */
+#define MSN_DEBUG_NS 1
 /* #define MSN_DEBUG_SB 1 */
 
 #include "internal.h"
--- a/libpurple/protocols/msn/notification.c	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Mon Sep 03 05:47:52 2007 +0000
@@ -1277,6 +1277,7 @@
 	g_strfreev(params);
 }
 
+#if 0
 static void
 rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -1302,6 +1303,7 @@
 	msn_got_rem_user(session, user, list_id, group_id);
 	msn_user_update(user);
 }
+#endif
 
 static void
 rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
@@ -1330,6 +1332,7 @@
 	g_strfreev(params);
 }
 
+#if 0
 static void
 syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
@@ -1360,6 +1363,7 @@
 	session->sync = sync;
 	cmdproc->cbs_table = sync->cbs_table;
 }
+#endif
 
 /**************************************************************************
  * Misc commands
--- a/libpurple/protocols/msn/soap.h	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/soap.h	Mon Sep 03 05:47:52 2007 +0000
@@ -29,7 +29,7 @@
 #define MSN_SOAP_READ_BUFF_SIZE		8192
 
 /* define this to debug the communications with the SOAP server */
-/* #define MSN_SOAP_DEBUG */
+#define MSN_SOAP_DEBUG 
 
 
 typedef enum
@@ -112,6 +112,7 @@
 	int body_len;
 };
 
+
 /*Function Prototype*/
 /*Soap Request Function */
 MsnSoapReq *msn_soap_request_new(const char *host, const char *post_url,
--- a/libpurple/protocols/msn/user.c	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Mon Sep 03 05:47:52 2007 +0000
@@ -218,6 +218,14 @@
 }
 
 void
+msn_user_unset_op(MsnUser *user, int list_op)
+{
+	g_return_if_fail(user != NULL);
+	
+	user->list_op &= ~list_op;
+}
+
+void
 msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img)
 {
 	MsnObject *msnobj = msn_user_get_object(user);
--- a/libpurple/protocols/msn/user.h	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/user.h	Mon Sep 03 05:47:52 2007 +0000
@@ -82,7 +82,11 @@
 	GHashTable *clientcaps; /**< The client's capabilities.     */
 
 	MsnUserType type;       /**< The user type                  */
+
 	int list_op;            /**< Which lists the user is in     */
+
+	guint membership_id[5];	/**< The membershipId sent by the contacts server,
+				     indexed by the list it belongs to		*/
 };
 
 /**************************************************************************/
@@ -327,7 +331,8 @@
 gboolean
 msn_user_is_yahoo(PurpleAccount *account ,const char *name);
 
-void msn_user_set_op(MsnUser *user,int list_op);
+void msn_user_set_op(MsnUser *user, int list_op);
+void msn_user_unset_op(MsnUser *user, int list_op);
 
 /*@}*/
 
--- a/libpurple/protocols/msn/userlist.c	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/userlist.c	Mon Sep 03 05:47:52 2007 +0000
@@ -43,9 +43,15 @@
 	MsnPermitAdd *pa = data;
 	MsnSession *session = pa->gc->proto_data;
 	MsnUserList *userlist = session->userlist;
+	MsnUser *user = msn_userlist_find_add_user(userlist, pa->who, pa->who);
 	
+	purple_debug_misc("MSN Userlist", "Accepted the new buddy: %s\n", pa->who);
+
 	msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL);
-	msn_userlist_add_buddy(userlist, pa->who, NULL);
+
+	if (msn_userlist_user_is_in_list(user, MSN_LIST_FL)) {
+		msn_del_contact_from_list(session->contact, NULL, pa->who, MSN_LIST_PL);
+	}
 
 	g_free(pa->who);
 	g_free(pa->friendly);
@@ -57,12 +63,18 @@
 {
 	MsnPermitAdd *pa = data;
 
+	purple_debug_misc("MSN Userlist", "Deniedthe new buddy: %s\n", pa->who);
+
 	if (g_list_find(purple_connections_get_all(), pa->gc) != NULL)
 	{
 		MsnSession *session = pa->gc->proto_data;
 		MsnUserList *userlist = session->userlist;
+		MsnCallbackState *state = msn_callback_state_new();
+	
+		msn_callback_state_set_action(state, MSN_DENIED_BUDDY);
 
 		msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_BL);
+		msn_del_contact_from_list(session->contact, state, pa->who, MSN_LIST_PL);
 	}
 
 	g_free(pa->who);
@@ -90,7 +102,7 @@
  * Utility functions
  **************************************************************************/
 
-static gboolean
+gboolean
 msn_userlist_user_is_in_group(MsnUser *user, const char * group_id)
 {
 	if (user == NULL)
@@ -105,7 +117,7 @@
 	return FALSE;
 }
 
-static gboolean
+gboolean
 msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id)
 {
 	int list_op;
@@ -327,6 +339,8 @@
 
 	passport = msn_user_get_passport(user);
 	store = msn_user_get_store_name(user);
+	
+	msn_user_set_op(user, list_op);
 
 	if (list_op & MSN_LIST_FL_OP)
 	{
@@ -377,8 +391,6 @@
 	{
 		got_new_entry(gc, passport, store);
 	}
-
-	user->list_op |= list_op;
 }
 
 /**************************************************************************
@@ -609,7 +621,8 @@
 {
 	MsnUser *user;
 	const gchar *list;
-	
+	MsnListOp list_op = 1 << list_id;
+
 	user = msn_userlist_find_user(userlist, who);
 	
 	g_return_if_fail(user != NULL);
@@ -619,14 +632,15 @@
 		purple_debug_info("MSN Userlist", "User %s is not in list %s, not removing.\n", who, list);
 		return;
 	}
-	
+
+	msn_user_unset_op(user, list_op);
+
 	msn_notification_rem_buddy_from_list(userlist->session->notification, list_id, who);
 }
 
 /*add buddy*/
 void
-msn_userlist_add_buddy(MsnUserList *userlist, const char *who, 
-					      const char *group_name)
+msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group_name)
 {
 	MsnUser *user;
 	MsnCallbackState *state = NULL;
--- a/libpurple/protocols/msn/userlist.h	Fri Aug 31 18:15:23 2007 +0000
+++ b/libpurple/protocols/msn/userlist.h	Mon Sep 03 05:47:52 2007 +0000
@@ -40,6 +40,7 @@
 
 } MsnListId;
 
+
 struct _MsnUserList
 {
 	MsnSession *session;
@@ -58,6 +59,8 @@
 
 };
 
+gboolean msn_userlist_user_is_in_group(MsnUser *user, const char * group_id);
+gboolean msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id);
 MsnListId msn_get_list_id(const char *list);
 
 void msn_got_add_user(MsnSession *session, MsnUser *user,
@@ -89,7 +92,8 @@
 void msn_userlist_remove_group_id(MsnUserList *userlist, const char *group_id);
 
 void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who);
-void msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group_name);
+void msn_userlist_add_buddy(MsnUserList *userlist, 
+			    const char *who, const char *group_name);
 void msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
 						    const char *old_group_name,
 						    const char *new_group_name);