changeset 24625:eae0e194658e

merge of '834b032b518f6b4ab9148b946ca635214a9388d8' and 'fd954d6a415456dce86aa9443b24e1df25019bd7'
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Wed, 10 Dec 2008 06:58:10 +0000
parents 781723b2a170 (diff) 35d0ce5f319a (current diff)
children 2f84f888d3db
files ChangeLog libpurple/protocols/msn/msn.c
diffstat 9 files changed, 375 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Dec 10 02:44:03 2008 +0000
+++ b/ChangeLog	Wed Dec 10 06:58:10 2008 +0000
@@ -13,9 +13,6 @@
 	* The Buddy State Notification plugin no longer turns JID's, MSN Passport
 	  ID's, etc. into links (Florian Quèze)
 	* Fix a crash in SIMPLE when a malformed message is received.
-	* On MSN, the Games and Office media can now be set and displayed (in
-	  addition to the previous Music media). The Media status text now shows
-	  the album, if possible. 
 	* purple-remote now has a "getstatusmessage" command to retrieve the text
 	  of the current status message.
 	* Various fixes to the nullprpl (Paul Aurich)
@@ -23,6 +20,9 @@
 	  connected (Paul Aurich)
 	* Fix a crash in purple_accounts_delete that happens when this function is
 	  called before the buddy list is initialized (Florian Quèze)
+	* On MSN, the Games and Office media can now be set and displayed (in
+	  addition to the previous Music media). The Media status text now shows
+	  the album, if possible. 
 
 	Gadu-Gadu:
 	* Fix some problems with Gadu-Gadu buddy icons (Adam Strzelecki)
--- a/libpurple/protocols/msn/contact.c	Wed Dec 10 02:44:03 2008 +0000
+++ b/libpurple/protocols/msn/contact.c	Wed Dec 10 06:58:10 2008 +0000
@@ -870,7 +870,7 @@
 	gpointer data)
 {
 	MsnCallbackState *state = data;
-	xmlnode *faultcode;
+	xmlnode *fault;
 	char *faultcode_str;
 
 	if (resp == NULL) {
@@ -880,9 +880,9 @@
 		return;
 	}
 
-	faultcode = xmlnode_get_child(resp->xml, "Body/Fault/faultcode");
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
 
-	if (faultcode == NULL) {
+	if (fault == NULL) {
 		/* No errors */
 		if (state->cb)
 			((MsnSoapCallback)state->cb)(req, resp, data);
@@ -890,7 +890,7 @@
 		return;
 	}
 
-	faultcode_str = xmlnode_get_data(faultcode);
+	faultcode_str = xmlnode_get_data(xmlnode_get_child(fault, "faultcode"));
 
 	if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) {
 		purple_debug_info("msn",
@@ -903,12 +903,15 @@
 	}
 	else
 	{
-		/* We don't know how to respond to this faultcode, so just log it */
-		/* XXX: Probably should notify the user or undo the change or something? */
-		char *str = xmlnode_to_str(xmlnode_get_child(resp->xml, "Body/Fault"), NULL);
-		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
-		                   msn_contact_operation_str(state->action), str);
-		g_free(str);
+		if (state->cb) {
+			((MsnSoapCallback)state->cb)(req, resp, data);
+		} else {
+			/* We don't know how to respond to this faultcode, so log it */
+			char *str = xmlnode_to_str(fault, NULL);
+			purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+			                   msn_contact_operation_str(state->action), str);
+			g_free(str);
+		}
 		msn_callback_state_free(state);
 	}
 
@@ -943,21 +946,44 @@
 	MsnUser *user;
 	xmlnode *guid;
 
+	xmlnode *fault;
+
 	g_return_if_fail(session != NULL);
