changeset 20481:eb93710aec4d

Make buddy and group management actually work, add some SOAP templates, redesign some parts of code and separate some bigger functions into its smaller tasks which are completely unrelated to each other
author Carlos Silva <typ0@pidgin.im>
date Tue, 28 Aug 2007 03:54:18 +0000
parents 7d3e53e3f623
children 1754155051a4
files libpurple/protocols/msn/contact.c libpurple/protocols/msn/contact.h libpurple/protocols/msn/dialog.c libpurple/protocols/msn/msn.c libpurple/protocols/msn/notification.c libpurple/protocols/msn/notification.h libpurple/protocols/msn/oim.c libpurple/protocols/msn/session.c libpurple/protocols/msn/soap.c libpurple/protocols/msn/soap.h libpurple/protocols/msn/user.c libpurple/protocols/msn/userlist.c libpurple/protocols/msn/userlist.h
diffstat 13 files changed, 1135 insertions(+), 442 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/msn/contact.c	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/contact.c	Tue Aug 28 03:54:18 2007 +0000
@@ -31,7 +31,14 @@
 #include "group.h"
 
 
-/*new a contact*/
+const char *MsnAddressBookActionText[] =
+{
+        "Initial",
+        "ContactSave"
+};
+
+
+/* new a contact */
 MsnContact *
 msn_contact_new(MsnSession *session)
 {
@@ -44,7 +51,7 @@
 	return contact;
 }
 
-/*destroy the contact*/
+/* destroy the contact */
 void
 msn_contact_destroy(MsnContact *contact)
 {
@@ -52,6 +59,93 @@
 	g_free(contact);
 }
 
+MsnCallbackState *
+msn_callback_state_new(void)
+{
+	return g_new0(MsnCallbackState, 1);
+}	
+
+void
+msn_callback_state_free(MsnCallbackState *state)
+{
+	g_return_if_fail(state != NULL);
+	
+	if (state->who != NULL)
+		g_free(state->who);
+	
+	if (state->old_group_name != NULL)
+		g_free(state->old_group_name);
+	
+	if (state->new_group_name != NULL)
+		g_free(state->new_group_name);
+	
+	if (state->guid != NULL)
+		g_free(state->guid);
+	
+	g_free(state);
+}
+	
+void
+msn_callback_state_set_who(MsnCallbackState *state, const gchar *who)
+{
+	g_return_if_fail(state != NULL);
+	
+	if (state->who != NULL)
+		g_free(state->who);
+	
+	state->who = who != NULL ? g_strdup(who) : NULL;
+}
+
+void
+msn_callback_state_set_old_group_name(MsnCallbackState *state, const gchar *old_group_name)
+{
+	g_return_if_fail(state != NULL);
+	
+	if (state->old_group_name != NULL)
+		g_free(state->old_group_name);
+	
+	state->old_group_name = old_group_name != NULL ? g_strdup(old_group_name) : NULL;
+}
+
+void
+msn_callback_state_set_new_group_name(MsnCallbackState *state, const gchar *new_group_name)
+{
+	g_return_if_fail(state != NULL);
+	
+	if (state->new_group_name != NULL)
+		g_free(state->new_group_name);
+	
+	state->new_group_name = new_group_name != NULL ? g_strdup(new_group_name) : NULL;
+}
+
+void
+msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid)
+{
+	g_return_if_fail(state != NULL);
+	
+	if (state->guid != NULL)
+		g_free(state->guid);
+	
+	state->guid = guid != NULL ? g_strdup(guid) : NULL;
+}
+
+
+void
+msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id)
+{
+	g_return_if_fail(state != NULL);
+	
+	state->list_id = list_id;
+}
+
+void
+msn_callback_state_set_action(MsnCallbackState *state, MsnCallbackAction action)
+{
+	g_return_if_fail(state != NULL);
+	
+	state->action |= action;
+}
+		       
 /*contact SOAP server login error*/
 static void
 msn_contact_login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
@@ -89,15 +183,13 @@
 msn_get_memberrole(char * role)
 {
 	
-	purple_debug_info("::","msn_get_memberrole()\n");
-
-	if(!strcmp(role,"Allow")){
+	if (!strcmp(role,"Allow")) {
 		return MSN_LIST_AL_OP;
-	}else if(!strcmp(role,"Block")){
+	} else if (!strcmp(role,"Block")) {
 		return MSN_LIST_BL_OP;
-	}else if(!strcmp(role,"Reverse")){
+	} else if (!strcmp(role,"Reverse")) {
 		return MSN_LIST_RL_OP;
-	}else if(!strcmp(role,"Pending")){
+	} else if (!strcmp(role,"Pending")) {
 		return MSN_LIST_PL_OP;
 	}
 	return 0;
@@ -107,13 +199,13 @@
 static int 
 msn_get_user_type(char * type)
 {
-	if(!strcmp(type,"Regular")){
+	if (!strcmp(type,"Regular")) {
 		return MSN_USER_TYPE_PASSPORT;
 	}
-	if(!strcmp(type,"Live")){
+	if (!strcmp(type,"Live")) {
 		return MSN_USER_TYPE_PASSPORT;
 	}
-	if(!strcmp(type,"LivePending")){
+	if (!strcmp(type,"LivePending")) {
 		return MSN_USER_TYPE_PASSPORT;
 	}
 
@@ -130,15 +222,10 @@
 	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");
-	}
+	purple_debug_info("MSN AddressBook", "Address Book successfully created!\n");
+	msn_get_address_book(contact, MSN_AB_INITIAL, NULL, NULL);
 
-	msn_soap_free_read_buf(soapconn);
+//	msn_soap_free_read_buf(soapconn);
 	return;
 }
 
@@ -159,6 +246,11 @@
 	MsnSoapReq *soap_request;
 	gchar *body;
 
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(contact->session != NULL);
+	g_return_if_fail(contact->session->user != NULL);
+	g_return_if_fail(contact->session->user->passport != NULL);
+	
 	purple_debug_info("MSN AddressBook","Creating an Address Book.\n");
 
 	body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport);
@@ -166,6 +258,7 @@
 	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
 					MSN_ADDRESS_BOOK_POST_URL,MSN_ADD_ADDRESSBOOK_SOAP_ACTION,
 					body,
+					NULL,
 					msn_create_address_cb,
 					msn_create_address_written_cb);
 	msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
@@ -403,9 +496,9 @@
 	/* 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);
+	msn_get_address_book(contact, MSN_AB_INITIAL, abLastChange, dynamicItemLastChange);
 #else
-	msn_get_address_book(contact, NULL, NULL);
+	msn_get_address_book(contact, MSN_AB_INITIAL, NULL, NULL);
 #endif
 	msn_soap_free_read_buf(soapconn);
 }
@@ -441,6 +534,7 @@
 	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
 					MSN_GET_CONTACT_POST_URL,MSN_GET_CONTACT_SOAP_ACTION,
 					body,
+					NULL,
 					msn_get_contact_list_cb,
 					msn_get_contact_written_cb);
 	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
@@ -744,8 +838,10 @@
 	if ( msn_parse_addressbook(contact) ) {
 		msn_soap_free_read_buf(soapconn);
 
-		msn_send_privacy(session->account->gc);
-		msn_notification_dump_contact(session);
+		if (!session->logged_in) {
+			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)
@@ -773,11 +869,11 @@
 
 /*get the address book*/
 void
-msn_get_address_book(MsnContact *contact, const char *LastChanged, const char *dynamicItemLastChange)
+msn_get_address_book(MsnContact *contact, const MsnAddressBookAction abaction, const char *LastChanged, const char *dynamicItemLastChange)
 {
 	MsnSoapReq *soap_request;
 	char *body = NULL;
-	char *ab_update_str,*update_str;
+	char *ab_update_str,*update_str, *partner_scenario;
 
 	purple_debug_misc("MSN AddressBook","Getting Address Book\n");
 
@@ -795,22 +891,65 @@
 	}
 	g_free(ab_update_str);
 	
-	body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE,update_str);
+	partner_scenario = g_strdup(MsnAddressBookActionText[abaction]);
+
+	purple_debug_misc("MSN CL", "Get Address Book PartnerScenario parameter: %s\n", partner_scenario);
+
+	body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE, partner_scenario, update_str);
 	g_free(update_str);
+	g_free(partner_scenario);
 
 	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
 					MSN_ADDRESS_BOOK_POST_URL,MSN_GET_ADDRESS_SOAP_ACTION,
 					body,
+					NULL,
 					msn_get_address_cb,
 					msn_address_written_cb);
 	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
 	g_free(body);
 }
 
+/* We don't need this as libpurple doesn't support ungroupped buddies, so we
+ * can add buddies directly to a group with msn_add_contact_to_group() */
+#if 0	
 static void
 msn_add_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
-	purple_debug_info("MSNCL","Add contact read done\n");
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state = NULL;
+	MsnUserList *userlist;
+	
+	g_return_if_fail(soapconn->data_cb != NULL);
+	g_return_if_fail(soapconn->session != NULL);
+	g_return_if_fail(soapconn->session->userlist != NULL);
+	
+	userlist = soapconn->session->userlist;
+	
+	state = (MsnCallbackState *) soapconn->data_cb;
+	
+	purple_debug_info("MSNCL","Contact added successfully\n");
+
+	// the code this block is replacing didn't send ADL for yahoo contacts,
+	// but i haven't confirmed this is WLM's behaviour wrt yahoo contacts
+
+	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 (state->action & MSN_ADD_BUDDY_TO_GROUP) {
+		msn_add_contact_to_group(soapconn->session->contact,
+					 state,
+			       		 state->who,
+					 state->guid);
+	} else {
+		msn_callback_state_free(state);
+		msn_soap_free_read_buf(soapconn);
+	}
+	
 }
 
 static void
