pidgin/pidgin

Revert my previous change.

2017-01-08, Mark Doliner
7ccb54f5d342
Revert my previous change.

Apparently it caused `make distcheck` to fail which caused a Bitbucket build pipeline to fail: https://bitbucket.org/pidgin/main/addon/pipelines/home#!/results/%7B9f059724-78b4-4a5e-9780-9a614c7c2742%7D

Not sure how to fix this.

FYI there's a daily cronjob on... rock? in that calls /srv/trac/developer.pidgin.im/mercurial_support/nightly_update.sh which updates https://developer.pidgin.im/l10n/ It's been failing for a few months.
/**
* @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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "internal.h"
#include "debug.h"
#include "contact.h"
#include "xmlnode.h"
#include "group.h"
#include "msnutils.h"
#include "soap.h"
#include "nexus.h"
#include "user.h"
const char *MsnSoapPartnerScenarioText[] =
{
"Initial",
"ContactSave",
"MessengerPendingList",
"ContactMsgrAPI",
"BlockUnblock",
"Timer"
};
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);
if (state->body)
purple_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;
}
/***************************************************************
* General SOAP handling
***************************************************************/
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,");
if (action & MSN_ANNOTATE_USER)
strcat(buf, "Annotating Contact,");
return buf;
}
static gboolean msn_contact_request(MsnCallbackState *state);
static void
msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = data;
PurpleXmlNode *fault;
char *faultcode_str;
PurpleXmlNode *cachekey;
char *changed;
if (resp == NULL) {
purple_debug_error("msn",
"Operation {%s} failed. No response received from server.\n",
msn_contact_operation_str(state->action));
msn_session_set_error(state->session, MSN_ERROR_BAD_BLIST, NULL);
msn_callback_state_free(state);
return;
}
/* Update CacheKey if necessary */
cachekey = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Header/ServiceHeader/CacheKeyChanged");
if (cachekey != NULL) {
changed = purple_xmlnode_get_data(cachekey);
if (changed && !strcmp(changed, "true")) {
cachekey = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Header/ServiceHeader/CacheKey");
g_free(state->session->abch_cachekey);
state->session->abch_cachekey = purple_xmlnode_get_data(cachekey);
purple_debug_info("msn", "Updated CacheKey for %s to '%s'.\n",
purple_account_get_username(state->session->account),
state->session->abch_cachekey);
}
g_free(changed);
}
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault == NULL) {
/* No errors */
if (state->cb)
state->cb(req, resp, data);
msn_callback_state_free(state);
return;
}
faultcode_str = purple_xmlnode_get_data(purple_xmlnode_get_child(fault, "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
{
if (state->cb) {
state->cb(req, resp, data);
} else {
/* We don't know how to respond to this faultcode, so log it */
char *str = purple_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);
}
g_free(faultcode_str);
}
static gboolean
msn_contact_request(MsnCallbackState *state)
{
PurpleXmlNode *cachekey = purple_xmlnode_get_child(state->body,
"Header/ABApplicationHeader/CacheKey");
if (cachekey != NULL)
purple_xmlnode_free(cachekey);
if (state->session->abch_cachekey != NULL) {
cachekey = purple_xmlnode_new_child(purple_xmlnode_get_child(state->body, "Header/ABApplicationHeader"), "CacheKey");
purple_xmlnode_insert_data(cachekey, state->session->abch_cachekey, -1);
}
if (state->token == NULL)
state->token = purple_xmlnode_get_child(state->body,
"Header/ABAuthHeader/TicketToken");
/* delete old & replace with new token */
purple_xmlnode_free(state->token->child);
purple_xmlnode_insert_data(state->token,
msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1);
msn_soap_service_send_message(state->session->soap,
msn_soap_message_new(state->post_action, purple_xmlnode_copy(state->body)),
MSN_CONTACT_SERVER, state->post_url, FALSE,
msn_contact_request_cb, state);
return FALSE;
}
/***************************************************************
* Address Book and Membership List Operations
***************************************************************/
/*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;
}
/* Create the AddressBook in the server, if we don't have one */
static void
msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
{
MsnCallbackState *state = data;
if (resp && purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault") == NULL) {
purple_debug_info("msn", "Address Book successfully created!\n");
msn_get_address_book(state->session, 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;
MsnCallbackState *state;
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");
body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE,
session->user->passport);
state = msn_callback_state_new(session);
state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_ADD_ADDRESSBOOK_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_create_address_cb;
msn_contact_request(state);
g_free(body);
}
static void
msn_parse_each_member(MsnSession *session, PurpleXmlNode *member, const char *node,
MsnListId list)
{
char *passport;
char *type;
char *member_id;
MsnUser *user;
PurpleXmlNode *annotation;
guint nid = MSN_NETWORK_UNKNOWN;
char *invite = NULL;
passport = purple_xmlnode_get_data(purple_xmlnode_get_child(member, node));
if (!msn_email_is_valid(passport)) {
g_free(passport);
return;
}
type = purple_xmlnode_get_data(purple_xmlnode_get_child(member, "Type"));
member_id = purple_xmlnode_get_data(purple_xmlnode_get_child(member, "MembershipId"));
user = msn_userlist_find_add_user(session->userlist, passport, NULL);
for (annotation = purple_xmlnode_get_child(member, "Annotations/Annotation");
annotation;
annotation = purple_xmlnode_get_next_twin(annotation)) {
char *name = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Name"));
char *value = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Value"));
if (name && value) {
if (!strcmp(name, "MSN.IM.BuddyType")) {
nid = strtoul(value, NULL, 10);
}
else if (!strcmp(name, "MSN.IM.InviteMessage")) {
invite = value;
value = NULL;
}
}
g_free(name);
g_free(value);
}
/* For EmailMembers, the network must be found in the annotations, above.
Otherwise, PassportMembers are on the Passport network. */
if (!strcmp(node, "PassportName"))
nid = MSN_NETWORK_PASSPORT;
purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s, NetworkID: %u\n",
node, passport, type, member_id == NULL ? "(null)" : member_id, nid);
msn_user_set_network(user, nid);
msn_user_set_invite_message(user, invite);
if (list == MSN_LIST_PL && member_id) {
user->member_id_on_pending_list = atoi(member_id);
}
msn_got_lst_user(session, user, 1 << list, NULL);
g_free(passport);
g_free(type);
g_free(member_id);
g_free(invite);
}
static void
msn_parse_each_service(MsnSession *session, PurpleXmlNode *service)
{
PurpleXmlNode *type;
if ((type = purple_xmlnode_get_child(service, "Info/Handle/Type"))) {
char *type_str = purple_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")) {
PurpleXmlNode *lastchange = purple_xmlnode_get_child(service, "LastChange");
char *lastchange_str = purple_xmlnode_get_data(lastchange);
PurpleXmlNode *membership;
purple_debug_info("msn", "CL last change: %s\n", lastchange_str);
purple_account_set_string(session->account, "CLLastChange",
lastchange_str);
for (membership = purple_xmlnode_get_child(service,
"Memberships/Membership");
membership; membership = purple_xmlnode_get_next_twin(membership)) {
PurpleXmlNode *role = purple_xmlnode_get_child(membership, "MemberRole");
char *role_str = purple_xmlnode_get_data(role);
MsnListId list = msn_get_memberrole(role_str);
PurpleXmlNode *member;
purple_debug_info("msn", "CL MemberRole role: %s, list: %d\n",
role_str, list);
for (member = purple_xmlnode_get_child(membership, "Members/Member");
member; member = purple_xmlnode_get_next_twin(member)) {
const char *member_type = purple_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 gboolean
msn_parse_contact_list(MsnSession *session, PurpleXmlNode *node)
{
PurpleXmlNode *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 = purple_xmlnode_get_child(node, "Body/Fault"))) {
if ((faultnode = purple_xmlnode_get_child(fault, "detail/errorcode"))) {
char *errorcode = purple_xmlnode_get_data(faultnode);
if (g_str_equal(errorcode, "ABDoesNotExist")) {
msn_create_address_book(session);
g_free(errorcode);
return FALSE;
}
g_free(errorcode);
}
if ((faultnode = purple_xmlnode_get_child(fault, "faultstring"))) {
char *faultstring = purple_xmlnode_get_data(faultnode);
purple_debug_info("msn", "Retrieving contact list failed: %s\n",
faultstring);
msn_session_set_error(session, MSN_ERROR_BAD_BLIST, faultstring);
g_free(faultstring);
} else {
msn_session_set_error(session, MSN_ERROR_BAD_BLIST, NULL);
}
return FALSE;
} else {
PurpleXmlNode *service;
for (service = purple_xmlnode_get_child(node, "Body/FindMembershipResponse/"
"FindMembershipResult/Services/Service");
service; service = purple_xmlnode_get_next_twin(service)) {
msn_parse_each_service(session, service);
}
return TRUE;
}
}
static void
msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = data;
MsnSession *session = state->session;
g_return_if_fail(session != NULL);
if (resp != NULL) {
#ifdef MSN_PARTIAL_LISTS
const char *abLastChange;
const char *dynamicItemLastChange;
#endif
purple_debug_misc("msn", "Got the contact list!\n");
if (msn_parse_contact_list(session, msn_soap_message_get_xml(resp))) {
#ifdef MSN_PARTIAL_LISTS
abLastChange = purple_account_get_string(session->account,
"ablastChange", NULL);
dynamicItemLastChange = purple_account_get_string(session->account,
"DynamicItemLastChanged", NULL);
#endif
if (state->partner_scenario == 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
}
}
}
}
/*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;
MsnCallbackState *state;
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);
}
body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str,
update_str ? update_str : "");
state = msn_callback_state_new(session);
state->partner_scenario = partner_scenario;
state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_GET_CONTACT_SOAP_ACTION;
state->post_url = MSN_GET_CONTACT_POST_URL;
state->cb = msn_get_contact_list_cb;
msn_contact_request(state);
g_free(update_str);
g_free(body);
}
static void
msn_parse_addressbook_groups(MsnSession *session, PurpleXmlNode *node)
{
PurpleXmlNode *group;
purple_debug_info("msn", "msn_parse_addressbook_groups()\n");
for(group = purple_xmlnode_get_child(node, "Group"); group;
group = purple_xmlnode_get_next_twin(group)){
PurpleXmlNode *groupId, *groupInfo, *groupname;
char *group_id = NULL, *group_name = NULL;
if ((groupId = purple_xmlnode_get_child(group, "groupId")))
group_id = purple_xmlnode_get_data(groupId);
if ((groupInfo = purple_xmlnode_get_child(group, "groupInfo")) && (groupname = purple_xmlnode_get_child(groupInfo, "name")))
group_name = purple_xmlnode_get_data(groupname);
if (group_id == NULL) {
/* Group of ungroupped buddies */
g_free(group_name);
continue;
}
msn_group_new(session->userlist, group_id, group_name);
purple_debug_info("msn", "AB group_id: %s, name: %s\n", group_id, group_name ? group_name : "(null)");
if ((purple_blist_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(PurpleXmlNode *contactInfo, char **inout_mobile_number)
{
PurpleXmlNode *phones;
char *mobile_number = NULL;
gboolean mobile = FALSE;
*inout_mobile_number = NULL;
if ((phones = purple_xmlnode_get_child(contactInfo, "phones"))) {
PurpleXmlNode *contact_phone;
char *phone_type = NULL;
for (contact_phone = purple_xmlnode_get_child(phones, "ContactPhone");
contact_phone;
contact_phone = purple_xmlnode_get_next_twin(contact_phone)) {
PurpleXmlNode *contact_phone_type;
if (!(contact_phone_type =
purple_xmlnode_get_child(contact_phone, "contactPhoneType")))
continue;
phone_type = purple_xmlnode_get_data(contact_phone_type);
if (phone_type && !strcmp(phone_type, "ContactPhoneMobile")) {
PurpleXmlNode *number;
if ((number = purple_xmlnode_get_child(contact_phone, "number"))) {
PurpleXmlNode *messenger_enabled;
char *is_messenger_enabled = NULL;
g_free(mobile_number);
mobile_number = purple_xmlnode_get_data(number);
if (mobile_number &&
(messenger_enabled = purple_xmlnode_get_child(contact_phone, "isMessengerEnabled"))
&& (is_messenger_enabled = purple_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, PurpleXmlNode *node)
{
PurpleXmlNode *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 = purple_xmlnode_get_child(node, "Contact"); contactNode;
contactNode = purple_xmlnode_get_next_twin(contactNode)) {
PurpleXmlNode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds;
PurpleXmlNode *annotation;
MsnUser *user;
g_free(passport);
g_free(Name);
g_free(uid);
g_free(type);
g_free(mobile_number);
g_free(alias);
passport = Name = uid = type = mobile_number = alias = NULL;
if (!(contactId = purple_xmlnode_get_child(contactNode,"contactId"))
|| !(contactInfo = purple_xmlnode_get_child(contactNode, "contactInfo"))
|| !(contactType = purple_xmlnode_get_child(contactInfo, "contactType")))
continue;
uid = purple_xmlnode_get_data(contactId);
type = purple_xmlnode_get_data(contactType);
/* Find out our settings */
if (type && !strcmp(type, "Me")) {
/* setup the Display Name */
if (purple_connection_get_display_name(pc) == NULL) {
char *friendly = NULL;
if ((displayName = purple_xmlnode_get_child(contactInfo, "displayName")))
friendly = purple_xmlnode_get_data(displayName);
purple_connection_set_display_name(pc,
friendly ? purple_url_decode(friendly) : NULL);
g_free(friendly);
}
for (annotation = purple_xmlnode_get_child(contactInfo, "annotations/Annotation");
annotation;
annotation = purple_xmlnode_get_next_twin(annotation)) {
char *name, *value;
name = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Name"));
value = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Value"));
if (name && g_str_equal(name, "MSN.IM.MPOP")) {
if (!value || atoi(value) != 0)
session->enable_mpop = TRUE;
else
session->enable_mpop = FALSE;
}
g_free(name);
g_free(value);
}
continue; /* Not adding own account as buddy to buddylist */
}
passportName = purple_xmlnode_get_child(contactInfo, "passportName");
if (passportName != NULL) {
PurpleXmlNode *messenger_user;
/* ignore non-messenger contacts */
if ((messenger_user = purple_xmlnode_get_child(contactInfo, "isMessengerUser"))) {
char *is_messenger_user = purple_xmlnode_get_data(messenger_user);
if (is_messenger_user && !strcmp(is_messenger_user, "false")) {
passportName = NULL;
}
g_free(is_messenger_user);
}
}
if (passportName == NULL) {
PurpleXmlNode *emailsNode, *contactEmailNode, *emailNode;
PurpleXmlNode *messengerEnabledNode;
char *msnEnabled;
/*TODO: add it to the non-instant Messenger group and recognize as email Membership*/
/* Yahoo/Federated User? */
emailsNode = purple_xmlnode_get_child(contactInfo, "emails");
if (emailsNode == NULL) {
/*TODO: need to support the Mobile type*/
continue;
}
for (contactEmailNode = purple_xmlnode_get_child(emailsNode, "ContactEmail");
contactEmailNode;
contactEmailNode = purple_xmlnode_get_next_twin(contactEmailNode)) {
if ((messengerEnabledNode = purple_xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) {
msnEnabled = purple_xmlnode_get_data(messengerEnabledNode);
if (msnEnabled && !strcmp(msnEnabled, "true")) {
if ((emailNode = purple_xmlnode_get_child(contactEmailNode, "email")))
passport = purple_xmlnode_get_data(emailNode);
/* Messenger enabled, Get the Passport*/
purple_debug_info("msn", "AB Yahoo/Federated User %s\n", passport ? passport : "(null)");
g_free(msnEnabled);
break;
}
g_free(msnEnabled);
}
}
} else {
passport = purple_xmlnode_get_data(passportName);
}
/* Couldn't find anything */
if (passport == NULL)
continue;
if (!msn_email_is_valid(passport))
continue;
if ((displayName = purple_xmlnode_get_child(contactInfo, "displayName")))
Name = purple_xmlnode_get_data(displayName);
else
Name = g_strdup(passport);
for (annotation = purple_xmlnode_get_child(contactInfo, "annotations/Annotation");
annotation;
annotation = purple_xmlnode_get_next_twin(annotation)) {
char *name;
name = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Name"));
if (!name)
continue;
if (!strcmp(name, "AB.NickName"))
alias = purple_xmlnode_get_data(purple_xmlnode_get_child(annotation, "Value"));
else if (!strcmp(name, "MSN.IM.HasSharedFolder"))
; /* Do nothing yet... */
else if (!strcmp(name, "AB.Spouse"))
; /* Do nothing yet... */
else if (!strcmp(name, "MSN.Mobile.ContactId"))
; /* Do nothing yet... */
else
purple_debug_info("msn",
"Unknown AB contact annotation: %s\n", name);
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_mobile_phone(user, mobile_number);
groupIds = purple_xmlnode_get_child(contactInfo, "groupIds");
if (groupIds) {
for (guid = purple_xmlnode_get_child(groupIds, "guid"); guid;
guid = purple_xmlnode_get_next_twin(guid)) {
char *group_id = purple_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_protocol_got_user_status(session->account, user->passport, "mobile", NULL);
purple_protocol_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);
g_free(alias);
}
static void
msn_parse_addressbook_circles(MsnSession *session, PurpleXmlNode *node)
{
PurpleXmlNode *ticket;
/* TODO: Parse groups */
ticket = purple_xmlnode_get_child(node, "CircleTicket");
if (ticket) {
char *data = purple_xmlnode_get_data(ticket);
msn_notification_send_circle_auth(session, data);
g_free(data);
}
}
static gboolean
msn_parse_addressbook(MsnSession *session, PurpleXmlNode *node)
{
PurpleXmlNode *result;
PurpleXmlNode *groups;
PurpleXmlNode *contacts;
PurpleXmlNode *abNode;
PurpleXmlNode *circleNode;
PurpleXmlNode *fault;
if ((fault = purple_xmlnode_get_child(node, "Body/Fault"))) {
PurpleXmlNode *faultnode;
if ((faultnode = purple_xmlnode_get_child(fault, "faultstring"))) {
gchar *faultstring = purple_xmlnode_get_data(faultnode);
purple_debug_info("msn", "AB Faultstring: %s\n", faultstring);
g_free(faultstring);
}
if ((faultnode = purple_xmlnode_get_child(fault, "detail/errorcode"))) {
gchar *errorcode = purple_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 = purple_xmlnode_get_child(node, "Body/ABFindContactsPagedResponse/ABFindContactsPagedResult");
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 = purple_xmlnode_get_child(result, "Groups");
if (groups != NULL) {
msn_parse_addressbook_groups(session, groups);
}
/* Add an "Other Contacts" group for buddies who aren't in a group */
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_blist_find_group(MSN_INDIVIDUALS_GROUP_NAME)) == NULL){
PurpleGroup *g = purple_group_new(MSN_INDIVIDUALS_GROUP_NAME);
purple_blist_add_group(g, NULL);
}
/* Add a "Non-IM Contacts" group */
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_blist_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 = purple_xmlnode_get_child(result, "Contacts");
if (contacts != NULL) {
msn_parse_addressbook_contacts(session, contacts);
}
abNode = purple_xmlnode_get_child(result, "Ab");
if (abNode != NULL) {
PurpleXmlNode *node2;
char *tmp = NULL;
if ((node2 = purple_xmlnode_get_child(abNode, "lastChange")))
tmp = purple_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 = purple_xmlnode_get_child(abNode, "DynamicItemLastChanged")))
tmp = purple_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);
}
circleNode = purple_xmlnode_get_child(result, "CircleResult");
if (circleNode != NULL) {
msn_parse_addressbook_circles(session, circleNode);
}
return TRUE;
}
static void
msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
{
MsnCallbackState *state = data;
MsnSession *session = state->session;
g_return_if_fail(session != NULL);
purple_debug_misc("msn", "Got the Address Book!\n");
if (msn_parse_addressbook(session, msn_soap_message_get_xml(resp))) {
msn_send_privacy(purple_account_get_connection(session->account));
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_set_error(session, MSN_ERROR_BAD_BLIST, NULL);
}
}
/*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;
MsnCallbackState *state;
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);
body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE,
MsnSoapPartnerScenarioText[partner_scenario],
update_str ? update_str : "");
state = msn_callback_state_new(session);
state->body = purple_xmlnode_from_str(body, -1);
state->post_action = MSN_GET_ADDRESS_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_get_address_cb;
msn_contact_request(state);
g_free(update_str);
g_free(body);
}
/***************************************************************
* Contact Operations
***************************************************************/
static void
msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = data;
MsnSession *session = state->session;
MsnUserList *userlist;
MsnUser *user;
PurpleXmlNode *guid;
PurpleXmlNode *fault;
g_return_if_fail(session != NULL);
userlist = session->userlist;
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
char *errorcode = purple_xmlnode_get_data(purple_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");
} else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) {
PurpleBuddy *buddy = purple_blist_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."),
purple_request_cpar_from_account(
session->account));
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 = purple_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");
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);
guid = purple_xmlnode_get_child(msn_soap_message_get_xml(resp),
"Body/ABContactAddResponse/ABContactAddResult/guid");
if (guid != NULL) {
char *uid = purple_xmlnode_get_data(guid);
msn_user_set_uid(user, uid);
purple_debug_info("msn", "Set %s guid to %s.\n", state->who, uid);
g_free(uid);
}
}
/* add a Contact in MSN_INDIVIDUALS_GROUP */
void
msn_add_contact(MsnSession *session, MsnCallbackState *state, const char *passport)
{
MsnUser *user;
gchar *body = NULL;
gchar *contact_xml = NULL;
purple_debug_info("msn", "Adding contact %s to contact list\n", 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 = purple_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;
MsnSession *session = state->session;
MsnUserList *userlist;
PurpleXmlNode *fault;
g_return_if_fail(session != NULL);
userlist = session->userlist;
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
char *errorcode = purple_xmlnode_get_data(purple_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");
} else if (errorcode && !strcmp(errorcode, "InvalidPassportUser")) {
PurpleBuddy *buddy = purple_blist_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."),
purple_request_cpar_from_account(
session->account));
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 = purple_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)) {
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);
PurpleXmlNode *guid = purple_xmlnode_get_child(msn_soap_message_get_xml(resp),
"Body/ABGroupContactAddResponse/ABGroupContactAddResult/guid");
if (guid != NULL) {
char *uid = purple_xmlnode_get_data(guid);
msn_user_set_uid(user, uid);
purple_debug_info("msn", "Set %s guid to %s.\n", state->who, uid);
g_free(uid);
}
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_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, *invite;
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->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);
}
if (user->invite_message) {
char *tmp;
body = g_markup_escape_text(user->invite_message, -1);
/* Ignore the cast, we treat it as const anyway. */
tmp = (char *)purple_connection_get_display_name(purple_account_get_connection(session->account));
tmp = tmp ? g_markup_escape_text(tmp, -1) : g_strdup("");
invite = g_strdup_printf(MSN_CONTACT_INVITE_MESSAGE_XML, body, tmp);
g_free(body);
g_free(tmp);
/* We can free this now */
g_free(user->invite_message);
user->invite_message = NULL;
} else {
invite = g_strdup("");
}
body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE, groupId, contact_xml, invite);
state->body = purple_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(invite);
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);
PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
char *fault_str = purple_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");
if (user != NULL) {
msn_userlist_remove_user(userlist, user);
}
}
/*delete a Contact*/
void
msn_delete_contact(MsnSession *session, MsnUser *user)
{
gchar *body = NULL;
gchar *contact_id_xml = NULL ;
MsnCallbackState *state;
if (user->uid != NULL) {
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 {
purple_debug_info("msn", "Unable to delete contact %s without a ContactId\n", user->passport);
return;
}
state = msn_callback_state_new(session);
msn_callback_state_set_uid(state, user->uid);
/* build SOAP request */
body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE, contact_id_xml);
state->body = purple_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;
PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
char *fault_str = purple_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)) {
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);
if (user->uid != NULL)
contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
else
contact_id_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
body = g_strdup_printf(MSN_CONTACT_DEL_GROUP_TEMPLATE, contact_id_xml, groupId);
state->body = purple_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)
{
MsnCallbackState *state = (MsnCallbackState *)data;
PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
char *fault_str = purple_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");
}
/* Update a contact's info */
void
msn_update_contact(MsnSession *session, const char *passport, MsnContactUpdateType type, const char* value)
{
MsnCallbackState *state;
PurpleXmlNode *contact;
PurpleXmlNode *contact_info;
PurpleXmlNode *changes;
MsnUser *user = NULL;
purple_debug_info("msn", "Update contact information for %s with new %s: %s\n",
passport ? passport : "(null)",
type == MSN_UPDATE_DISPLAY ? "display name" : "alias",
value ? value : "(null)");
g_return_if_fail(passport != NULL);
if (strcmp(passport, "Me") != 0) {
user = msn_userlist_find_user(session->userlist, passport);
if (!user)
return;
}
contact_info = purple_xmlnode_new("contactInfo");
changes = purple_xmlnode_new("propertiesChanged");
switch (type) {
PurpleXmlNode *annotations;
PurpleXmlNode *display;
PurpleXmlNode *a, *n, *v;
case MSN_UPDATE_DISPLAY:
display = purple_xmlnode_new_child(contact_info, "displayName");
purple_xmlnode_insert_data(display, value, -1);
purple_xmlnode_insert_data(changes, "DisplayName", -1);
break;
case MSN_UPDATE_ALIAS:
annotations = purple_xmlnode_new_child(contact_info, "annotations");
purple_xmlnode_insert_data(changes, "Annotation ", -1);
a = purple_xmlnode_new_child(annotations, "Annotation");
n = purple_xmlnode_new_child(a, "Name");
purple_xmlnode_insert_data(n, "AB.NickName", -1);
v = purple_xmlnode_new_child(a, "Value");
purple_xmlnode_insert_data(v, value, -1);
break;
default:
g_return_if_reached();
}
state = msn_callback_state_new(session);
state->body = purple_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 = purple_xmlnode_get_child(state->body, "Body/ABContactUpdate/contacts/Contact");
purple_xmlnode_insert_child(contact, contact_info);
purple_xmlnode_insert_child(contact, changes);
purple_xmlnode_insert_data(purple_xmlnode_get_child(state->body,
"Header/ABApplicationHeader/PartnerScenario"),
MsnSoapPartnerScenarioText[MSN_PS_SAVE_CONTACT], -1);
if (user) {
PurpleXmlNode *contactId = purple_xmlnode_new_child(contact, "contactId");
msn_callback_state_set_uid(state, user->uid);
purple_xmlnode_insert_data(contactId, state->uid, -1);
} else {
PurpleXmlNode *contactType = purple_xmlnode_new_child(contact_info, "contactType");
purple_xmlnode_insert_data(contactType, "Me", -1);
}
msn_contact_request(state);
}
static void
msn_annotate_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = (MsnCallbackState *)data;
PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
char *fault_str = purple_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 annotated successfully\n");
}
/* Update a contact's annotations */
void
msn_annotate_contact(MsnSession *session, const char *passport, ...)
{
va_list params;
MsnCallbackState *state;
PurpleXmlNode *contact;
PurpleXmlNode *contact_info;
PurpleXmlNode *annotations;
MsnUser *user = NULL;
g_return_if_fail(passport != NULL);
if (strcmp(passport, "Me") != 0) {
user = msn_userlist_find_user(session->userlist, passport);
if (!user)
return;
}
contact_info = purple_xmlnode_new("contactInfo");
annotations = purple_xmlnode_new_child(contact_info, "annotations");
va_start(params, passport);
while (TRUE) {
const char *name;
const char *value;
PurpleXmlNode *a, *n, *v;
name = va_arg(params, const char *);
if (!name)
break;
value = va_arg(params, const char *);
if (!value)
break;
a = purple_xmlnode_new_child(annotations, "Annotation");
n = purple_xmlnode_new_child(a, "Name");
purple_xmlnode_insert_data(n, name, -1);
v = purple_xmlnode_new_child(a, "Value");
purple_xmlnode_insert_data(v, value, -1);
}
va_end(params);
state = msn_callback_state_new(session);
state->body = purple_xmlnode_from_str(MSN_CONTACT_ANNOTATE_TEMPLATE, -1);
state->action = MSN_ANNOTATE_USER;
state->post_action = MSN_CONTACT_ANNOTATE_SOAP_ACTION;
state->post_url = MSN_ADDRESS_BOOK_POST_URL;
state->cb = msn_annotate_contact_read_cb;
purple_xmlnode_insert_data(purple_xmlnode_get_child(state->body,
"Header/ABApplicationHeader/PartnerScenario"),
MsnSoapPartnerScenarioText[MSN_PS_SAVE_CONTACT], -1);
contact = purple_xmlnode_get_child(state->body, "Body/ABContactUpdate/contacts/Contact");
purple_xmlnode_insert_child(contact, contact_info);
if (user) {
PurpleXmlNode *contactId = purple_xmlnode_new_child(contact, "contactId");
msn_callback_state_set_uid(state, user->uid);
purple_xmlnode_insert_data(contactId, state->uid, -1);
} else {
PurpleXmlNode *contactType = purple_xmlnode_new_child(contact_info, "contactType");
purple_xmlnode_insert_data(contactType, "Me", -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;
PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
char *fault_str = purple_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]);
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_account_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_account_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(session->userlist != 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);
user = msn_userlist_find_user(session->userlist, passport);
g_return_if_fail(user != NULL);
if (list == MSN_LIST_PL) {
partner_scenario = MSN_PS_CONTACT_API;
if (user->networkid != MSN_NETWORK_PASSPORT)
member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
"EmailMember", "Email",
user->member_id_on_pending_list);
else
member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML,
"PassportMember", "Passport",
user->member_id_on_pending_list);
} else {
/* list == MSN_LIST_AL || list == MSN_LIST_BL */
partner_scenario = MSN_PS_BLOCK_UNBLOCK;
if (user && user->networkid != MSN_NETWORK_PASSPORT)
member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
"EmailMember", "Email",
"Email", passport, "Email");
else
member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
"PassportMember", "Passport",
"PassportName", passport, "PassportName");
}
body = g_strdup_printf(MSN_CONTACT_DELETE_FROM_LIST_TEMPLATE,
MsnSoapPartnerScenarioText[partner_scenario],
MsnMemberRole[list], member);
state->body = purple_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;
PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
char *fault_str = purple_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]);
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_account_privacy_permit_add(state->session->account, state->who, TRUE);
} else if (state->list_id == MSN_LIST_BL) {
purple_account_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;
MsnUser *user;
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);
user = msn_userlist_find_user(session->userlist, passport);
partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK;
if (user && user->networkid != MSN_NETWORK_PASSPORT)
member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
"EmailMember", "Email",
"Email", state->who, "Email");
else
member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML,
"PassportMember", "Passport",
"PassportName", state->who, "PassportName");
body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE,
MsnSoapPartnerScenarioText[partner_scenario],
MsnMemberRole[list], member);
state->body = purple_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 = purple_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;
PurpleXmlNode *fault;
/* We don't know how to respond to this faultcode, so log it */
fault = purple_xmlnode_get_child(msn_soap_message_get_xml(resp), "Body/Fault");
if (fault != NULL) {
char *fault_str = purple_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");
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 */
PurpleXmlNode *guid_node = purple_xmlnode_get_child(msn_soap_message_get_xml(resp),
"Body/ABGroupAddResponse/ABGroupAddResult/guid");
if (guid_node) {
char *guid = purple_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 = purple_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 = purple_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 = purple_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);
}