+	userlist = session->userlist;
+
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *errorcode = xmlnode_get_data(xmlnode_get_child(fault, "detail/errorcode"));
+		if (errorcode && !strcmp(errorcode, "EmailDomainIsFederated")) {
+			/* Do something special! */
+			purple_debug_error("msn", "Contact is from a federated domain, but don't know what to do yet!\n");
 
-	userlist = session->userlist;
+		} else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) {
+			PurpleBuddy *buddy = purple_find_buddy(session->account, state->who);
+			char *str = g_strdup_printf(_("Unable to add \"%s\"."), state->who);
+			purple_notify_error(state->session, _("Buddy Add error"), str,
+			                    _("The username specified does not exist."));
+			g_free(str);
+			msn_userlist_rem_buddy(userlist, state->who);
+			if (buddy != NULL)
+				purple_blist_remove_buddy(buddy);
+
+		} else {
+			/* We don't know how to respond to this faultcode, so log it */
+			char *fault_str = xmlnode_to_str(fault, NULL);
+			if (fault_str != NULL) {
+				purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+				                   msn_contact_operation_str(state->action), fault_str);
+				g_free(fault_str);
+			}
+		}
+		return;
+	}
 
 	purple_debug_info("msn", "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(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(session, 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);
 
 	user = msn_userlist_find_add_user(userlist, state->who, state->who);
 	msn_user_add_group_id(user, state->guid);
@@ -976,6 +1002,7 @@
 void
 msn_add_contact(MsnSession *session, MsnCallbackState *state, const char *passport)
 {
+	MsnUser *user;
 	gchar *body = NULL;
 	gchar *contact_xml = NULL;
 
@@ -993,7 +1020,21 @@
 
 	purple_debug_info("msn", "Adding contact %s to contact list\n", passport);
 
-	contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
+	user = msn_userlist_find_user(session->userlist, passport);
+	if (user == NULL) {
+		purple_debug_warning("msn", "Unable to retrieve user %s from the userlist!\n", passport);
+		return; /* guess this never happened! */
+	}
+
+	if (user->networkid != MSN_NETWORK_PASSPORT) {
+		contact_xml = g_strdup_printf(MSN_CONTACT_EMAIL_XML,
+		                              user->networkid == MSN_NETWORK_YAHOO ?
+		                                  "Messenger2" :
+		                                  "Messenger3",
+		                              passport, 0);
+	} else {
+		contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
+	}
 	body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE, contact_xml);
 
 	state->body = xmlnode_from_str(body, -1);
@@ -1011,11 +1052,41 @@
 	gpointer data)
 {
 	MsnCallbackState *state = data;
+	MsnSession *session = state->session;
 	MsnUserList *userlist;
+	xmlnode *fault;
+
+	g_return_if_fail(session != NULL);
+	userlist = session->userlist;
+
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *errorcode = xmlnode_get_data(xmlnode_get_child(fault, "detail/errorcode"));
+		if (errorcode && !strcmp(errorcode, "EmailDomainIsFederated")) {
+			/* Do something special! */
+			purple_debug_error("msn", "Contact is from a federated domain, but don't know what to do yet!\n");
 
-	g_return_if_fail(data != NULL);
+		} else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) {
+			PurpleBuddy *buddy = purple_find_buddy(session->account, state->who);
+			char *str = g_strdup_printf(_("Unable to add \"%s\"."), state->who);
+			purple_notify_error(session, _("Buddy Add error"), str,
+			                    _("The username specified does not exist."));
+			g_free(str);
+			msn_userlist_rem_buddy(userlist, state->who);
+			if (buddy != NULL)
+				purple_blist_remove_buddy(buddy);
 
-	userlist = state->session->userlist;
+		} else {
+			/* We don't know how to respond to this faultcode, so log it */
+			char *fault_str = xmlnode_to_str(fault, NULL);
+			if (fault_str != NULL) {
+				purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+				                   msn_contact_operation_str(state->action), fault_str);
+				g_free(fault_str);
+			}
+		}
+		return;
+	}
 
 	if (msn_userlist_add_buddy_to_group(userlist, state->who,
 			state->new_group_name)) {
@@ -1036,11 +1107,8 @@
 			g_free(uid);
 		}
 