@@ -820,34 +959,40 @@
 
 	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 */
+/* add a Contact */
 void
-msn_add_contact(MsnContact *contact,const char *passport,const char *groupId)
+msn_add_contact(MsnContact *contact, MsnCallbackState *state, const char *passport, const char *displayname)
 {
 	MsnSoapReq *soap_request;
-	char *body = NULL;
-	char *contact_xml = NULL;
-	char *soap_action;
+	gchar *body = NULL;
+	gchar *contact_xml = NULL;
+	gchar *soap_action;
+//	gchar *escaped_displayname;
+
+	purple_debug_info("MSNCL","Adding contact %s to contact list\n", passport);
 
-	purple_debug_info("MSNCL","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);
-	}
+	// if (displayname != NULL) {
+	//	escaped_displayname = g_markup_decode_text(displayname, -1);
+	// } else {
+	//	escaped_displayname = passport;
+	// }
+	// contact_xml = g_strdup_printf(MSN_XML_ADD_CONTACT, escaped_displayname, passport);
+	
+	contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
+	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);
+
 	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,soap_action,
+					MSN_ADDRESS_BOOK_POST_URL,
+					soap_action,
 					body,
+					state,
 					msn_add_contact_read_cb,
 					msn_add_contact_written_cb);
 	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
@@ -855,11 +1000,116 @@
 	g_free(soap_action);
 	g_free(body);
 }
+#endif
+
+static void
+msn_add_contact_to_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state; 
+	MsnUserList *userlist;
+
+        g_return_if_fail(soapconn->data_cb != NULL);
+        g_return_if_fail(soapconn->session != NULL);
+        g_return_if_fail(soapconn->session->userlist != NULL);
+
+        userlist = soapconn->session->userlist;
+
+	state = (MsnCallbackState *) soapconn->data_cb;
+
+	if (msn_userlist_add_buddy_to_group(userlist, state->who, state->new_group_name) == TRUE) {
+		purple_debug_info("MSNCL", "Contact %s added to group %s successfully!\n", state->who, state->new_group_name);
+	} else {
+		purple_debug_info("MSNCL","Contact %s added to group %s successfully on server, but failed in the local list\n", state->who, state->new_group_name);
+	}
+
+	if (state->action & MSN_ADD_BUDDY) {
+        	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 (state->action & MSN_MOVE_BUDDY) {
+		msn_del_contact_from_group(soapconn->session->contact, state->who, state->old_group_name);
+	} else {
+		msn_callback_state_free(state);
+		msn_soap_free_read_buf(soapconn);
+	}
+}
+
+static void
+msn_add_contact_to_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+
+	purple_debug_info("MSNCL","Add contact to group request sent!\n");
+	soapconn->read_cb = msn_add_contact_to_group_read_cb;
+}
+
+void
+msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, 
+			 const char *passport, const char *groupId)
+{
+	MsnSoapReq *soap_request;
+	MsnUserList *userlist;
+	MsnUser *user;
+	gchar *body = NULL, *soap_action, *contact_xml;
+
+	g_return_if_fail(passport != NULL);
+	g_return_if_fail(groupId != NULL);
+
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(contact->session != NULL);
+	g_return_if_fail(contact->session->userlist != NULL);
+	
+	userlist = contact->session->userlist;
+	
+	purple_debug_info("MSNCL", "Adding user %s to group %s\n", passport, 
+			  msn_userlist_find_group_name(userlist, groupId));
+
+	user = msn_userlist_find_user(userlist, passport);
+	if (user == NULL) {
+		purple_debug_warning("MSN CL", "Unable to retrieve user %s from the userlist!\n", passport);
+	}
+
+	if (user->uid != NULL) {
+		contact_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
+	} else {
+		contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
+	}
+
+	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,
+					state,
+					msn_add_contact_to_group_read_cb,
+					msn_add_contact_to_group_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");
+	MsnSoapConn * soapconn = data;
+
+	// we should probably delete it from the userlist aswell
+	purple_debug_info("MSNCL","Delete contact successful\n");
+	msn_soap_free_read_buf(soapconn);
 }
 
 static void
@@ -869,38 +1119,127 @@
 
 	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)
+msn_delete_contact(MsnContact *contact, const char *contactId)
 {	
-	char *body = NULL;
-	char *contact_xml = NULL ;
+	gchar *body = NULL;
+	gchar *contact_id_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*/
+	contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, contactId);
+
+	/* build SOAP request */
+	purple_debug_info("MSNCL","Deleting contact with contactId: %s\n", contactId);
+	body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE, contact_id_xml);
 	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,MSN_CONTACT_DEL_SOAP_ACTION,
+					MSN_ADDRESS_BOOK_POST_URL,
+					MSN_CONTACT_DEL_SOAP_ACTION,
 					body,
+					NULL,
 					msn_delete_contact_read_cb,
 					msn_delete_contact_written_cb);
-	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+	g_free(contact_id_xml);
+
+	/* POST the SOAP request */
+	msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
 
 	g_free(body);
 }
 
-#if 0
+static void
+msn_del_contact_from_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;
+	MsnCallbackState *state = (MsnCallbackState *) soapconn->data_cb;
+	
+	if (msn_userlist_rem_buddy_from_group(soapconn->session->userlist, state->who, state->old_group_name)) {
+		purple_debug_info("MSN CL", "Contact %s deleted successfully from group %s\n", state->who, state->old_group_name);
+	} else {
+		purple_debug_info("MSN CL", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name);
+	}
+	
+	msn_callback_state_free(state);
+	msn_soap_free_read_buf(soapconn);
+	return;
+}
+
+static void
+msn_del_contact_from_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	MsnSoapConn * soapconn = data;	
+	
+	purple_debug_info("MSN CL","Del contact from group request sent!\n");
+	soapconn->read_cb = msn_del_contact_from_group_read_cb;
+}
+
+void
+msn_del_contact_from_group(MsnContact *contact, const char *passport, const char *group_name)
+{
+	MsnSoapReq *soap_request;
+	MsnUserList * userlist;
+	MsnUser *user;
+	MsnCallbackState *state;
+	gchar *body = NULL, *soap_action, *contact_id_xml;
+	const gchar *groupId;
+	
+	g_return_if_fail(passport != NULL);
+	g_return_if_fail(group_name != NULL);
+	g_return_if_fail(contact != NULL);
+	g_return_if_fail(contact->session != NULL);
+	g_return_if_fail(contact->session->userlist != NULL);
+	
+	userlist = contact->session->userlist;
+	
+	groupId = msn_userlist_find_group_id(userlist, group_name);
+	if (groupId != NULL) {
+		purple_debug_info("MSN CL", "Deleting user %s from group %s\n", passport, group_name);
+	} else {
+		purple_debug_warning("MSN CL", "Unable to retrieve group id from group %s !\n", group_name);
+		return;
+	}
+	
+	user = msn_userlist_find_user(userlist, passport);
+	
+	if (user == NULL) {
+		purple_debug_warning("MSN CL", "Unable to retrieve user from passport %s!\n", passport);
+		return;
+	}
+	
+	state = msn_callback_state_new();
+	msn_callback_state_set_who(state, passport);
+	msn_callback_state_set_guid(state, groupId);
+	msn_callback_state_set_old_group_name(state, group_name);
+		
+	contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
+	body = g_strdup_printf(MSN_CONTACT_DEL_GROUP_TEMPLATE, contact_id_xml, groupId);
+	g_free(contact_id_xml);
+	
+	/*build SOAP and POST it*/
+	soap_action = g_strdup(MSN_CONTACT_DEL_GROUP_SOAP_ACTION);
+	
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					    MSN_ADDRESS_BOOK_POST_URL,
+					    soap_action,
+					    body,
+					    state,
+					    msn_del_contact_from_group_read_cb,
+					    msn_del_contact_from_group_written_cb);
+	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	
+	g_free(soap_action);
+	g_free(body);
+}
+
+
 static void
 msn_update_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
-	purple_debug_info("MSNP14","update contact read done\n");
+	purple_debug_info("MSN CL","Contact updated successfully\n");
 }
 
 static void
@@ -908,32 +1247,37 @@
 {
 	MsnSoapConn * soapconn = data;	
 
-	purple_debug_info("MSNP14","update contact written\n");
+	purple_debug_info("MSN CL","Update contact information request sent\n");
 	soapconn->read_cb = msn_update_contact_read_cb;
-//	msn_soap_read_cb(data,source,cond);
 }
 
-/*update a contact's Nickname*/
+/* Update a contact's nickname */
+
 void
