Mercurial > pidgin
view libpurple/protocols/msn/contact.c @ 24121:5395b18f9f08
Revert my revision 849d4f7265598a9f0340411c4c0c0401d488ec3b, which
removed the select() code in child DNS processes. Stu pointed out
that this code is what allowed our child DNS processes to hang
around for 40 seconds waiting for additional requests, then die a
natural death.
But that wasn't happening even WITH the select code because the parent
was killing the DNS children when it was done with them. So I
made another change to set the resolver to NULL so that it isn't
killed by purple_dnsquery_destroy().
I'm assuming that we still want our DNS lookup children to hang around
for a little while after they're done. I reduced the timeout from 40
seconds to 20 seconds.
An arguably better way to do this is to go back to having the child
block on read() instead of calling select(), then have the parent
set a timer that kills the child after a certain about of time. But
I don't see an advantage to doing it either way, and this is simpler.
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Tue, 16 Sep 2008 17:56:01 +0000 |
parents | 35f7b7c62995 |
children | f54e7398f733 16734635febf |
line wrap: on
line source
/** * @file contact.c * get MSN contacts via SOAP request * created by MaYuan<mayuan2006@gmail.com> * * purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "msn.h" #include "contact.h" #include "xmlnode.h" #include "group.h" #include "soap.h" #include "nexus.h" const char *MsnSoapPartnerScenarioText[] = { "Initial", "ContactSave", "MessengerPendingList", "ContactMsgrAPI", "BlockUnblock" }; const char *MsnMemberRole[] = { "Forward", "Allow", "Block", "Reverse", "Pending" }; typedef struct { MsnSession *session; MsnSoapPartnerScenario which; } GetContactListCbData; MsnCallbackState * msn_callback_state_new(MsnSession *session) { MsnCallbackState *state = g_new0(MsnCallbackState, 1); state->session = session; return state; } MsnCallbackState * msn_callback_state_dup(MsnCallbackState *state) { MsnCallbackState *new_state = g_new0(MsnCallbackState, 1); new_state->session = state->session; new_state->who = g_strdup(state->who); new_state->uid = g_strdup(state->uid); new_state->old_group_name = g_strdup(state->old_group_name); new_state->new_group_name = g_strdup(state->new_group_name); new_state->guid = g_strdup(state->guid); /* The rest should be made new */ return new_state; } void msn_callback_state_free(MsnCallbackState *state) { if (state == NULL) return; g_free(state->who); g_free(state->uid); g_free(state->old_group_name); g_free(state->new_group_name); g_free(state->guid); xmlnode_free(state->body); g_free(state); } void msn_callback_state_set_who(MsnCallbackState *state, const gchar *who) { g_return_if_fail(state != NULL); g_free(state->who); state->who = g_strdup(who); } void msn_callback_state_set_uid(MsnCallbackState *state, const gchar *uid) { g_return_if_fail(state != NULL); g_free(state->uid); state->uid = g_strdup(uid); } void msn_callback_state_set_old_group_name(MsnCallbackState *state, const gchar *old_group_name) { g_return_if_fail(state != NULL); g_free(state->old_group_name); state->old_group_name = g_strdup(old_group_name); } void msn_callback_state_set_new_group_name(MsnCallbackState *state, const gchar *new_group_name) { g_return_if_fail(state != NULL); g_free(state->new_group_name); state->new_group_name = g_strdup(new_group_name); } void msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid) { g_return_if_fail(state != NULL); g_free(state->guid); state->guid = g_strdup(guid); } 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; } /*get MSN member role utility*/ static MsnListId msn_get_memberrole(const char *role) { g_return_val_if_fail(role != NULL, 0); if (!strcmp(role,"Allow")) { return MSN_LIST_AL; } else if (!strcmp(role,"Block")) { return MSN_LIST_BL; } else if (!strcmp(role,"Reverse")) { return MSN_LIST_RL; } else if (!strcmp(role,"Pending")) { return MSN_LIST_PL; } return 0; } /* get Network */ /* QuLogic: These names don't really refer to the MsnNetwork, * but I haven't yet written the code to properly use them. */ static MsnNetwork msn_get_network(char *type) { g_return_val_if_fail(type != NULL, 0); if (!strcmp(type,"Regular")) { return MSN_NETWORK_PASSPORT; } if (!strcmp(type,"Live")) { return MSN_NETWORK_PASSPORT; } if (!strcmp(type,"LivePending")) { return MSN_NETWORK_PASSPORT; } return MSN_NETWORK_UNKNOWN; } /* Create the AddressBook in the server, if we don't have one */ static void msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { if (resp && xmlnode_get_child(resp->xml, "Body/Fault") == NULL) { purple_debug_info("msn", "Address Book successfully created!\n"); msn_get_address_book((MsnSession *)data, MSN_PS_INITIAL, NULL, NULL); } else { purple_debug_info("msn", "Address Book creation failed!\n"); } } static void msn_create_address_book(MsnSession *session) { gchar *body; gchar *token_str; g_return_if_fail(session != NULL); g_return_if_fail(session->user != NULL); g_return_if_fail(session->user->passport != NULL); purple_debug_info("msn", "Creating an Address Book.\n"); token_str = g_markup_escape_text( msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1); body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, token_str, session->user->passport); g_free(token_str); msn_soap_message_send(session, msn_soap_message_new(MSN_ADD_ADDRESSBOOK_SOAP_ACTION, xmlnode_from_str(body, -1)), MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE, msn_create_address_cb, session); g_free(body); } static void msn_parse_each_member(MsnSession *session, xmlnode *member, const char *node, MsnListId list) { char *passport = xmlnode_get_data(xmlnode_get_child(member, node)); char *type = xmlnode_get_data(xmlnode_get_child(member, "Type")); char *member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId")); MsnUser *user = msn_userlist_find_add_user(session->userlist, passport, NULL); purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s\n", node, passport, type, member_id == NULL ? "(null)" : member_id); if (member_id) { user->membership_id[list] = atoi(member_id); } msn_got_lst_user(session, user, 1 << list, NULL); g_free(passport); g_free(type); g_free(member_id); } static void msn_parse_each_service(MsnSession *session, xmlnode *service) { xmlnode *type; if ((type = xmlnode_get_child(service, "Info/Handle/Type"))) { char *type_str = xmlnode_get_data(type); if (g_str_equal(type_str, "Profile")) { /* Process Windows Live 'Messenger Roaming Identity' */ } else if (g_str_equal(type_str, "Messenger")) { xmlnode *lastchange = xmlnode_get_child(service, "LastChange"); char *lastchange_str = xmlnode_get_data(lastchange); xmlnode *membership; purple_debug_info("msn", "CL last change: %s\n", lastchange_str); purple_account_set_string(session->account, "CLLastChange", lastchange_str); for (membership = xmlnode_get_child(service, "Memberships/Membership"); membership; membership = xmlnode_get_next_twin(membership)) { xmlnode *role = xmlnode_get_child(membership, "MemberRole"); char *role_str = xmlnode_get_data(role); MsnListId list = msn_get_memberrole(role_str); xmlnode *member; purple_debug_info("msn", "CL MemberRole role: %s, list: %d\n", role_str, list); for (member = xmlnode_get_child(membership, "Members/Member"); member; member = xmlnode_get_next_twin(member)) { const char *member_type = xmlnode_get_attrib(member, "type"); if (g_str_equal(member_type, "PassportMember")) { msn_parse_each_member(session, member, "PassportName", list); } else if (g_str_equal(member_type, "PhoneMember")) { } else if (g_str_equal(member_type, "EmailMember")) { msn_parse_each_member(session, member, "Email", list); } } g_free(role_str); } g_free(lastchange_str); } g_free(type_str); } } /*parse contact list*/ static void msn_parse_contact_list(MsnSession *session, xmlnode *node) { xmlnode *fault, *faultnode; /* we may get a response if our cache data is too old: * * <faultstring>Need to do full sync. Can't sync deltas Client * has too old a copy for us to do a delta sync</faultstring> * * this is not handled yet */ if ((fault = xmlnode_get_child(node, "Body/Fault"))) { if ((faultnode = xmlnode_get_child(fault, "faultstring"))) { char *faultstring = xmlnode_get_data(faultnode); purple_debug_info("msn", "Retrieving contact list failed: %s\n", faultstring); g_free(faultstring); } if ((faultnode = xmlnode_get_child(fault, "detail/errorcode"))) { char *errorcode = xmlnode_get_data(faultnode); if (g_str_equal(errorcode, "ABDoesNotExist")) { msn_create_address_book(session); g_free(errorcode); return; } g_free(errorcode); } msn_get_contact_list(session, MSN_PS_INITIAL, NULL); } else { xmlnode *service; for (service = xmlnode_get_child(node, "Body/FindMembershipResponse/" "FindMembershipResult/Services/Service"); service; service = xmlnode_get_next_twin(service)) { msn_parse_each_service(session, service); } } } static void msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { GetContactListCbData *cb_data = data; MsnSession *session = cb_data->session; g_return_if_fail(session != NULL); if (resp != NULL) { const char *abLastChange; const char *dynamicItemLastChange; purple_debug_misc("msn", "Got the contact list!\n"); msn_parse_contact_list(session, resp->xml); abLastChange = purple_account_get_string(session->account, "ablastChange", NULL); dynamicItemLastChange = purple_account_get_string(session->account, "dynamicItemLastChange", NULL); if (cb_data->which == MSN_PS_INITIAL) { #ifdef MSN_PARTIAL_LISTS /* XXX: this should be enabled when we can correctly do partial syncs with the server. Currently we need to retrieve the whole list to detect sync issues */ msn_get_address_book(session, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange); #else msn_get_address_book(session, MSN_PS_INITIAL, NULL, NULL); #endif } } g_free(cb_data); } /*SOAP get contact list*/ void msn_get_contact_list(MsnSession *session, const MsnSoapPartnerScenario partner_scenario, const char *update_time) { gchar *body = NULL; gchar *update_str = NULL; gchar *token_str; GetContactListCbData cb_data = { session, partner_scenario }; const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario]; purple_debug_misc("msn", "Getting Contact List.\n"); if (update_time != NULL) { purple_debug_info("msn", "CL Last update time: %s\n", update_time); update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML, update_time); } token_str = g_markup_escape_text( msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1); body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, token_str, update_str ? update_str : ""); g_free(token_str); msn_soap_message_send(session, msn_soap_message_new(MSN_GET_CONTACT_SOAP_ACTION, xmlnode_from_str(body, -1)), MSN_CONTACT_SERVER, MSN_GET_CONTACT_POST_URL, FALSE, msn_get_contact_list_cb, g_memdup(&cb_data, sizeof(cb_data))); g_free(update_str); g_free(body); } static void msn_parse_addressbook_groups(MsnSession *session, xmlnode *node) { xmlnode *group; purple_debug_info("msn", "msn_parse_addressbook_groups()\n"); for(group = xmlnode_get_child(node, "Group"); group; group = xmlnode_get_next_twin(group)){ xmlnode *groupId, *groupInfo, *groupname; char *group_id = NULL, *group_name = NULL; if ((groupId = xmlnode_get_child(group, "groupId"))) group_id = xmlnode_get_data(groupId); if ((groupInfo = xmlnode_get_child(group, "groupInfo")) && (groupname = xmlnode_get_child(groupInfo, "name"))) group_name = xmlnode_get_data(groupname); msn_group_new(session->userlist, group_id, group_name); if (group_id == NULL){ /* Group of ungroupped buddies */ g_free(group_name); continue; } purple_debug_info("msn", "AB group_id: %s, name: %s\n", group_id, group_name ? group_name : "(null)"); if ((purple_find_group(group_name)) == NULL){ PurpleGroup *g = purple_group_new(group_name); purple_blist_add_group(g, NULL); } g_free(group_id); g_free(group_name); } } static gboolean msn_parse_addressbook_mobile(xmlnode *contactInfo, char **inout_mobile_number) { xmlnode *phones; char *mobile_number = NULL; gboolean mobile = FALSE; *inout_mobile_number = NULL; if ((phones = xmlnode_get_child(contactInfo, "phones"))) { xmlnode *contact_phone; char *phone_type = NULL; for (contact_phone = xmlnode_get_child(phones, "ContactPhone"); contact_phone; contact_phone = xmlnode_get_next_twin(contact_phone)) { xmlnode *contact_phone_type; if (!(contact_phone_type = xmlnode_get_child(contact_phone, "contactPhoneType"))) continue; phone_type = xmlnode_get_data(contact_phone_type); if (phone_type && !strcmp(phone_type, "ContactPhoneMobile")) { xmlnode *number; if ((number = xmlnode_get_child(contact_phone, "number"))) { xmlnode *messenger_enabled; char *is_messenger_enabled = NULL; g_free(mobile_number); mobile_number = xmlnode_get_data(number); if (mobile_number && (messenger_enabled = xmlnode_get_child(contact_phone, "isMessengerEnabled")) && (is_messenger_enabled = xmlnode_get_data(messenger_enabled)) && !strcmp(is_messenger_enabled, "true")) mobile = TRUE; g_free(is_messenger_enabled); } } g_free(phone_type); } } *inout_mobile_number = mobile_number; return mobile; } static void msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node) { xmlnode *contactNode; char *passport = NULL, *Name = NULL, *uid = NULL, *type = NULL, *mobile_number = NULL, *alias = NULL; gboolean mobile = FALSE; PurpleConnection *pc = purple_account_get_connection(session->account); for(contactNode = xmlnode_get_child(node, "Contact"); contactNode; contactNode = xmlnode_get_next_twin(contactNode)) { xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds, *messenger_user; xmlnode *annotation; MsnUser *user; MsnNetwork networkId; if (!(contactId = xmlnode_get_child(contactNode,"contactId")) || !(contactInfo = xmlnode_get_child(contactNode, "contactInfo")) || !(contactType = xmlnode_get_child(contactInfo, "contactType"))) continue; g_free(passport); g_free(Name); g_free(alias); g_free(uid); g_free(type); g_free(mobile_number); passport = Name = uid = type = mobile_number = alias = NULL; mobile = FALSE; uid = xmlnode_get_data(contactId); type = xmlnode_get_data(contactType); /*setup the Display Name*/ if (type && !strcmp(type, "Me")){ char *friendly = NULL; if ((displayName = xmlnode_get_child(contactInfo, "displayName"))) friendly = xmlnode_get_data(displayName); purple_connection_set_display_name(session->account->gc, friendly ? purple_url_decode(friendly) : NULL); g_free(friendly); continue; /* Not adding own account as buddy to buddylist */ } /* ignore non-messenger contacts */ if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) { char *is_messenger_user = xmlnode_get_data(messenger_user); if(is_messenger_user && !strcmp(is_messenger_user, "false")) { g_free(is_messenger_user); continue; } g_free(is_messenger_user); } networkId = msn_get_network(type); passportName = xmlnode_get_child(contactInfo, "passportName"); if (passportName == NULL) { xmlnode *emailsNode, *contactEmailNode, *emailNode; xmlnode *messengerEnabledNode; char *msnEnabled; /*TODO: add it to the none-instant Messenger group and recognize as email Membership*/ /*Yahoo User?*/ emailsNode = xmlnode_get_child(contactInfo, "emails"); if (emailsNode == NULL) { /*TODO: need to support the Mobile type*/ continue; } for (contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail"); contactEmailNode; contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){ if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) { /* XXX: Should this be a continue instead of a break? It seems like it'd cause unpredictable results otherwise. */ break; } msnEnabled = xmlnode_get_data(messengerEnabledNode); if ((emailNode = xmlnode_get_child(contactEmailNode, "email"))) { g_free(passport); passport = xmlnode_get_data(emailNode); } if (msnEnabled && !strcmp(msnEnabled, "true")) { /*Messenger enabled, Get the Passport*/ purple_debug_info("msn", "AB Yahoo User %s\n", passport ? passport : "(null)"); networkId = MSN_NETWORK_YAHOO; g_free(msnEnabled); break; } else { /*TODO maybe we can just ignore it in Purple?*/ purple_debug_info("msn", "AB Other type user\n"); } g_free(msnEnabled); } } else { passport = xmlnode_get_data(passportName); } if (passport == NULL) continue; if ((displayName = xmlnode_get_child(contactInfo, "displayName"))) Name = xmlnode_get_data(displayName); else Name = g_strdup(passport); for (annotation = xmlnode_get_child(contactInfo, "annotations/Annotation"); annotation; annotation = xmlnode_get_next_twin(annotation)) { char *name; name = xmlnode_get_data(xmlnode_get_child(annotation, "Name")); if (!strcmp(name, "AB.NickName")) alias = xmlnode_get_data(xmlnode_get_child(annotation, "Value")); g_free(name); } mobile = msn_parse_addressbook_mobile(contactInfo, &mobile_number); purple_debug_misc("msn", "AB passport:{%s} uid:{%s} display:{%s} alias: {%s} mobile:{%s} mobile number:{%s}\n", passport, uid ? uid : "(null)", Name ? Name : "(null)", alias ? alias : "(null)", mobile ? "true" : "false", mobile_number ? mobile_number : "(null)"); user = msn_userlist_find_add_user(session->userlist, passport, Name); msn_user_set_uid(user, uid); msn_user_set_network(user, networkId); msn_user_set_mobile_phone(user, mobile_number); groupIds = xmlnode_get_child(contactInfo, "groupIds"); if (groupIds) { for (guid = xmlnode_get_child(groupIds, "guid"); guid; guid = xmlnode_get_next_twin(guid)) { char *group_id = xmlnode_get_data(guid); msn_user_add_group_id(user, group_id); purple_debug_misc("msn", "AB guid:%s\n", group_id ? group_id : "(null)"); g_free(group_id); } } else { purple_debug_info("msn", "User not in any groups, adding to default group.\n"); /*not in any group,Then set default group*/ msn_user_add_group_id(user, MSN_INDIVIDUALS_GROUP_ID); } msn_got_lst_user(session, user, MSN_LIST_FL_OP, NULL); if (mobile && user) { user->mobile = TRUE; purple_prpl_got_user_status(session->account, user->passport, "mobile", NULL); purple_prpl_got_user_status(session->account, user->passport, "available", NULL); } if (alias) purple_serv_got_private_alias(pc, passport, alias); } g_free(passport); g_free(Name); g_free(uid); g_free(type); g_free(mobile_number); } static gboolean msn_parse_addressbook(MsnSession *session, xmlnode *node) { xmlnode *result; xmlnode *groups; xmlnode *contacts; xmlnode *abNode; xmlnode *fault; if ((fault = xmlnode_get_child(node, "Body/Fault"))) { xmlnode *faultnode; if ((faultnode = xmlnode_get_child(fault, "faultstring"))) { gchar *faultstring = xmlnode_get_data(faultnode); purple_debug_info("msn", "AB Faultstring: %s\n", faultstring); g_free(faultstring); } if ((faultnode = xmlnode_get_child(fault, "detail/errorcode"))) { gchar *errorcode = xmlnode_get_data(faultnode); purple_debug_info("msn", "AB Error Code: %s\n", errorcode); if (g_str_equal(errorcode, "ABDoesNotExist")) { g_free(errorcode); return TRUE; } g_free(errorcode); } return FALSE; } result = xmlnode_get_child(node, "Body/ABFindAllResponse/ABFindAllResult"); if (result == NULL) { purple_debug_misc("msn", "Received no address book update\n"); return TRUE; } /* I don't see this "groups" tag documented on msnpiki, need to find out if they are really there, and update msnpiki */ /*Process Group List*/ groups = xmlnode_get_child(result, "groups"); if (groups != NULL) { msn_parse_addressbook_groups(session, groups); } /*add a default No group to set up the no group Membership*/ msn_group_new(session->userlist, MSN_INDIVIDUALS_GROUP_ID, MSN_INDIVIDUALS_GROUP_NAME); purple_debug_misc("msn", "AB group_id:%s name:%s\n", MSN_INDIVIDUALS_GROUP_ID, MSN_INDIVIDUALS_GROUP_NAME); if ((purple_find_group(MSN_INDIVIDUALS_GROUP_NAME)) == NULL){ PurpleGroup *g = purple_group_new(MSN_INDIVIDUALS_GROUP_NAME); purple_blist_add_group(g, NULL); } /*add a default No group to set up the no group Membership*/ msn_group_new(session->userlist, MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME); purple_debug_misc("msn", "AB group_id:%s name:%s\n", MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME); if ((purple_find_group(MSN_NON_IM_GROUP_NAME)) == NULL) { PurpleGroup *g = purple_group_new(MSN_NON_IM_GROUP_NAME); purple_blist_add_group(g, NULL); } /*Process contact List*/ purple_debug_info("msn", "Process contact list...\n"); contacts = xmlnode_get_child(result, "contacts"); if (contacts != NULL) { msn_parse_addressbook_contacts(session, contacts); } abNode = xmlnode_get_child(result, "ab"); if (abNode != NULL) { xmlnode *node2; char *tmp = NULL; if ((node2 = xmlnode_get_child(abNode, "lastChange"))) tmp = xmlnode_get_data(node2); purple_debug_info("msn", "AB lastchanged Time:{%s}\n", tmp ? tmp : "(null)"); purple_account_set_string(session->account, "ablastChange", tmp); g_free(tmp); tmp = NULL; if ((node2 = xmlnode_get_child(abNode, "DynamicItemLastChanged"))) tmp = xmlnode_get_data(node2); purple_debug_info("msn", "AB DynamicItemLastChanged :{%s}\n", tmp ? tmp : "(null)"); purple_account_set_string(session->account, "DynamicItemLastChanged", tmp); g_free(tmp); } return TRUE; } static void msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnSession *session = data; if (resp == NULL) return; g_return_if_fail(session != NULL); purple_debug_misc("msn", "Got the Address Book!\n"); if (msn_parse_addressbook(session, resp->xml)) { msn_send_privacy(session->account->gc); msn_notification_dump_contact(session); } else { /* This is making us loop infinitely when we fail to parse the address book, disable for now (we should re-enable when we send timestamps) */ /* msn_get_address_book(session, NULL, NULL); */ msn_session_disconnect(session); purple_connection_error_reason(session->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to retrieve MSN Address Book")); } } /*get the address book*/ void msn_get_address_book(MsnSession *session, MsnSoapPartnerScenario partner_scenario, const char *LastChanged, const char *dynamicItemLastChange) { char *body, *update_str = NULL; gchar *token_str; purple_debug_misc("msn", "Getting Address Book\n"); /*build SOAP and POST it*/ if (dynamicItemLastChange != NULL) update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML, dynamicItemLastChange); else if (LastChanged != NULL) update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML, LastChanged); token_str = g_markup_escape_text( msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), -1); body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], token_str, update_str ? update_str : ""); g_free(token_str); msn_soap_message_send(session, msn_soap_message_new(MSN_GET_ADDRESS_SOAP_ACTION, xmlnode_from_str(body, -1)), MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE, msn_get_address_cb, session); g_free(update_str); g_free(body); } /*************************************************************** * Contact Operations ***************************************************************/ static const char * msn_contact_operation_str(MsnCallbackAction action) { /* Make sure this is large enough when adding more */ static char buf[BUF_LEN]; buf[0] = '\0'; if (action & MSN_ADD_BUDDY) strcat(buf, "Adding Buddy,"); if (action & MSN_MOVE_BUDDY) strcat(buf, "Moving Buddy,"); if (action & MSN_ACCEPTED_BUDDY) strcat(buf, "Accepted Buddy,"); if (action & MSN_DENIED_BUDDY) strcat(buf, "Denied Buddy,"); if (action & MSN_ADD_GROUP) strcat(buf, "Adding Group,"); if (action & MSN_DEL_GROUP) strcat(buf, "Deleting Group,"); if (action & MSN_RENAME_GROUP) strcat(buf, "Renaming Group,"); if (action & MSN_UPDATE_INFO) strcat(buf, "Updating Contact Info,"); return buf; } static gboolean msn_contact_request(MsnCallbackState *state); static void msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnCallbackState *state = data; xmlnode *faultcode; char *faultcode_str; if (resp == NULL) { purple_debug_error("msn", "Operation {%s} failed. No response received from server.\n", msn_contact_operation_str(state->action)); return; } faultcode = xmlnode_get_child(resp->xml, "Body/Fault/faultcode"); if (faultcode == NULL) { /* No errors */ if (state->cb) ((MsnSoapCallback)state->cb)(req, resp, data); msn_callback_state_free(state); return; } faultcode_str = xmlnode_get_data(faultcode); if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) { purple_debug_info("msn", "Contact Operation {%s} failed because of bad token." " Updating token now and retrying operation.\n", msn_contact_operation_str(state->action)); /* Token has expired, so renew it, and try again later */ msn_nexus_update_token(state->session->nexus, MSN_AUTH_CONTACTS, (GSourceFunc)msn_contact_request, data); } 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); msn_callback_state_free(state); } g_free(faultcode_str); } static gboolean msn_contact_request(MsnCallbackState *state) { if (state->token == NULL) state->token = xmlnode_get_child(state->body, "Header/ABAuthHeader/TicketToken"); /* delete old & replace with new token */ xmlnode_free(state->token->child); xmlnode_insert_data(state->token, msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1); msn_soap_message_send(state->session, msn_soap_message_new(state->post_action, xmlnode_copy(state->body)), MSN_CONTACT_SERVER, state->post_url, FALSE, msn_contact_request_cb, state); return FALSE; } static void msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnCallbackState *state = data; MsnSession *session = state->session; MsnUserList *userlist; MsnUser *user; g_return_if_fail(session != NULL); userlist = session->userlist; 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); user = msn_userlist_find_add_user(userlist, state->who, state->who); msn_user_add_group_id(user, state->guid); } /* add a Contact in MSN_INDIVIDUALS_GROUP */ void msn_add_contact(MsnSession *session, MsnCallbackState *state, const char *passport) { gchar *body = NULL; gchar *contact_xml = NULL; #if 0 gchar *escaped_displayname; 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); #endif purple_debug_info("msn", "Adding contact %s to contact list\n", passport); 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); state->post_action = MSN_CONTACT_ADD_SOAP_ACTION; state->post_url = MSN_ADDRESS_BOOK_POST_URL; state->cb = msn_add_contact_read_cb; msn_contact_request(state); g_free(contact_xml); g_free(body); } static void msn_add_contact_to_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnCallbackState *state = data; MsnUserList *userlist; g_return_if_fail(data != NULL); userlist = state->session->userlist; if (msn_userlist_add_buddy_to_group(userlist, state->who, state->new_group_name)) { purple_debug_info("msn", "Contact %s added to group %s successfully!\n", state->who, state->new_group_name); } else { purple_debug_info("msn", "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) { MsnUser *user = msn_userlist_find_user(userlist, state->who); 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); if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) { msn_del_contact_from_list(state->session, NULL, state->who, MSN_LIST_PL); return; } } if (state->action & MSN_MOVE_BUDDY) { msn_del_contact_from_group(state->session, state->who, state->old_group_name); } } void msn_add_contact_to_group(MsnSession *session, MsnCallbackState *state, const char *passport, const char *groupId) { MsnUserList *userlist; MsnUser *user; gchar *body = NULL, *contact_xml; g_return_if_fail(passport != NULL); g_return_if_fail(groupId != NULL); g_return_if_fail(session != NULL); userlist = session->userlist; if (!strcmp(groupId, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(groupId, MSN_NON_IM_GROUP_ID)) { user = msn_userlist_find_add_user(userlist, passport, passport); if (state->action & MSN_ADD_BUDDY) { msn_add_contact(session, state, passport); return; } if (state->action & MSN_MOVE_BUDDY) { msn_user_add_group_id(user, groupId); msn_del_contact_from_group(session, passport, state->old_group_name); } return; } purple_debug_info("msn", "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", "Unable to retrieve user %s from the userlist!\n", passport); msn_callback_state_free(state); return; /* guess this never happened! */ } if (user != NULL && 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); state->body = xmlnode_from_str(body, -1); state->post_action = MSN_ADD_CONTACT_GROUP_SOAP_ACTION; state->post_url = MSN_ADDRESS_BOOK_POST_URL; state->cb = msn_add_contact_to_group_read_cb; msn_contact_request(state); g_free(contact_xml); g_free(body); } static void msn_delete_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnCallbackState *state = data; MsnUserList *userlist = state->session->userlist; MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid); purple_debug_info("msn", "Delete contact successful\n"); if (user != NULL) { msn_userlist_remove_user(userlist, user); } } /*delete a Contact*/ void msn_delete_contact(MsnSession *session, const char *contactId) { gchar *body = NULL; gchar *contact_id_xml = NULL ; MsnCallbackState *state; g_return_if_fail(contactId != NULL); contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, contactId); state = msn_callback_state_new(session); msn_callback_state_set_uid(state, contactId); /* build SOAP request */ purple_debug_info("msn", "Deleting contact with contactId: %s\n", contactId); body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE, contact_id_xml); state->body = xmlnode_from_str(body, -1); state->post_action = MSN_CONTACT_DEL_SOAP_ACTION; state->post_url = MSN_ADDRESS_BOOK_POST_URL; state->cb = msn_delete_contact_read_cb; msn_contact_request(state); g_free(contact_id_xml); g_free(body); } static void msn_del_contact_from_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnCallbackState *state = data; if (msn_userlist_rem_buddy_from_group(state->session->userlist, state->who, state->old_group_name)) { purple_debug_info("msn", "Contact %s deleted successfully from group %s\n", state->who, state->old_group_name); } else { purple_debug_info("msn", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name); } } void msn_del_contact_from_group(MsnSession *session, const char *passport, const char *group_name) { MsnUserList * userlist; MsnUser *user; MsnCallbackState *state; gchar *body, *contact_id_xml; const gchar *groupId; g_return_if_fail(passport != NULL); g_return_if_fail(group_name != NULL); g_return_if_fail(session != NULL); userlist = session->userlist; groupId = msn_userlist_find_group_id(userlist, group_name); if (groupId != NULL) { purple_debug_info("msn", "Deleting user %s from group %s\n", passport, group_name); } else { purple_debug_warning("msn", "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", "Unable to retrieve user from passport %s!\n", passport); return; } if ( !strcmp(groupId, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(groupId, MSN_NON_IM_GROUP_ID)) { msn_user_remove_group_id(user, groupId); return; } state = msn_callback_state_new(session); 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); state->body = xmlnode_from_str(body, -1); state->post_action = MSN_CONTACT_DEL_GROUP_SOAP_ACTION; state->post_url = MSN_ADDRESS_BOOK_POST_URL; state->cb = msn_del_contact_from_group_read_cb; msn_contact_request(state); g_free(contact_id_xml); g_free(body); } static void msn_update_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { purple_debug_info("msn", "Contact updated successfully\n"); } /* Update a contact's info */ void msn_update_contact(MsnSession *session, const char *passport, MsnContactUpdateType type, const char* value) { MsnCallbackState *state; xmlnode *contact; xmlnode *contact_info; xmlnode *changes; purple_debug_info("msn", "Update contact information with new %s: %s\n", type==MSN_UPDATE_DISPLAY ? "display name" : "alias", value); purple_debug_info("msn", "passport=%s\n", passport); g_return_if_fail(passport != NULL); contact_info = xmlnode_new("contactInfo"); changes = xmlnode_new("propertiesChanged"); switch (type) { xmlnode *annotations; xmlnode *display; xmlnode *a, *n, *v; case MSN_UPDATE_DISPLAY: display = xmlnode_new_child(contact_info, "displayName"); xmlnode_insert_data(display, value, -1); xmlnode_insert_data(changes, "DisplayName", -1); break; case MSN_UPDATE_ALIAS: annotations = xmlnode_new_child(contact_info, "annotations"); xmlnode_insert_data(changes, "Annotation ", -1); a = xmlnode_new_child(annotations, "Annotation"); n = xmlnode_new_child(a, "Name"); xmlnode_insert_data(n, "AB.NickName", -1); v = xmlnode_new_child(a, "Value"); xmlnode_insert_data(v, value, -1); break; default: g_return_if_reached(); } state = msn_callback_state_new(session); state->body = xmlnode_from_str(MSN_CONTACT_UPDATE_TEMPLATE, -1); state->action = MSN_UPDATE_INFO; state->post_action = MSN_CONTACT_UPDATE_SOAP_ACTION; state->post_url = MSN_ADDRESS_BOOK_POST_URL; state->cb = msn_update_contact_read_cb; contact = xmlnode_get_child(state->body, "Body/ABContactUpdate/contacts/Contact"); xmlnode_insert_child(contact, contact_info); xmlnode_insert_child(contact, changes); if (!strcmp(passport, "Me")) { xmlnode *contactType = xmlnode_new_child(contact_info, "contactType"); xmlnode_insert_data(contactType, "Me", -1); } else { MsnUser *user = msn_userlist_find_user(session->userlist, passport); xmlnode *contactId = xmlnode_new_child(contact, "contactId"); msn_callback_state_set_uid(state, user->uid); xmlnode_insert_data(contactId, state->uid, -1); } msn_contact_request(state); } static void msn_del_contact_from_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnCallbackState *state = data; MsnSession *session = state->session; purple_debug_info("msn", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]); if (state->list_id == MSN_LIST_PL) { MsnUser *user = msn_userlist_find_user(session->userlist, state->who); MsnCallbackState *new_state = msn_callback_state_dup(state); if (user != NULL) msn_user_unset_op(user, MSN_LIST_PL_OP); msn_add_contact_to_list(session, new_state, state->who, MSN_LIST_RL); return; } else if (state->list_id == MSN_LIST_AL) { purple_privacy_permit_remove(session->account, state->who, TRUE); msn_add_contact_to_list(session, NULL, state->who, MSN_LIST_BL); } else if (state->list_id == MSN_LIST_BL) { purple_privacy_deny_remove(session->account, state->who, TRUE); msn_add_contact_to_list(session, NULL, state->who, MSN_LIST_AL); } } void msn_del_contact_from_list(MsnSession *session, MsnCallbackState *state, const gchar *passport, const MsnListId list) { gchar *body = NULL, *member = NULL; MsnSoapPartnerScenario partner_scenario; MsnUser *user; g_return_if_fail(session != NULL); g_return_if_fail(passport != NULL); g_return_if_fail(list < 5); purple_debug_info("msn", "Deleting contact %s from %s list\n", passport, MsnMemberRole[list]); if (state == NULL) { state = msn_callback_state_new(session); } msn_callback_state_set_list_id(state, list); msn_callback_state_set_who(state, passport); 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]); } else { /* list == MSN_LIST_AL || list == MSN_LIST_BL */ partner_scenario = MSN_PS_BLOCK_UNBLOCK; member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport); } body = g_strdup_printf(MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], MsnMemberRole[list], member); state->body = xmlnode_from_str(body, -1); state->post_action = MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION; state->post_url = MSN_SHARE_POST_URL; state->cb = msn_del_contact_from_list_read_cb; msn_contact_request(state); g_free(member); g_free(body); } static void msn_add_contact_to_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnCallbackState *state = data; g_return_if_fail(state != NULL); 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]); if (state->list_id == MSN_LIST_RL) { MsnUser *user = msn_userlist_find_user(state->session->userlist, state->who); if (user != NULL) { msn_user_set_op(user, MSN_LIST_RL_OP); } if (state->action & MSN_DENIED_BUDDY) { msn_add_contact_to_list(state->session, NULL, state->who, MSN_LIST_BL); } else if (state->list_id == MSN_LIST_AL) { purple_privacy_permit_add(state->session->account, state->who, TRUE); } else if (state->list_id == MSN_LIST_BL) { purple_privacy_deny_add(state->session->account, state->who, TRUE); } } } void msn_add_contact_to_list(MsnSession *session, MsnCallbackState *state, const gchar *passport, const MsnListId list) { gchar *body = NULL, *member = NULL; MsnSoapPartnerScenario partner_scenario; g_return_if_fail(session != NULL); g_return_if_fail(passport != NULL); g_return_if_fail(list < 5); purple_debug_info("msn", "Adding contact %s to %s list\n", passport, MsnMemberRole[list]); if (state == NULL) { state = msn_callback_state_new(session); } msn_callback_state_set_list_id(state, list); msn_callback_state_set_who(state, passport); partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK; member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, state->who); body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], MsnMemberRole[list], member); state->body = xmlnode_from_str(body, -1); state->post_action = MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION; state->post_url = MSN_SHARE_POST_URL; state->cb = msn_add_contact_to_list_read_cb; msn_contact_request(state); g_free(member); g_free(body); } #if 0 static void msn_gleams_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { purple_debug_info("msn", "Gleams read done\n"); } /*get the gleams info*/ void msn_get_gleams(MsnSession *session) { MsnSoapReq *soap_request; purple_debug_info("msn", "msn get gleams info...\n"); state = msn_callback_state_new(session); state->body = xmlnode_from_str(MSN_GLEAMS_TEMPLATE, -1); state->post_action = MSN_GET_GLEAMS_SOAP_ACTION; state->post_url = MSN_ADDRESS_BOOK_POST_URL; state->cb = msn_gleams_read_cb; msn_contact_request(state); } #endif /*************************************************************** * Group Operations ***************************************************************/ static void msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnCallbackState *state = data; MsnSession *session; MsnUserList *userlist; purple_debug_info("msn", "Group request successful.\n"); g_return_if_fail(state->session != NULL); g_return_if_fail(state->session->userlist != NULL); session = state->session; userlist = session->userlist; if (state->action & MSN_RENAME_GROUP) { msn_userlist_rename_group_id(session->userlist, state->guid, state->new_group_name); } if (state->action & MSN_ADD_GROUP) { /* the response is taken from http://telepathy.freedesktop.org/wiki/Pymsn/MSNP/ContactListActions should copy it to msnpiki some day */ xmlnode *guid_node = xmlnode_get_child(resp->xml, "Body/ABGroupAddResponse/ABGroupAddResult/guid"); if (guid_node) { char *guid = xmlnode_get_data(guid_node); /* create and add the new group to the userlist */ purple_debug_info("msn", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid); msn_group_new(session->userlist, guid, state->new_group_name); if (state->action & MSN_ADD_BUDDY) { msn_userlist_add_buddy(session->userlist, state->who, state->new_group_name); } else if (state->action & MSN_MOVE_BUDDY) { /* This will be freed when the add contact callback fires */ MsnCallbackState *new_state = msn_callback_state_dup(state); msn_add_contact_to_group(session, new_state, state->who, guid); g_free(guid); return; } g_free(guid); } else { purple_debug_info("msn", "Adding group %s failed\n", state->new_group_name); } } if (state->action & MSN_DEL_GROUP) { GList *l; msn_userlist_remove_group_id(session->userlist, state->guid); for (l = userlist->users; l != NULL; l = l->next) { msn_user_remove_group_id( (MsnUser *)l->data, state->guid); } } } /* add group */ void msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_name) { char *body = NULL; char *escaped_group_name = NULL; g_return_if_fail(session != NULL); g_return_if_fail(group_name != NULL); purple_debug_info("msn", "Adding group %s to contact list.\n", group_name); if (state == NULL) { state = msn_callback_state_new(session); } 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); state->body = xmlnode_from_str(body, -1); state->post_action = MSN_GROUP_ADD_SOAP_ACTION; state->post_url = MSN_ADDRESS_BOOK_POST_URL; state->cb = msn_group_read_cb; msn_contact_request(state); g_free(escaped_group_name); g_free(body); } /* delete group */ void msn_del_group(MsnSession *session, const gchar *group_name) { MsnCallbackState *state; char *body = NULL; const gchar *guid; g_return_if_fail(session != NULL); g_return_if_fail(group_name != NULL); purple_debug_info("msn", "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", "Group %s guid not found, returning.\n", group_name); return; } if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) { /* XXX add back PurpleGroup since it isn't really removed in the server? */ return; } state = msn_callback_state_new(session); 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); state->body = xmlnode_from_str(body, -1); state->post_action = MSN_GROUP_DEL_SOAP_ACTION; state->post_url = MSN_ADDRESS_BOOK_POST_URL; state->cb = msn_group_read_cb; msn_contact_request(state); g_free(body); } /* rename group */ void msn_contact_rename_group(MsnSession *session, const char *old_group_name, const char *new_group_name) { gchar *body = NULL; const gchar * guid; MsnCallbackState *state; char *escaped_group_name; 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); purple_debug_info("msn", "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; state = msn_callback_state_new(session); msn_callback_state_set_guid(state, guid); msn_callback_state_set_new_group_name(state, new_group_name); if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) { MsnCallbackState *new_state = msn_callback_state_dup(state); msn_add_group(session, new_state, new_group_name); /* XXX move every buddy there (we probably need to fix concurrent SOAP reqs first) */ } msn_callback_state_set_action(state, MSN_RENAME_GROUP); escaped_group_name = g_markup_escape_text(new_group_name, -1); body = g_strdup_printf(MSN_GROUP_RENAME_TEMPLATE, guid, escaped_group_name); state->body = xmlnode_from_str(body, -1); state->post_action = MSN_GROUP_RENAME_SOAP_ACTION; state->post_url = MSN_ADDRESS_BOOK_POST_URL; state->cb = msn_group_read_cb; msn_contact_request(state); g_free(escaped_group_name); g_free(body); }