-		if ( !msn_user_is_yahoo(state->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(state->session, 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);
 
 		if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
 			msn_del_contact_from_list(state->session, NULL, state->who, MSN_LIST_PL);
@@ -1095,8 +1163,14 @@
 		return; /* guess this never happened! */
 	}
 
-	if (user != NULL && user->uid != NULL) {
+	if (user->uid != NULL) {
 		contact_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
+	} else if (user->networkid != MSN_NETWORK_PASSPORT) {
+		contact_xml = g_strdup_printf(MSN_CONTACT_EMAIL_XML,
+		                              user->networkid == MSN_NETWORK_YAHOO ?
+		                                  "Messenger2" :
+		                                  "Messenger3",
+		                              passport, 0);
 	} else {
 		contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
 	}
@@ -1120,6 +1194,17 @@
 	MsnCallbackState *state = data;
 	MsnUserList *userlist = state->session->userlist;
 	MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid);
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
 
 	purple_debug_info("msn", "Delete contact successful\n");
 
@@ -1140,8 +1225,8 @@
 		contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
 		purple_debug_info("msn", "Deleting contact with contactId: %s\n", user->uid);
 	} else {
-		contact_id_xml = g_strdup_printf(MSN_CONTACT_XML, user->passport);
-		purple_debug_info("msn", "Deleting contact with passport: %s\n", user->passport);
+		purple_debug_info("msn", "Unable to delete contact %s without a ContactId\n", user->passport);
+		return;
 	}
 
 	state = msn_callback_state_new(session);
@@ -1165,6 +1250,17 @@
 	gpointer data)
 {
 	MsnCallbackState *state = data;
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
 
 	if (msn_userlist_rem_buddy_from_group(state->session->userlist,
 			state->who, state->old_group_name)) {
@@ -1235,6 +1331,19 @@
 msn_update_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
 	gpointer data)
 {
+	MsnCallbackState *state = (MsnCallbackState *)data;
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
+
 	purple_debug_info("msn", "Contact updated successfully\n");
 }
 
@@ -1312,6 +1421,17 @@
 {
 	MsnCallbackState *state = data;
 	MsnSession *session = state->session;
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
 
 	purple_debug_info("msn", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
 
@@ -1339,10 +1459,13 @@
 			  const gchar *passport, const MsnListId list)
 {
 	gchar *body = NULL, *member = NULL;
+	const char *type = "PassportMember";
+	gchar *federate = NULL;
 	MsnSoapPartnerScenario partner_scenario;
 	MsnUser *user;
 
 	g_return_if_fail(session != NULL);
+	g_return_if_fail(session->userlist != NULL);
 	g_return_if_fail(passport != NULL);
 	g_return_if_fail(list < 5);
 
@@ -1354,21 +1477,27 @@
 	msn_callback_state_set_list_id(state, list);
 	msn_callback_state_set_who(state, passport);
 
+	user = msn_userlist_find_user(session->userlist, passport);
+	if (user && user->networkid != MSN_NETWORK_PASSPORT) {
+		type = "EmailMember";
+		federate = g_strdup_printf(MSN_MEMBER_FEDERATED_ANNOTATION_XML,
+		                           user->networkid);
+	}
+	
 	if (list == MSN_LIST_PL) {
-		g_return_if_fail(session->userlist != NULL);
-
-		user = msn_userlist_find_user(session->userlist, passport);
-
 		partner_scenario = MSN_PS_CONTACT_API;
-		member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, user->membership_id[MSN_LIST_PL]);
+		member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
+		                         type, user->membership_id[MSN_LIST_PL],
+		                         federate ? federate : "");
 	} else {
 		/* list == MSN_LIST_AL || list == MSN_LIST_BL */
 		partner_scenario = MSN_PS_BLOCK_UNBLOCK;
-		
-		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
+		member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
+		                         type, passport,
+		                         federate ? federate : "");
 	}
 
-	body = g_strdup_printf(MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE,
+	body = g_strdup_printf(MSN_CONTACT_DELETE_FROM_LIST_TEMPLATE,
 		MsnSoapPartnerScenarioText[partner_scenario],
 		MsnMemberRole[list], member);
 
@@ -1378,6 +1507,7 @@
 	state->cb = msn_del_contact_from_list_read_cb;
 	msn_contact_request(state);
 
+	g_free(federate);
 	g_free(member);
 	g_free(body);
 }