-msn_update_contact(MsnContact *contact,const char* nickname)
+msn_update_contact(MsnContact *contact, const char* nickname)
 {
 	MsnSoapReq *soap_request;
-	char *body = NULL;
+	gchar *body = NULL, *escaped_nickname;
 
-	purple_debug_info("MSNP14","msn unblock a contact...\n");
+	purple_debug_info("MSN CL","Update contact information with new friendly name: %s\n", nickname);
+	
+	escaped_nickname = g_markup_escape_text(nickname, -1);
 
-	body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE,nickname);
+	body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, escaped_nickname);
+	
+	g_free(escaped_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,
+					MSN_ADDRESS_BOOK_POST_URL,
+					MSN_CONTACT_UPDATE_SOAP_ACTION,
 					body,
+					NULL,
 					msn_update_contact_read_cb,
 					msn_update_contact_written_cb);
-	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	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)
@@ -946,7 +1290,7 @@
 {
 	MsnSoapConn * soapconn = data;	
 
-	purple_debug_info("MSNP14","finish unblock written\n");
+	purple_debug_info("MSNP14","finish block written\n");
 	soapconn->read_cb = msn_block_read_cb;
 }
 
@@ -961,8 +1305,10 @@
 	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,
+					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);
@@ -999,6 +1345,7 @@
 	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);
@@ -1032,25 +1379,83 @@
 	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_ADDRESS_BOOK_POST_URL,
+					MSN_GET_GLEAMS_SOAP_ACTION,
 					MSN_GLEAMS_TEMPLATE,
+					NULL,
 					msn_gleams_read_cb,
 					msn_gleams_written_cb);
 	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
 }
 #endif
 
+
 /***************************************************************
- * Group Operation
+ * Group Operations
  ***************************************************************/
+
 static void
 msn_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
 	MsnSoapConn * soapconn = data;
+	MsnUserList *userlist;
+	MsnCallbackState *state = NULL;
 	
-	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);
+	purple_debug_info("MSN CL", "Group request successful.\n");
+	
+	g_return_if_fail(soapconn->session != NULL);
+	g_return_if_fail(soapconn->session->userlist != NULL);
+	g_return_if_fail(soapconn->session->contact != NULL);
+	
+	if (soapconn->data_cb != NULL) {
+		state = (MsnCallbackState *) soapconn->data_cb;
+		userlist = soapconn->session->userlist;
+		
+		if (state->action & MSN_RENAME_GROUP) {
+			msn_userlist_rename_group_id(soapconn->session->userlist,
+						     state->guid,
+						     state->new_group_name);
+		}
+		
+		if (state->action & MSN_ADD_GROUP) {
+			gchar *guid, *endguid;
+			
+			guid = g_strstr_len(soapconn->read_buf, soapconn->read_len, "<guid>");
+			guid += 6;
+			endguid = g_strstr_len(soapconn->read_buf, soapconn->read_len, "</guid>");
+			*endguid = '\0';
+			/* create and add the new group to the userlist */
+			purple_debug_info("MSN CL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid);
+			msn_group_new(soapconn->session->userlist, guid, state->new_group_name);
 
+			if (state->action & MSN_ADD_BUDDY) {
+				msn_userlist_add_buddy(soapconn->session->userlist,
+						       state->who,
+						       state->new_group_name);
+				msn_callback_state_free(state);
+				return;
+			}
+			
+			if (state->action & MSN_MOVE_BUDDY) {
+				msn_add_contact_to_group(soapconn->session->contact, state, state->who, guid); 
+				return;
+			}
+		}
+		
+		if (state->action & MSN_DEL_GROUP) {
+			GList *l;
+			
+			msn_userlist_remove_group_id(soapconn->session->userlist, state->guid);
+			for (l = userlist->users; l != NULL; l = l->next) {
+				msn_user_remove_group_id( (MsnUser *)l->data, state->guid);
+			}
+			
+		}
+			
+		msn_callback_state_free(state);
+	}
+	
+	msn_soap_free_read_buf(soapconn);
 }
 
 static void
@@ -1058,66 +1463,145 @@
 {
 	MsnSoapConn * soapconn = data;	
 
-	purple_debug_info("MSNCL","Finished sending Add Group\n");
+	purple_debug_info("MSN CL","Sent group request.\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)
+/* add group */
+void
+msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_name)
 {
 	MsnSoapReq *soap_request;
 	MsnContact *contact;
 	char *body = NULL;
+	gchar *escaped_group_name;
 
 	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);
+	g_return_if_fail(group_name != NULL);
+	
 	contact = session->contact;
-	purple_debug_info("::","msn_del_group()\n");
+	purple_debug_info("MSN CL","Adding group %s to contact list.\n", group_name);
+
+	if (state == NULL) {
+		state = msn_callback_state_new();
+	}
 
-	body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE,guid);
+	msn_callback_state_set_action(state, MSN_ADD_GROUP);
+	msn_callback_state_set_new_group_name(state, group_name);
+
+	/* escape group name's html special chars so it can safely be sent
+	* in a XML SOAP request
+	*/
+	escaped_group_name = g_markup_escape_text(group_name, -1);
+	body = g_strdup_printf(MSN_GROUP_ADD_TEMPLATE, escaped_group_name);
+	g_free(escaped_group_name);
+
 	/*build SOAP and POST it*/
 	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-					MSN_ADDRESS_BOOK_POST_URL,MSN_GROUP_DEL_SOAP_ACTION,
+					MSN_ADDRESS_BOOK_POST_URL,
+					MSN_GROUP_ADD_SOAP_ACTION,
 					body,
+					state,
 					msn_group_read_cb,
 					msn_group_written_cb);
 	msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+	
+	g_free(body);
+}
 
+/* delete group */
+void
+msn_del_group(MsnSession *session, const gchar *group_name)
+{
+	MsnSoapReq *soap_request;
+	MsnContact *contact;
+	MsnCallbackState *state;
+	char *body = NULL;
+	const gchar *guid;
+
+	g_return_if_fail(session != NULL);
+	
+	g_return_if_fail(group_name != NULL);
+	contact = session->contact;
+	purple_debug_info("MSN CL","Deleting group %s from contact list\n", group_name);
+	
+	guid = msn_userlist_find_group_id(session->userlist, group_name);
+	
+	/* if group uid we need to del is NULL, 
+	*  we need to delete nothing
+	*/
+	if (guid == NULL) {
+		purple_debug_info("MSN CL", "Group %s guid not found, returning.\n", group_name);
+		return;
+	}
+	state = msn_callback_state_new();
+	msn_callback_state_set_action(state, MSN_DEL_GROUP);
+	msn_callback_state_set_guid(state, guid);
+	
+	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,
+					    state,
+					    msn_group_read_cb,
+					    msn_group_written_cb);
+	msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+	g_free(body);
+}
+
+/* rename group */
+void
+msn_contact_rename_group(MsnSession *session, const char *old_group_name, const char *new_group_name)
+{
+	MsnSoapReq *soap_request;
+	MsnContact *contact;
+	gchar * escaped_group_name, *body = NULL;
+	const gchar * guid;
+	MsnCallbackState *state = msn_callback_state_new();
+	
+	g_return_if_fail(session != NULL);
+	g_return_if_fail(session->userlist != NULL);
+	g_return_if_fail(old_group_name != NULL);
+	g_return_if_fail(new_group_name != NULL);
+	
+	contact = session->contact;
+	purple_debug_info("MSN CL", "Renaming group %s to %s.\n", old_group_name, new_group_name);
+	
+	guid = msn_userlist_find_group_id(session->userlist, old_group_name);
+	if (guid == NULL)
+		return;
+	
+	msn_callback_state_set_action(state, MSN_RENAME_GROUP);
+	msn_callback_state_set_guid(state, guid);
+	msn_callback_state_set_new_group_name(state, new_group_name);
+	
+	/* escape group name's html special chars so it can safely be sent
+	 * in a XML SOAP request
+	*/
+	escaped_group_name = g_markup_escape_text(new_group_name, -1);
+	
+	body = g_strdup_printf(MSN_GROUP_RENAME_TEMPLATE, guid, escaped_group_name);
+	
+	soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+					    MSN_ADDRESS_BOOK_POST_URL,
+					    MSN_GROUP_RENAME_SOAP_ACTION,
+					    body,
+					    state,
+					    msn_group_read_cb,
+					    msn_group_written_cb);
+	msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+	
+	g_free(escaped_group_name);
 	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_soap_init(soapconn, MSN_CONTACT_SERVER, 1,
 					msn_contact_login_connect_cb,
 					msn_contact_login_error_cb);
 }
--- a/libpurple/protocols/msn/contact.h	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/contact.h	Tue Aug 28 03:54:18 2007 +0000
@@ -27,7 +27,8 @@
 
 #define MSN_CONTACT_SERVER	"omega.contacts.msn.com"
 
-/*get contact list soap request template*/
+/* Get Contact List */
+
 #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>"\
@@ -64,9 +65,10 @@
 /************************************************
  * Address Book SOAP
  * *********************************************/
+
 #define MSN_ADDRESS_BOOK_POST_URL	"/abservice/abservice.asmx"
 
-/* Add an address book template */
+/* Create AddressBook template */
 #define MSN_ADD_ADDRESSBOOK_SOAP_ACTION     "http://www.msn.com/webservices/AddressBook/ABAdd"
 
 #define MSN_ADD_ADDRESSBOOK_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
@@ -93,9 +95,9 @@
 	"</soap:Body>"\
 "</soap:Envelope>"
 
-/* get addressbook soap request template */
+/* Get AddressBook */
 #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_FULL_TIME	"0001-01-01T00:00:00.0000000-08:00"
 #define MSN_GET_ADDRESS_UPDATE_XML "<deltasOnly>true</deltasOnly>"\
 	"<lastChange>%s</lastChange>"
 