@@ -1387,8 +1517,18 @@
 	gpointer data)
 {
 	MsnCallbackState *state = data;
+	xmlnode *fault;
 
-	g_return_if_fail(state != NULL);
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
+
 	g_return_if_fail(state->session != NULL);
 
 	purple_debug_info("msn", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
@@ -1415,7 +1555,10 @@
 			const gchar *passport, const MsnListId list)
 {
 	gchar *body = NULL, *member = NULL;
+	const char *type = "PassportMember";
+	gchar *federate = NULL;
 	MsnSoapPartnerScenario partner_scenario;
+	MsnUser *user;
 
 	g_return_if_fail(session != NULL);
 	g_return_if_fail(passport != NULL);
@@ -1429,9 +1572,16 @@
 	msn_callback_state_set_list_id(state, list);
 	msn_callback_state_set_who(state, passport);
 
-	partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK;
+	user = msn_userlist_find_user(session->userlist, passport);
+	if (user && user->networkid != MSN_NETWORK_PASSPORT) {
+		type = "EmailMember";
+		federate = g_strdup_printf(MSN_MEMBER_FEDERATED_ANNOTATION_XML,
+		                           user->networkid);
+	}
 
-	member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, state->who);
+	partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK;
+	member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
+	                         type, state->who, federate ? federate : "");
 
 	body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE,
 		MsnSoapPartnerScenarioText[partner_scenario],
@@ -1443,6 +1593,7 @@
 	state->cb = msn_add_contact_to_list_read_cb;
 	msn_contact_request(state);
 
+	g_free(federate);
 	g_free(member);
 	g_free(body);
 }
@@ -1482,6 +1633,17 @@
 	MsnCallbackState *state = data;
 	MsnSession *session;
 	MsnUserList *userlist;
+	xmlnode *fault;
+
+	/* We don't know how to respond to this faultcode, so log it */
+	fault = xmlnode_get_child(resp->xml, "Body/Fault");
+	if (fault != NULL) {
+		char *fault_str = xmlnode_to_str(fault, NULL);
+		purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n",
+		                   msn_contact_operation_str(state->action), fault_str);
+		g_free(fault_str);
+		return;
+	}
 
 	purple_debug_info("msn", "Group request successful.\n");
 
@@ -1661,3 +1823,4 @@
 	g_free(escaped_group_name);
 	g_free(body);
 }
+
--- a/libpurple/protocols/msn/contact.h	Wed Dec 10 02:44:03 2008 +0000
+++ b/libpurple/protocols/msn/contact.h	Wed Dec 10 06:58:10 2008 +0000
@@ -211,6 +211,27 @@
 		"</contactInfo>"\
 	"</Contact>"
 
+#define MSN_CONTACT_ID_XML \
+	"<Contact>"\
+		"<contactId>%s</contactId>"\
+	"</Contact>"
+
+#define MSN_CONTACT_EMAIL_XML \
+	"<Contact>"\
+		"<contactInfo>"\
+			"<emails>"\
+				"<ContactEmail>"\
+					"<contactEmailType>%s</contactEmailType>"\
+					"<email>%s</email>"\
+					"<isMessengerEnabled>true</isMessengerEnabled>"\
+					"<Capability>%d</Capability>"\
+					"<MessengerEnabledExternally>false</MessengerEnabledExternally>"\
+					"<propertiesChanged/>"\
+				"</ContactEmail>"\
+			"</emails>"\
+		"</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\""\
@@ -275,7 +296,6 @@
 
 /* Delete a contact from the Contact List */
 #define MSN_CONTACT_DEL_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/ABContactDelete"
-#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/\""\
@@ -376,22 +396,32 @@
 #define MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION	"http://www.msn.com/webservices/AddressBook/DeleteMember"
 
 #define MSN_MEMBER_PASSPORT_XML	\
-	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"%s\">"\
 		"<Type>Passport</Type>"\
 		"<State>Accepted</State>"\
 		"<PassportName>%s</PassportName>"\
+		"%s"\
 	"</Member>"
 
 #define MSN_MEMBER_MEMBERSHIPID_XML	\
-	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+	"<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"%s\">"\
 		"<Type>Passport</Type>"\
 		"<MembershipId>%u</MembershipId>"\
 		"<State>Accepted</State>"\
+		"%s"\
 	"</Member>"
 
+#define MSN_MEMBER_FEDERATED_ANNOTATION_XML \
+	"<Annotations>"\
+		"<Annotation>"\
+			"<Name>MSN.IM.BuddyType</Name>"\
+			"<Value>%02d:</Value>"\
+		"</Annotation>"\
+	"</Annotations>"
+
 /* first delete contact from allow list */
 
-#define MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+#define MSN_CONTACT_DELETE_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
 "<soap:Envelope"\
 	" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\
 	" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\