@@ -110,7 +112,7 @@
 		"<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
 			"<IsMigration>false</IsMigration>"\
-			"<PartnerScenario>Initial</PartnerScenario>"\
+			"<PartnerScenario>%s</PartnerScenario>"\
 		"</ABApplicationHeader>"\
 		"<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
 			"<ManagedGroupRequest>false</ManagedGroupRequest>"\
@@ -125,6 +127,33 @@
 	"</soap:Body>"\
 "</soap:Envelope>"
 
+
+/*	Send this shit after adding a contact (with ABGroupContactAdd or something) damnit!	
+<?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>
+		<ABFindAll xmlns="http://www.msn.com/webservices/AddressBook">
+			<abId>00000000-0000-0000-0000-000000000000</abId>
+			<abView>Full</abView>
+			<deltasOnly>true</deltasOnly>
+			<lastChange>2007-08-22T06:19:36.84-07:00</lastChange>
+			<dynamicItemView>Gleam</dynamicItemView>
+			<dynamicItemLastChange>0001-01-01T00:00:00</dynamicItemLastChange>
+		</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\"?>"\
@@ -149,82 +178,146 @@
 	"</soap:Body>"\
 "</soap:Envelope>"
 
+
 /*******************************************************
- * Contact Add/del SOAP Action
+ * Contact Management SOAP actions
  *******************************************************/
-/*add conatct soap request*/
+
+/* Add a new contact t*/
 #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_CONTACT_LIVE_PENDING_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><contactType>LivePending</contactType><passportName>%s</passportName><isMessengerUser>true</isMessengerUser></contactInfo></Contact>"
+
+#define MSN_CONTACT_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+				"<contactInfo>"\
+					"<passportName>%s</passportName>"\
+					"<isSmtp>false</isSmtp>"\
+					"<isMessengerUser>true</isMessengerUser>"\
+				"</contactInfo>"\
+			"</Contact>"
+
+#define MSN_CONTACT_DISPLAYNAME_XML	"<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><displayName>%s</displayName><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*/
+/* Add a contact to a group */
 #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>"
+#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*/
+/* Delete a contact from the Contact List */
 #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_CONTACT_ID_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>"
+/* Remove a contact from a group */
+#define MSN_CONTACT_DEL_GROUP_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABGroupContactDelete"
+#define MSN_CONTACT_DEL_GROUP_TEMPLATE	"<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts><groupFilter><groupIds><guid>%s</guid></groupIds></groupFilter></ABGroupContactDelete></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>"
+
+/* 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>"
+
 
 /*******************************************************
- * Contact Block/Unblock SOAP Action
+ * Contact Block/Unblock SOAP actions
  *******************************************************/
-/*block means delete from allow list and add contact to block list*/
+
+/* 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*/
+
+/* 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*/
+/* 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
+ * Group management SOAP actions
  *******************************************************/
-/*add a group*/
+
+/* 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*/
+/* 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>"
 
+/* change a group's name */
+#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
+} MsnCallbackAction;
 
 typedef struct _MsnContact MsnContact;
 
@@ -235,24 +328,66 @@
 	MsnSoapConn *soapconn;
 };
 
+typedef struct _MsnCallbackState MsnCallbackState;
+
+struct _MsnCallbackState
+{
+	gchar * who;
+	gchar * old_group_name;
+	gchar * new_group_name;
+	gchar * guid;
+	MsnListId list_id;
+	MsnCallbackAction action;
+};
+
+typedef enum 
+{
+	MSN_AB_INITIAL,
+	MSN_AB_SAVE_CONTACT
+} MsnAddressBookAction;
+
+
 /************************************************
  * function prototype
  ************************************************/
 MsnContact * msn_contact_new(MsnSession *session);
 void msn_contact_destroy(MsnContact *contact);
 
+MsnCallbackState * msn_callback_state_new(void);
+void msn_callback_state_free(MsnCallbackState *state);
+void msn_callback_state_set_who(MsnCallbackState *state, const gchar *who);
+void msn_callback_state_set_old_group_name(MsnCallbackState *state,
+					   const gchar *old_group_name);
+void msn_callback_state_set_new_group_name(MsnCallbackState *state, 
+					   const gchar *new_group_name);
+void msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid);
+void msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id);
+void msn_callback_state_set_action(MsnCallbackState *state, 
+				   MsnCallbackAction action);
+
 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);
+void msn_get_address_book(MsnContact *contact, MsnAddressBookAction abaction,
+			  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);
+void msn_update_contact(MsnContact *contact, const char* nickname);
 
+void msn_add_contact(MsnContact *contact, MsnCallbackState *state, 
+		     const char *passport, const char *displayname);
+void msn_delete_contact(MsnContact *contact, const char *contactId);
 
+void msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, 
+			      const char *passport, const char *groupId);
+void msn_del_contact_from_group(MsnContact *contact, const char *passport, 
+				const char *group_name);
 /*group operation*/
-void msn_add_group(MsnSession *session,const char* group_name);
-void msn_del_group(MsnSession *session,const char *guid);
+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);
@@ -260,5 +395,5 @@
 
 void msn_contact_connect_init(MsnSoapConn *soapconn);
 
-#endif/* _MSN_CMDPROC_H_*/
+#endif /* _MSN_CONTACT_H_ */
 
--- a/libpurple/protocols/msn/dialog.c	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/dialog.c	Tue Aug 28 03:54:18 2007 +0000
@@ -69,7 +69,7 @@
 		MsnSession *session = data->gc->proto_data;
 		MsnUserList *userlist = session->userlist;
 
-		msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, data->group);
+		msn_userlist_add_buddy(userlist, data->who, data->group);
 	}
 
 	g_free(data->group);
@@ -87,10 +87,13 @@
 		MsnSession *session = data->gc->proto_data;
 		MsnUserList *userlist = session->userlist;
 
-		msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->group);
+		if (data->group == NULL) {
+			msn_userlist_rem_buddy_from_list(userlist, data->who, MSN_LIST_FL);
+		} else {
+			g_free(data->group);
+		}
 	}
 
-	g_free(data->group);
 	g_free(data->who);
 	g_free(data);
 }
@@ -109,7 +112,7 @@
 
 	data        = g_new0(MsnAddRemData, 1);
 	data->who   = g_strdup(passport);
-	data->group = g_strdup(group_name);
+	data->group = group_name != NULL ? g_strdup(group_name) : NULL;
 	data->gc    = gc;
 
 	msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"),
--- a/libpurple/protocols/msn/msn.c	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/msn.c	Tue Aug 28 03:54:18 2007 +0000
@@ -1061,7 +1061,7 @@
 	userlist = session->userlist;
 	who = msn_normalize(gc->account, buddy->name);
 
-	purple_debug_info("MSNP14","add user:{%s} to group:{%s}\n",who,group->name);
+	purple_debug_info("MSN","Add user:%s to group:%s\n", who, group->name);
 	if (!session->logged_in)
 	{
 #if 0
@@ -1095,8 +1095,7 @@
 	/* XXX - Would group ever be NULL here?  I don't think so...
 	 * shx: Yes it should; MSN handles non-grouped buddies, and this is only
 	 * internal. */
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_FL,
-						   group ? group->name : NULL);
+	msn_userlist_add_buddy(userlist, who, group ? group->name : NULL);
 }
 
 static void
@@ -1112,7 +1111,7 @@
 		return;
 
 	/* XXX - Does buddy->name need to be msn_normalize'd here?  --KingAnt */
-	msn_userlist_rem_buddy(userlist, buddy->name, MSN_LIST_FL, group->name);
+	msn_userlist_rem_buddy(userlist, buddy->name);
 }
 
 static void
@@ -1130,9 +1129,9 @@
 		return;
 
 	if (user != NULL && user->list_op & MSN_LIST_BL_OP)
-		msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
+		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
 
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
+	msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
 }
 
 static void
@@ -1150,9 +1149,9 @@
 		return;
 
 	if (user != NULL && user->list_op & MSN_LIST_AL_OP)
-		msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
+		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
 
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
+	msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
 }
 
 static void
@@ -1170,10 +1169,10 @@
 
 	user = msn_userlist_find_user(userlist, who);
 
-	msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
+	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
 
 	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
-		msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
+		msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
 }
 
 static void
@@ -1191,10 +1190,10 @@
 
 	user = msn_userlist_find_user(userlist, who);
 
-	msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
+	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
 
 	if (user != NULL && user->list_op & MSN_LIST_RL_OP)
-		msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
+		msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
 }
 
 static void
@@ -1337,27 +1336,20 @@
 				 PurpleGroup *group, GList *moved_buddies)
 {
 	MsnSession *session;
-	MsnCmdProc *cmdproc;
-	const char *old_gid;
-	const char *enc_new_group_name;
 
 	session = gc->proto_data;
-	cmdproc = session->notification->cmdproc;
-	enc_new_group_name = purple_url_encode(group->name);
-
-	purple_debug_info("MSNP14","rename group:old{%s},new{%s}",old_name,enc_new_group_name);
-	old_gid = msn_userlist_find_group_id(session->userlist, old_name);
-
-	if (old_gid != NULL)
+	
+	g_return_if_fail(session != NULL);
+	g_return_if_fail(session->userlist != NULL);
+	
+	if (msn_userlist_find_group_with_name(session->userlist, old_name) != NULL)
 	{
-		/*find a Group*/
-		msn_cmdproc_send(cmdproc, "REG", "%d %s 0", old_gid,
-						 enc_new_group_name);
+		msn_contact_rename_group(session, old_name, group->name);
 	}
 	else
 	{
-		/*not found*/
-		msn_cmdproc_send(cmdproc, "ADG", "%s 0", enc_new_group_name);
+		/* not found */
+		msn_add_group(session, NULL, group->name);
 	}
 }
 
@@ -1417,22 +1409,18 @@
 {
 	MsnSession *session;
 	MsnCmdProc *cmdproc;
-	const char *group_id;
 
 	session = gc->proto_data;
 	cmdproc = session->notification->cmdproc;
 
 	/*we can't delete the default group*/
-	if(!strcmp(group->name,MSN_INDIVIDUALS_GROUP_NAME)||
-		!strcmp(group->name,MSN_NON_IM_GROUP_NAME))
+	if(!strcmp(group->name, MSN_INDIVIDUALS_GROUP_NAME)||
+		!strcmp(group->name, MSN_NON_IM_GROUP_NAME))
 	{
 		return ;
 	}
-	group_id = msn_userlist_find_group_id(session->userlist, group->name);
-	if (group_id != NULL)
-	{
-		msn_del_group(session,group_id);
-	}
+	
+	msn_del_group(session, group->name);
 }
 
 /**
--- a/libpurple/protocols/msn/notification.c	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/notification.c	Tue Aug 28 03:54:18 2007 +0000
@@ -38,9 +38,10 @@
  * 	Local Function Prototype
  ****************************************************************************/
 
-static void msn_notification_fqy_yahoo(MsnSession *session, const char *passport);
 static void msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len);
-static void msn_add_contact_xml(MsnSession *session, xmlnode *mlNode, const char *passport, int list_op, MsnUserType type);
+static void
+msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport,
+					 MsnListOp list_op, MsnUserType type);
 
 /**************************************************************************
  * Main
@@ -585,7 +586,7 @@
  **************************************************************************/
 /* add contact to xmlnode */
 static void
-msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport,int list_op, MsnUserType type)
+msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnUserType type)
 {
 	xmlnode *d_node,*c_node;
 	char **tokens;
@@ -593,7 +594,7 @@
 	char *list_op_str,*type_str;
 
 	purple_debug_info("::","msn_add_contact_xml()\n");
-	purple_debug_info("MSNP14","Passport: %s, type: %d\n",passport, type);
+	purple_debug_info("MSNP14","Passport: %s, type: %d\n", passport, type);
 	tokens = g_strsplit(passport, "@", 2);
 	email = tokens[0];
 	domain = tokens[1];
@@ -652,8 +653,8 @@
 {
 	MsnTransaction *trans;
 	purple_debug_info("::","msn_notification_post_adl()\n");
-	purple_debug_info("MSNP14","Sending ADL with payload: %s\n",payload);
-	trans = msn_transaction_new(cmdproc, "ADL","%d",strlen(payload));
+	purple_debug_info("MSNP14","Sending ADL with payload: %s\n", payload);
+	trans = msn_transaction_new(cmdproc, "ADL","%d", strlen(payload));
 	msn_transaction_set_payload(trans, payload, strlen(payload));
 	msn_cmdproc_send_trans(cmdproc, trans);
 }
@@ -676,8 +677,7 @@
 	xmlnode_set_attrib(adl_node, "l", "1");
 
 
-	if ( session->userlist->users == NULL ) {
-		/* we have no users yet in our contact list */
+/*	if ( session->userlist->users == NULL ) {
 		payload = xmlnode_to_str(adl_node,&payload_len);
 
 		msn_notification_post_adl(session->notification->cmdproc,
@@ -686,7 +686,7 @@
 		g_free(payload);
 		xmlnode_free(adl_node);
 	}
-	else {
+	else { */
 	/*get the userlist*/
 	for (l = session->userlist->users; l != NULL; l = l->next){
 		user = l->data;
@@ -715,6 +715,14 @@
 			}
 		}
 	}
+
+	if (adl_count == 0) {
+		payload = xmlnode_to_str(adl_node,&payload_len);
+
+		msn_notification_post_adl(session->notification->cmdproc, payload, payload_len);
+
+		g_free(payload);
+		xmlnode_free(adl_node);
 	}
 
 	display_name = purple_connection_get_display_name(session->account->gc);
@@ -727,8 +735,8 @@
 }
 
 /*Post FQY to NS,Inform add a Yahoo User*/
-static void
-msn_notification_fqy_yahoo(MsnSession *session, const char *passport)
+void
+msn_notification_send_fqy(MsnSession *session, const char *passport)
 {
 	MsnTransaction *trans;
 	MsnCmdProc *cmdproc;
@@ -741,8 +749,8 @@
 	email = tokens[0];
 	domain = tokens[1];
 
-	payload = g_strdup_printf("<ml><d n=\"%s\"><c n=\"%s\"/></d></ml>",domain,email);
-	trans = msn_transaction_new(cmdproc, "FQY","%d",strlen(payload));
+	payload = g_strdup_printf("<ml><d n=\"%s\"><c n=\"%s\"/></d></ml>", domain, email);
+	trans = msn_transaction_new(cmdproc, "FQY","%d", strlen(payload));
 	msn_transaction_set_payload(trans, payload, strlen(payload));
 	msn_cmdproc_send_trans(cmdproc, trans);
 
@@ -753,14 +761,11 @@
 static void
 blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	purple_debug_info("MSNP14","Process BLP\n");
 }
 
 static void
 adl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
-	purple_debug_info("MSNP14","Process ADL\n");
-
 	msn_session_finish_login(cmdproc->session);
 
 	return;
@@ -788,15 +793,18 @@
 fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
 			 size_t len)
 {
-	purple_debug_info("MSNP14","FQY payload{%s}\n",payload);
-	msn_notification_post_adl(cmdproc,payload,len);
+	purple_debug_info("MSN Notification","FQY payload:\n%s\n", payload);
+	g_return_if_fail(cmdproc->session != NULL);
+	g_return_if_fail(cmdproc->session->contact != NULL);
+//	msn_notification_post_adl(cmdproc, payload, len);
+	msn_get_address_book(cmdproc->session->contact, MSN_AB_SAVE_CONTACT, NULL, NULL);
 }
 
 static void
 fqy_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
 {
 	purple_debug_info("MSNP14","Process FQY\n");
-	cmdproc->last_cmd->payload_cb  = fqy_cmd_post;
+	cmdproc->last_cmd->payload_cb = fqy_cmd_post;
 }
 
 static void
@@ -913,22 +921,20 @@
 
 	msn_group_new(session->userlist, cmd->params[3], group_name);
 
-	/* There is a user that must me moved to this group */
+	/* There is a user that must be moved to this group */
 	if (cmd->trans->data)
 	{
 		/* msn_userlist_move_buddy(); */
 		MsnUserList *userlist = cmdproc->session->userlist;
-		MsnMoveBuddy *data = cmd->trans->data;
+		MsnCallbackState *data = cmd->trans->data;
 
 		if (data->old_group_name != NULL)
 		{
-			msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->old_group_name);
+			msn_userlist_move_buddy(userlist, data->who, data->old_group_name, group_name);
 			g_free(data->old_group_name);
+		} else {
+			// msn_add_contact_to_group(userlist, data, data->who, group_name);
 		}
-
-		msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, group_name);
-		g_free(data->who);
-
 	}
 }
 
@@ -1146,8 +1152,6 @@
 {
 	MsnSession *session = cmdproc->session;
 	const char *type, *value, *friendlyname;
-	gchar *soapname, *soapbody;
-	MsnSoapReq *soap_request;
 
 	purple_debug_info("MSN Notification", "prp_cmd()\n");
 
@@ -1177,24 +1181,9 @@
 			type = cmd->params[1];
 			if (!strcmp(type, "MFN")) {
 				friendlyname = purple_url_decode(cmd->params[2]);
-				soapname = g_markup_escape_text(friendlyname,-1);
-				soapbody = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, soapname);
+				
+				msn_update_contact(session->contact, friendlyname);
 
-				soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
-								    MSN_ADDRESS_BOOK_POST_URL,
-								    MSN_CONTACT_UPDATE_SOAP_ACTION,
-								    soapbody,
-								    NULL,
-								    NULL);
-
-				session->contact->soapconn->read_cb = NULL;
-
-				msn_soap_post(session->contact->soapconn,
-					      soap_request,
-					      msn_contact_connect_init);
-
-				g_free(soapbody);
-				g_free(soapname);
 				purple_connection_set_display_name(
 					purple_account_get_connection(session->account),
 					friendlyname);
@@ -1918,11 +1907,11 @@
 }
 
 void