--- a/libpurple/protocols/msn/msn.c	Wed Dec 10 02:44:03 2008 +0000
+++ b/libpurple/protocols/msn/msn.c	Wed Dec 10 06:58:10 2008 +0000
@@ -1443,21 +1443,18 @@
 		purple_debug_info("msn", "msn_add_buddy: %s\n", who);
 #endif
 
-#if 0
-	/* Which is the max? */
-	if (session->fl_users_count >= 150)
-	{
-		purple_debug_info("msn", "Too many buddies\n");
-		/* Buddy list full */
-		/* TODO: purple should be notified of this */
-		return;
-	}
-#endif
-
 	/* 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, group ? group->name : NULL);
+	if (msn_userlist_find_user(userlist, who) != NULL) {
+		/* We already know this buddy. This function takes care of users
+		   already in the list and stuff... */
+		msn_userlist_add_buddy(userlist, who, group ? group->name : NULL);
+	} else {
+		/* We need to check the network for this buddy first */
+		msn_userlist_save_pending_buddy(userlist, who, group ? group->name : NULL);
+		msn_notification_send_fqy(session, who);
+	}
 }
 
 static void
--- a/libpurple/protocols/msn/notification.c	Wed Dec 10 02:44:03 2008 +0000
+++ b/libpurple/protocols/msn/notification.c	Wed Dec 10 06:58:10 2008 +0000
@@ -849,10 +849,35 @@
 fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
 			 size_t len)
 {
-	purple_debug_info("msn", "FQY payload:\n%s\n", payload);
-	g_return_if_fail(cmdproc->session != NULL);
-/*	msn_notification_post_adl(cmdproc, payload, len); */
-/*	msn_get_address_book(cmdproc->session, MSN_AB_SAVE_CONTACT, NULL, NULL); */
+	MsnUserList *userlist;
+	xmlnode *ml, *d, *c;
+	const char *domain;
+	const char *local;
+	const char *type;
+	char *passport;
+	MsnNetwork network = MSN_NETWORK_PASSPORT;
+
+	userlist = cmdproc->session->userlist;
+
+	/* FQY response:
+	    <ml><d n="domain.com"><c n="local-node" t="network" /></d></ml> */
+	ml = xmlnode_from_str(payload, len);
+	d = xmlnode_get_child(ml, "d");
+	c = xmlnode_get_child(d, "c");
+	domain = xmlnode_get_attrib(d, "n");
+	local = xmlnode_get_attrib(c, "n");
+	type = xmlnode_get_attrib(c, "t");
+
+	passport = g_strdup_printf("%s@%s", local, domain);
+
+	if (type != NULL)
+		network = (MsnNetwork)strtoul(type, NULL, 10);
+	purple_debug_info("msn", "FQY response says %s is from network %d\n",
+	                  passport, network);
+	msn_userlist_add_pending_buddy(userlist, passport, network);
+
+	g_free(passport);
+	xmlnode_free(ml);
 }
 
 static void
--- a/libpurple/protocols/msn/user.c	Wed Dec 10 02:44:03 2008 +0000
+++ b/libpurple/protocols/msn/user.c	Wed Dec 10 06:58:10 2008 +0000
@@ -340,6 +340,20 @@
 }
 
 void