-msn_notification_add_buddy(MsnNotification *notification, const char *list,
-						   const char *who, const char *store_name,
-						   const char *group_id)
+msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list_id,
+						   	  const char *who)
 {
 	MsnCmdProc *cmdproc;
+	MsnListOp list_op = 1 << list_id;
 	xmlnode *adl_node;
 	char *payload;
 	int payload_len;
@@ -1932,44 +1921,39 @@
 	adl_node = xmlnode_new("ml");
 	adl_node->child = NULL;
 
-	msn_add_contact_xml(notification->session,adl_node,who,1,MSN_USER_TYPE_PASSPORT);
+	msn_add_contact_xml(notification->session, adl_node, who, list_op, 
+						MSN_USER_TYPE_PASSPORT);
 
 	payload = xmlnode_to_str(adl_node,&payload_len);
 	xmlnode_free(adl_node);
-	if (msn_user_is_yahoo(notification->session->account,who))
-	{
-		msn_notification_fqy_yahoo(notification->session, who);
-	}
-	else
-	{
-		msn_notification_post_adl(notification->servconn->cmdproc,
-							payload,payload_len);
-	}
+	
+	msn_notification_post_adl(notification->servconn->cmdproc,
+						payload,payload_len);
 }
 
 void
-msn_notification_rem_buddy(MsnNotification *notification, const char *list,
-						   const char *who, const char *group_id)
+msn_notification_rem_buddy_from_list(MsnNotification *notification, MsnListId list_id,
+						   const char *who)
 {
 	MsnCmdProc *cmdproc;
 	MsnTransaction *trans;
+	MsnListOp list_op = 1 << list_id;
 	xmlnode *rml_node;
 	char *payload;
 	int payload_len;
 
-	purple_debug_info("::","msn_notification_rem_buddy()\n");
 	cmdproc = notification->servconn->cmdproc;
 
 	rml_node = xmlnode_new("ml");
 	rml_node->child = NULL;
 
-	msn_add_contact_xml(notification->session,rml_node,who,1,MSN_USER_TYPE_PASSPORT);
+	msn_add_contact_xml(notification->session, rml_node, who, list_op, MSN_USER_TYPE_PASSPORT);
 
-	payload = xmlnode_to_str(rml_node,&payload_len);
+	payload = xmlnode_to_str(rml_node, &payload_len);
 	xmlnode_free(rml_node);
 
-	purple_debug_info("MSNP14","Send RML with payload {%s}\n",payload);
-	trans = msn_transaction_new(cmdproc, "RML","%d",strlen(payload));
+	purple_debug_info("MSN Notification","Send RML with payload:\n%s\n", payload);
+	trans = msn_transaction_new(cmdproc, "RML","%d", strlen(payload));
 	msn_transaction_set_payload(trans, payload, strlen(payload));
 	msn_cmdproc_send_trans(cmdproc, trans);
 }
--- a/libpurple/protocols/msn/notification.h	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/notification.h	Tue Aug 28 03:54:18 2007 +0000
@@ -53,16 +53,17 @@
 void msn_notification_end(void);
 void msn_notification_init(void);
 
-void msn_notification_add_buddy(MsnNotification *notification,
-								const char *list, const char *who,
-								const char *store_name, const char *group_id);
-void msn_notification_rem_buddy(MsnNotification *notification,
-								const char *list, const char *who,
-								const char *group_id);
+void msn_notification_add_buddy_to_list(MsnNotification *notification,
+					MsnListId list_id, const char *who);
+void msn_notification_rem_buddy_from_list(MsnNotification *notification,
+					  MsnListId list_id, const char *who);
+
+void msn_notification_send_fqy(MsnSession *session, const char *passport);
+
 MsnNotification *msn_notification_new(MsnSession *session);
 void msn_notification_destroy(MsnNotification *notification);
 gboolean msn_notification_connect(MsnNotification *notification,
-							  const char *host, int port);
+				  const char *host, int port);
 void msn_notification_disconnect(MsnNotification *notification);
 void msn_notification_dump_contact(MsnSession *session);
 
--- a/libpurple/protocols/msn/oim.c	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/oim.c	Tue Aug 28 03:54:18 2007 +0000
@@ -310,8 +310,10 @@
 					msg_body
 					);
 	soap_request = msn_soap_request_new(MSN_OIM_SEND_HOST,
-					MSN_OIM_SEND_URL,MSN_OIM_SEND_SOAP_ACTION,
+					MSN_OIM_SEND_URL,
+					MSN_OIM_SEND_SOAP_ACTION,
 					soap_body,
+					NULL,
 					msn_oim_send_read_cb,
 					msn_oim_send_written_cb);
 	g_free(mspauth);
@@ -368,8 +370,10 @@
 					msgid
 					);
 	soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST,
-					MSN_OIM_RETRIEVE_URL,MSN_OIM_DEL_SOAP_ACTION,
+					MSN_OIM_RETRIEVE_URL,
+					MSN_OIM_DEL_SOAP_ACTION,
 					soap_body,
+					NULL,
 					msn_oim_delete_read_cb,
 					msn_oim_delete_written_cb);
 	msn_soap_post(oim->retrieveconn,soap_request,msn_oim_retrieve_connect_init);
@@ -602,8 +606,10 @@
 					msgid
 					);
 	soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST,
-					MSN_OIM_RETRIEVE_URL,MSN_OIM_GET_SOAP_ACTION,
+					MSN_OIM_RETRIEVE_URL,
+					MSN_OIM_GET_SOAP_ACTION,
 					soap_body,
+					NULL,
 					msn_oim_get_read_cb,
 					msn_oim_get_written_cb);
 	msn_soap_post(oim->retrieveconn,soap_request,msn_oim_retrieve_connect_init);
--- a/libpurple/protocols/msn/session.c	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/session.c	Tue Aug 28 03:54:18 2007 +0000
@@ -277,10 +277,10 @@
 		if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
 			continue;
 		group_name = group->name;
-		if(!g_strcasecmp(group_name, MSN_INDIVIDUALS_GROUP_NAME)
-						||	!g_strcasecmp(group_name,MSN_NON_IM_GROUP_NAME)){
-			continue;
-		}
+//		if(!g_strcasecmp(group_name, MSN_INDIVIDUALS_GROUP_NAME)
+//						||	!g_strcasecmp(group_name,MSN_NON_IM_GROUP_NAME)){
+//			continue;
+//		}
 		for(cnode = gnode->child; cnode; cnode = cnode->next) {
 			if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
 				continue;
@@ -303,7 +303,7 @@
 						purple_debug_info("MSNP14","remote user:{%s}\n",b->name);
 						group_id = msn_userlist_find_group_id(remote_user->userlist,
 								group_name);
-						if(group_id == NULL){
+						if (group_id == NULL) {
 							continue;
 						}
 						purple_debug_info("MSNP14","group_id:{%s}\n",group_id);
--- a/libpurple/protocols/msn/soap.c	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/soap.c	Tue Aug 28 03:54:18 2007 +0000
@@ -486,6 +486,8 @@
 void 
 msn_soap_free_read_buf(MsnSoapConn *soapconn)
 {
+	g_return_if_fail(soapconn != NULL);
+	
 	if (soapconn->read_buf) {
 		g_free(soapconn->read_buf);
 	}
@@ -497,13 +499,23 @@
 void
 msn_soap_free_write_buf(MsnSoapConn *soapconn)
 {
-	if(soapconn->write_buf){
+	g_return_if_fail(soapconn != NULL);
+
+	if (soapconn->write_buf) {
 		g_free(soapconn->write_buf);
 	}
 	soapconn->write_buf = NULL;
 	soapconn->written_len = 0;
 }
 
+void
+msn_soap_free_data_cb(MsnSoapConn *soapconn)
+{
+	if (soapconn->data_cb) {
+		g_free(soapconn->data_cb);
+	}
+}
+
 /*Soap write process func*/
 static void
 msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond)
@@ -582,7 +594,7 @@
 /* New a soap request*/
 MsnSoapReq *
 msn_soap_request_new(const char *host,const char *post_url,const char *soap_action,
-				const char *body,
+				const char *body, const gpointer data_cb,
 				PurpleInputFunction read_cb,PurpleInputFunction written_cb)
 {
 	MsnSoapReq *request;
@@ -594,6 +606,7 @@
 	request->login_path = g_strdup(post_url);
 	request->soap_action		= g_strdup(soap_action);
 	request->body		= g_strdup(body);
+	request->data_cb 	= data_cb;
 	request->read_cb	= read_cb;
 	request->written_cb	= written_cb;
 
@@ -610,6 +623,7 @@
 	g_free(request->login_path);
 	g_free(request->soap_action);
 	g_free(request->body);
+	g_free(request->data_cb);
 	request->read_cb	= NULL;
 	request->written_cb	= NULL;
 
@@ -649,7 +663,7 @@
 		msn_soap_connect(soapconn);
 		return;
 	}
-	purple_debug_misc("MSN SOAP","Connected to SOAP server!\n");
+	purple_debug_misc("MSN SOAP","Connected to SOAP server\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.
@@ -710,8 +724,9 @@
 	
 	g_free(soap_head);
 	/*free read buffer*/
-	msn_soap_free_read_buf(soapconn);
+	// msn_soap_free_read_buf(soapconn);
 	/*post it to server*/
-	msn_soap_write(soapconn,request_str,request->written_cb);
+	soapconn->data_cb = request->data_cb;
+	msn_soap_write(soapconn, request_str, request->written_cb);
 }
 
--- a/libpurple/protocols/msn/soap.h	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/soap.h	Tue Aug 28 03:54:18 2007 +0000
@@ -49,6 +49,7 @@
 
 typedef void (*MsnSoapConnectInitFunction)(MsnSoapConn *);
 
+
 struct _MsnSoapReq{
 	/*request sequence*/
 	int	 id;
@@ -58,7 +59,8 @@
 	char *soap_action;
 
 	char *body;
-
+	
+	gpointer data_cb;
 	PurpleInputFunction read_cb;
 	PurpleInputFunction written_cb;
 };
@@ -103,6 +105,8 @@
 	gsize need_to_read;
 	PurpleInputFunction read_cb;
 
+	gpointer data_cb;
+
 	/*HTTP reply body part*/
 	char *body;
 	int body_len;
@@ -111,9 +115,10 @@
 /*Function Prototype*/
 /*Soap Request Function */
 MsnSoapReq *msn_soap_request_new(const char *host, const char *post_url,
-								 const char *soap_action, const char *body,
-								 PurpleInputFunction read_cb,
-								 PurpleInputFunction written_cb);
+				 const char *soap_action, const char *body,
+				 const gpointer data_cb,
+				 PurpleInputFunction read_cb,
+				 PurpleInputFunction written_cb);
 
 void msn_soap_request_free(MsnSoapReq *request);
 void msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request);
@@ -134,7 +139,8 @@
 void msn_soap_write(MsnSoapConn * soapconn, char *write_buf, PurpleInputFunction written_cb);
 void msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request,MsnSoapConnectInitFunction msn_soap_init_func);
 
-void  msn_soap_free_read_buf(MsnSoapConn *soapconn);
+void msn_soap_free_data_cb(MsnSoapConn *soapconn);
+void msn_soap_free_read_buf(MsnSoapConn *soapconn);
 void msn_soap_free_write_buf(MsnSoapConn *soapconn);
 void msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
 void msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond);
--- a/libpurple/protocols/msn/user.c	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/user.c	Tue Aug 28 03:54:18 2007 +0000
@@ -309,7 +309,7 @@
 
 	group_name = msn_userlist_find_group_name(userlist, group_id);
 
-	purple_debug_info("User","group id:%s,name:%s,user:%s\n",group_id,group_name,passport);
+	purple_debug_info("User","group id:%s,name:%s,user:%s\n", group_id, group_name, passport);
 
 	g = purple_find_group(group_name);
 
--- a/libpurple/protocols/msn/userlist.c	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/userlist.c	Tue Aug 28 03:54:18 2007 +0000
@@ -42,8 +42,9 @@
 {
 	MsnSession *session = pa->gc->proto_data;
 	MsnUserList *userlist = session->userlist;
-
-	msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_AL, NULL);
+	
+	msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL);
+	msn_userlist_add_buddy(userlist, pa->who, NULL);
 
 	g_free(pa->who);
 	g_free(pa->friendly);
@@ -58,7 +59,7 @@
 		MsnSession *session = pa->gc->proto_data;
 		MsnUserList *userlist = session->userlist;
 
-		msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_BL, NULL);
+		msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_BL);
 	}
 
 	g_free(pa->who);
@@ -87,7 +88,7 @@
  **************************************************************************/
 
 static gboolean
-user_is_in_group(MsnUser *user, const char * group_id)
+msn_userlist_user_is_in_group(MsnUser *user, const char * group_id)
 {
 	if (user == NULL)
 		return FALSE;
@@ -102,25 +103,19 @@
 }
 
 static gboolean
-user_is_there(MsnUser *user, int list_id, const char * group_id)
+msn_userlist_user_is_in_list(MsnUser *user, int list_id)
 {
 	int list_op;
 
 	if (user == NULL)
 		return FALSE;
-
+	
 	list_op = 1 << list_id;
 
-	if (!(user->list_op & list_op))
+	if (user->list_op & list_op)
+		return TRUE;
+	else
 		return FALSE;
-
-	if (list_id == MSN_LIST_FL)
-	{
-		if (group_id != NULL)
-			return user_is_in_group(user, group_id);
-	}
-
-	return TRUE;
 }
 
 static const char*
@@ -151,32 +146,6 @@
 	return store_name;
 }
 
-static void
-msn_request_add_group(MsnUserList *userlist, const char *who,
-					  const char *old_group_name, const char *new_group_name)
-{
-	MsnSession *session;
-	MsnCmdProc *cmdproc;
-	MsnMoveBuddy *data;
-
-	session = userlist->session;
-	cmdproc = session->notification->cmdproc;
-	data = g_new0(MsnMoveBuddy, 1);
-
-	data->who = g_strdup(who);
-
-	if (old_group_name)
-	{
-		data->old_group_name = g_strdup(old_group_name);
-		/*delete the old group via SOAP action*/
-		msn_del_group(session,old_group_name);
-	}
-
-	/*add new group via SOAP action*/
-	msn_add_group(session, new_group_name);
-
-}
-
 /**************************************************************************
  * Server functions
  **************************************************************************/
@@ -463,8 +432,9 @@
 	{
 		user = msn_user_new(userlist, passport, userName);
 		msn_userlist_add_user(userlist, user);
+	} else {
+		msn_user_set_store_name(user, userName);
 	}
-	msn_user_set_store_name(user, userName);
 	return user;
 }
 
@@ -506,6 +476,7 @@
 msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group)
 {
 	userlist->groups = g_list_append(userlist->groups, group);
+	
 }
 
 void
@@ -605,138 +576,237 @@
 }
 
 void
-msn_userlist_rem_buddy(MsnUserList *userlist,
-					   const char *who, int list_id, const char *group_name)
+msn_userlist_rem_buddy(MsnUserList *userlist, const char *who)
 {
-	MsnUser *user;
-	const char *group_id;
-	const char *list;
-
+	MsnUser *user = NULL;
+	
+	g_return_if_fail(userlist != NULL);
+	g_return_if_fail(userlist->session != NULL);
+	g_return_if_fail(userlist->session->contact != NULL);
+	g_return_if_fail(who != NULL);
+	
 	user = msn_userlist_find_user(userlist, who);
 
-	g_return_if_fail(user != NULL);
-
-	/*delete the contact from address book via soap action*/
-	msn_delete_contact(userlist->session->contact,user->uid);
+	msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_FL);
 
-	group_id = NULL;
-
-	if (group_name != NULL)
-	{
-		group_id = msn_userlist_find_group_id(userlist, group_name);
+	/* delete the contact from address book via soap action */
+	if (user != NULL) {
+		msn_delete_contact(userlist->session->contact, user->uid);
+	}
+}
 
-		if (group_id == NULL)
-		{
-			/* Whoa, there is no such group. */
-			purple_debug_error("msn", "Group doesn't exist: %s\n", group_name);
-			return;
-		}
-	}
-
-	/* First we're going to check if not there. */
-	if (!(user_is_there(user, list_id, group_id)))
-	{
+void
+msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
+				 MsnListId list_id)
+{
+	MsnUser *user;
+	const gchar *list;
+	
+	user = msn_userlist_find_user(userlist, who);
+	
+	g_return_if_fail(user != NULL);
+	
+	if ( !msn_userlist_user_is_in_list(user, list_id)) {
 		list = lists[list_id];
-		purple_debug_error("msn", "User '%s' is not there: %s\n",
-						 who, list);
+		purple_debug_info("MSN Userlist", "User %s is not in list %s, not removing.\n", who, list);
 		return;
 	}
-
-	/* Then request the rem to the server. */
-	list = lists[list_id];
-
-	msn_notification_rem_buddy(userlist->session->notification, list, who, group_id);
+	
+	msn_notification_rem_buddy_from_list(userlist->session->notification, list_id, who);
 }
 
 /*add buddy*/
 void