+msn_user_set_pending_group(MsnUser *user, const char *group)
+{
+	user->pending_group = g_strdup(group);
+}
+
+char *
+msn_user_remove_pending_group(MsnUser *user)
+{
+	char *group = user->pending_group;
+	user->pending_group = NULL;
+	return group;
+}
+
+void
 msn_user_set_home_phone(MsnUser *user, const char *number)
 {
 	g_return_if_fail(user != NULL);
--- a/libpurple/protocols/msn/user.h	Wed Dec 10 02:44:03 2008 +0000
+++ b/libpurple/protocols/msn/user.h	Wed Dec 10 06:58:10 2008 +0000
@@ -91,6 +91,7 @@
 	gboolean mobile;        /**< Signed up with MSN Mobile.     */
 
 	GList *group_ids;       /**< The group IDs.                 */
+	char *pending_group;    /**< A pending group to add.        */
 
 	MsnObject *msnobj;      /**< The user's MSN Object.         */
 
@@ -213,6 +214,23 @@
 void msn_user_remove_group_id(MsnUser *user, const char * id);
 
 /**
+ * Sets the pending group for a user.
+ *
+ * @param user  The user.
+ * @param group The group name.
+ */
+void msn_user_set_pending_group(MsnUser *user, const char *group);
+
+/**
+ * Removes the pending group from a user.
+ *
+ * @param user The user.
+ *
+ * @return Returns the pending group name.
+ */
+char *msn_user_remove_pending_group(MsnUser *user);
+
+/**
  * Sets the home phone number for a user.
  *
  * @param user   The user.
--- a/libpurple/protocols/msn/userlist.c	Wed Dec 10 02:44:03 2008 +0000
+++ b/libpurple/protocols/msn/userlist.c	Wed Dec 10 06:58:10 2008 +0000
@@ -184,10 +184,6 @@
 		{
 			msn_user_add_group_id(user, group_id);
 		}
-		else
-		{
-			/* session->sync->fl_users_count++; */
-		}
 	}
 	else if (list_id == MSN_LIST_AL)
 	{
@@ -253,10 +249,6 @@
 			msn_user_remove_group_id(user, group_id);
 			return;
 		}
-		else
-		{
-			/* session->sync->fl_users_count--; */
-		}
 	}
 	else if (list_id == MSN_LIST_AL)
 	{
@@ -756,6 +748,62 @@
 	msn_add_contact_to_group(userlist->session, state, who, group_id);
 }
 
+/*
+ * Save a buddy address/group until we get back response from FQY
+ */
+void
+msn_userlist_save_pending_buddy(MsnUserList *userlist,
+                               const char *who,
+                               const char *group_name)
+{
+	MsnUser *user;
+
+	g_return_if_fail(userlist != NULL);
+
+	user = msn_user_new(userlist, who, NULL);
+	msn_user_set_pending_group(user, group_name);
+	msn_user_set_network(user, MSN_NETWORK_UNKNOWN);
+	userlist->pending = g_list_prepend(userlist->pending, user);
+}
+
+/*
+ * Actually adds a buddy once we have the response from FQY
+ */
+void
+msn_userlist_add_pending_buddy(MsnUserList *userlist,
+                               const char *who,
+                               /*MsnNetwork*/ int network)
+{
+	MsnUser *user = NULL;
+	GList *l;
+	char *group;
+
+	for (l = userlist->pending; l != NULL; l = l->next)
+	{
+		user = (MsnUser *)l->data;
+
+		if (!g_strcasecmp(who, user->passport)) {
+			userlist->pending = g_list_delete_link(userlist->pending, l);
+			break;
+		}
+	}
+
+	if (user == NULL) {
+		purple_debug_error("msn", "Attempting to add a pending user that does not exist.\n");
+		return;
+	}
+
+	/* Bit of a hack, but by adding to userlist now, the rest of the code
+	 * will know what network to use.
+	 */
+	msn_user_set_network(user, network);
+	msn_userlist_add_user(userlist, user);
+
+	group = msn_user_remove_pending_group(user);
+	msn_userlist_add_buddy(userlist, who, group);
+	g_free(group);
+}
+
 void
 msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
 							MsnListId list_id)
--- a/libpurple/protocols/msn/userlist.h	Wed Dec 10 02:44:03 2008 +0000
+++ b/libpurple/protocols/msn/userlist.h	Wed Dec 10 06:58:10 2008 +0000
@@ -47,13 +47,12 @@
 
 	GList *users; /* Contains MsnUsers */
 	GList *groups; /* Contains MsnGroups */
+	GList *pending; /* MsnUsers pending addition (waiting for FQY response) */
 
 	GQueue *buddy_icon_requests;
 	int buddy_icon_window;
 	guint buddy_icon_request_timer;
 
-	int fl_users_count;
-
 };
 
 gboolean msn_userlist_user_is_in_group(MsnUser *user, const char * group_id);
@@ -93,6 +92,12 @@
 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_save_pending_buddy(MsnUserList *userlist,
+                                     const char *who,
+                                     const char *group_name);
+void msn_userlist_add_pending_buddy(MsnUserList *userlist,
+                                    const char *who,
+                                    /*MsnNetwork*/ int network);
 void msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
 						    const char *old_group_name,
 						    const char *new_group_name);