-msn_userlist_add_buddy(MsnUserList *userlist,
-					   const char *who, int list_id,
-					   const char *group_name)
+msn_userlist_add_buddy(MsnUserList *userlist, const char *who, 
+					      const char *group_name)
 {
 	MsnUser *user;
-	const char *group_id;
-	const char *list;
-	const char *store_name;
+	MsnCallbackState *state = NULL;
+	const char *group_id = NULL, *new_group_name;
+	
+	new_group_name = group_name == NULL ? MSN_INDIVIDUALS_GROUP_NAME : group_name;
 
-	purple_debug_info("MSNP14", "userlist add buddy,name:{%s},group:{%s}\n",who ,group_name);
-	group_id = NULL;
+	g_return_if_fail(userlist != NULL);
+	g_return_if_fail(userlist->session != NULL);
+
+	
+	purple_debug_info("MSN Userlist", "Add user: %s to group: %s\n", who, new_group_name);
+
+	state = msn_callback_state_new();
+	msn_callback_state_set_who(state, who);
+	msn_callback_state_set_new_group_name(state, new_group_name);
 
 	if (!purple_email_is_valid(who))
 	{
 		/* only notify the user about problems adding to the friends list
 		 * maybe we should do something else for other lists, but it probably
 		 * won't cause too many problems if we just ignore it */
-		if (list_id == MSN_LIST_FL)
-		{
-			char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
-			purple_notify_error(NULL, NULL, str,
-							  _("The screen name specified is invalid."));
-			g_free(str);
-		}
+		
+		char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
+		
+		purple_notify_error(NULL, NULL, str,
+				  _("The screen name specified is invalid."));
+		g_free(str);
 
 		return;
 	}
 
-	if (group_name != NULL)
+	group_id = msn_userlist_find_group_id(userlist, new_group_name);
+
+	if (group_id == NULL)
 	{
-		group_id = msn_userlist_find_group_id(userlist, group_name);
+		/* Whoa, we must add that group first. */
+		purple_debug_info("MSN Userlist", "Adding user %s to a new group, creating group %s first\n", who, new_group_name);
+		
+		msn_callback_state_set_action(state, MSN_ADD_BUDDY);
 
-		if (group_id == NULL)
-		{
-			/* Whoa, we must add that group first. */
-			msn_request_add_group(userlist, who, NULL, group_name);
+		msn_add_group(userlist->session, state, new_group_name);
+		return;
+	} else {
+		msn_callback_state_set_guid(state, group_id);
+	}
+	
+
+	user = msn_userlist_find_add_user(userlist, who, who);
+
+	if ( msn_userlist_user_is_in_list(user, MSN_LIST_FL) ) {
+
+		purple_debug_info("MSN Userlist", "User %s already exists\n", who);
+
+		msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
+
+		if (msn_userlist_user_is_in_group(user, group_id)) {
+			purple_debug_info("MSN Userlist", "User %s is already in group %s, returning\n", who, new_group_name);
 			return;
 		}
 	}
+			
+	purple_debug_info("MSN Userlist", "Adding user: %s to group id: %s\n", who, group_id);
 
-	/* XXX: using _add_user here may not be correct (should add them in the
-	   ACK to the ADL command, and we might also want to make sure the user's groups
-	   are correct. but for now we need to make sure they exist early enough that
-	   the ILN command doesn't screw us up */
+	msn_callback_state_set_action(state, MSN_ADD_BUDDY);
+
+	/* Add contact in the Contact server with a SOAP request and if
+	   successful, send ADL with MSN_LIST_AL and MSN_LIST_FL and a FQY */
+	msn_add_contact_to_group(userlist->session->contact, state, who, group_id);
+}
+
+void
+msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who, 
+							MsnListId list_id)
+{
+	MsnUser *user = NULL;
+	const gchar *list;
+	MsnListOp list_op = 1 << list_id;
+
+	g_return_if_fail(userlist != NULL);
+	
 	user = msn_userlist_find_add_user(userlist, who, who);
-
+	
 	/* First we're going to check if it's already there. */
-	if (user_is_there(user, list_id, group_id))
+	if (msn_userlist_user_is_in_list(user, list_id))
 	{
 		list = lists[list_id];
-		purple_debug_error("msn", "User '%s' is already there: %s\n", who, list);
+		purple_debug_info("MSN Userlist", "User '%s' is already in list: %s\n", who, list);
 		return;
 	}
+	
+	//store_name = (user != NULL) ? get_store_name(user) : who;
+	
+	//purple_debug_info("MSN Userlist", "store_name = %s\n", store_name);
+	
+	/* XXX: see XXX above, this should really be done when we get the response from
+		the server */
+	
+	msn_user_set_op(user, list_op);
+	
+	msn_notification_add_buddy_to_list(userlist->session->notification, list_id, who);
+}
+
+gboolean
+msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who,
+				const char *group_name)
+{
+	MsnUser *user;
+	gchar * group_id;
+	
+	g_return_val_if_fail(userlist != NULL, FALSE);
+	g_return_val_if_fail(group_name != NULL, FALSE);
+	g_return_val_if_fail(who != NULL, FALSE);
+
+	purple_debug_info("MSN Userlist","Adding buddy with passport %s to group %s\n", who, group_name);
+
+	if ( (group_id = (gchar *)msn_userlist_find_group_id(userlist, group_name)) == NULL) {
+		purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name);
+		return FALSE;
+	}
 
-	store_name = (user != NULL) ? get_store_name(user) : who;
-
-	purple_debug_info("MSNCL", "store_name = %s\n",store_name);
+	if ( (user = msn_userlist_find_user(userlist, who)) == NULL) {
+		purple_debug_error("MSN Userlist", "User %s not found!", who);
+		return FALSE;
+	}
+	
+	msn_user_add_group_id(user, group_id);
 
-	/* XXX: see XXX above, this should really be done when we get the response from
-	   the server */
-	msn_user_set_op(user, list_id);
+	return TRUE;
+}
+
+
+gboolean
+msn_userlist_rem_buddy_from_group(MsnUserList *userlist, const char *who,
+				const char *group_name)
+{
+	const gchar * group_id;
+	MsnUser *user;
 
-	/* Then request the add to the server. */
-	list = lists[list_id];
+	g_return_val_if_fail(userlist != NULL, FALSE);
+	g_return_val_if_fail(group_name != NULL, FALSE);
+	g_return_val_if_fail(who != NULL, FALSE);
+	
+	purple_debug_info("MSN Userlist","Removing buddy with passport %s from group %s\n", who, group_name);
 
-	purple_debug_info("MSNP14", "Add user: %s to group id: %s\n",store_name ,group_id);
-	msn_add_contact(userlist->session->contact,who,group_id);
-	msn_notification_add_buddy(userlist->session->notification, list, who,
-							   store_name, group_id);
+	if ( (group_id = msn_userlist_find_group_id(userlist, group_name)) == NULL) {
+		purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name);
+		return FALSE;
+	}
+
+	if ( (user = msn_userlist_find_user(userlist, who)) == NULL) {
+		purple_debug_error("MSN Userlist", "User %s not found!", who);
+		return FALSE;
+	}
+
+	msn_user_remove_group_id(user, group_id);
+
+	return TRUE;
 }
 
 void
 msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
-						const char *old_group_name, const char *new_group_name)
+			const char *old_group_name, const char *new_group_name)
 {
 	const char *new_group_id;
+	MsnCallbackState *state = msn_callback_state_new();
+	
+	g_return_if_fail(userlist != NULL);
+	g_return_if_fail(userlist->session != NULL);
+	g_return_if_fail(userlist->session->contact != NULL);
+	
+	msn_callback_state_set_who(state, who);
+	msn_callback_state_set_action(state, MSN_MOVE_BUDDY);
+	msn_callback_state_set_old_group_name(state, old_group_name);
+	msn_callback_state_set_new_group_name(state, new_group_name);
 
 	new_group_id = msn_userlist_find_group_id(userlist, new_group_name);
 
 	if (new_group_id == NULL)
-	{
-		msn_request_add_group(userlist, who, old_group_name, new_group_name);
+	{		
+		msn_add_group(userlist->session, state, new_group_name);
 		return;
 	}
-
-	msn_userlist_add_buddy(userlist, who, MSN_LIST_FL, new_group_name);
-	msn_userlist_rem_buddy(userlist, who, MSN_LIST_FL, old_group_name);
+	
+	/* add the contact to the new group, and remove it from the old one in
+	 * the callback
+	*/
+	msn_add_contact_to_group(userlist->session->contact, state, who, new_group_id);
 }
 
 /*load userlist from the Blist file cache*/
--- a/libpurple/protocols/msn/userlist.h	Wed Aug 08 23:04:44 2007 +0000
+++ b/libpurple/protocols/msn/userlist.h	Tue Aug 28 03:54:18 2007 +0000
@@ -40,13 +40,6 @@
 
 } MsnListId;
 
-typedef struct
-{
-	char *who;
-	char *old_group_name;
-
-} MsnMoveBuddy;
-
 struct _MsnUserList
 {
 	MsnSession *session;
@@ -79,31 +72,39 @@
 
 void msn_userlist_add_user(MsnUserList *userlist, MsnUser *user);
 void msn_userlist_remove_user(MsnUserList *userlist, MsnUser *user);
-MsnUser *msn_userlist_find_user(MsnUserList *userlist,
-				const char *passport);
-MsnUser *msn_userlist_find_add_user(MsnUserList *userlist,
-				const char *passport,const char *userName);
+
+MsnUser * msn_userlist_find_user(MsnUserList *userlist, const char *passport);
+MsnUser * msn_userlist_find_add_user(MsnUserList *userlist,
+				const char *passport, const char *userName);
 
 void msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group);
 void msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group);
 MsnGroup *msn_userlist_find_group_with_id(MsnUserList *userlist, const char *id);
-MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist,
-											const char *name);
+MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist, const char *name);
 const char * msn_userlist_find_group_id(MsnUserList *userlist,
-							   const char *group_name);
-const char *msn_userlist_find_group_name(MsnUserList *userlist,
-										 const char *group_id);
+					const char *group_name);
+const char *msn_userlist_find_group_name(MsnUserList *userlist, const char *group_id);
 void msn_userlist_rename_group_id(MsnUserList *userlist, const char *group_id,
-								  const char *new_name);
+				  const char *new_name);
 void msn_userlist_remove_group_id(MsnUserList *userlist, const char *group_id);
 
-void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who,
-							int list_id, const char *group_name);
-void msn_userlist_add_buddy(MsnUserList *userlist, const char *who,
-							int list_id, const char *group_name);
+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_move_buddy(MsnUserList *userlist, const char *who,
-							 const char *old_group_name,
-							 const char *new_group_name);
+						    const char *old_group_name,
+						    const char *new_group_name);
+
+gboolean msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who,
+					 const char *group_name);
+gboolean msn_userlist_rem_buddy_from_group(MsnUserList *userlist,
+					   const char *who,
+					   const char *group_name);
+
+void msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who, 
+				    MsnListId list_id);
+void msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
+				      MsnListId list_id);
+
 void msn_userlist_load(MsnSession *session);
 
 #endif /* _MSN_USERLIST_H_ */