summaryrefslogtreecommitdiff
path: root/libpurple/protocols/msn
diff options
context:
space:
mode:
Diffstat (limited to 'libpurple/protocols/msn')
-rw-r--r--libpurple/protocols/msn/Makefile.am14
-rw-r--r--libpurple/protocols/msn/Makefile.mingw5
-rw-r--r--libpurple/protocols/msn/README55
-rw-r--r--libpurple/protocols/msn/cmdproc.c4
-rw-r--r--libpurple/protocols/msn/command.c50
-rw-r--r--libpurple/protocols/msn/contact.c1883
-rw-r--r--libpurple/protocols/msn/contact.h446
-rw-r--r--libpurple/protocols/msn/dialog.c42
-rw-r--r--libpurple/protocols/msn/directconn.c23
-rw-r--r--libpurple/protocols/msn/directconn.h2
-rw-r--r--libpurple/protocols/msn/group.c17
-rw-r--r--libpurple/protocols/msn/group.h24
-rw-r--r--libpurple/protocols/msn/history.c1
-rw-r--r--libpurple/protocols/msn/msg.c14
-rw-r--r--libpurple/protocols/msn/msg.h9
-rw-r--r--libpurple/protocols/msn/msn.c376
-rw-r--r--libpurple/protocols/msn/msn.h20
-rw-r--r--libpurple/protocols/msn/msnutils.c (renamed from libpurple/protocols/msn/msn-utils.c)163
-rw-r--r--libpurple/protocols/msn/msnutils.h (renamed from libpurple/protocols/msn/msn-utils.h)11
-rw-r--r--libpurple/protocols/msn/nexus.c413
-rw-r--r--libpurple/protocols/msn/nexus.h125
-rw-r--r--libpurple/protocols/msn/notification.c889
-rw-r--r--libpurple/protocols/msn/notification.h25
-rw-r--r--libpurple/protocols/msn/oim.c717
-rw-r--r--libpurple/protocols/msn/oim.h148
-rw-r--r--libpurple/protocols/msn/servconn.c26
-rw-r--r--libpurple/protocols/msn/session.c60
-rw-r--r--libpurple/protocols/msn/session.h16
-rw-r--r--libpurple/protocols/msn/slp.c10
-rw-r--r--libpurple/protocols/msn/slpcall.c19
-rw-r--r--libpurple/protocols/msn/slplink.c7
-rw-r--r--libpurple/protocols/msn/soap.c732
-rw-r--r--libpurple/protocols/msn/soap.h155
-rw-r--r--libpurple/protocols/msn/state.c192
-rw-r--r--libpurple/protocols/msn/state.h11
-rw-r--r--libpurple/protocols/msn/switchboard.c31
-rw-r--r--libpurple/protocols/msn/sync.c10
-rw-r--r--libpurple/protocols/msn/user.c146
-rw-r--r--libpurple/protocols/msn/user.h62
-rw-r--r--libpurple/protocols/msn/userlist.c441
-rw-r--r--libpurple/protocols/msn/userlist.h67
41 files changed, 6689 insertions, 772 deletions
diff --git a/libpurple/protocols/msn/Makefile.am b/libpurple/protocols/msn/Makefile.am
index 4d3add0132..b4b8fbba93 100644
--- a/libpurple/protocols/msn/Makefile.am
+++ b/libpurple/protocols/msn/Makefile.am
@@ -1,4 +1,6 @@
EXTRA_DIST = \
+ directconn.c \
+ directconn.h \
Makefile.mingw
pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION)
@@ -8,10 +10,10 @@ MSNSOURCES = \
cmdproc.h \
command.c \
command.h \
+ contact.c\
+ contact.h\
dialog.c \
dialog.h \
- directconn.c \
- directconn.h \
error.c \
error.h \
group.c \
@@ -30,6 +32,8 @@ MSNSOURCES = \
notification.h \
object.c \
object.h \
+ oim.c\
+ oim.h\
page.c \
page.h \
servconn.c \
@@ -46,6 +50,8 @@ MSNSOURCES = \
slpmsg.h \
slpsession.c \
slpsession.h \
+ soap.c\
+ soap.h\
state.c \
state.h \
switchboard.c \
@@ -60,8 +66,8 @@ MSNSOURCES = \
user.h \
userlist.c \
userlist.h \
- msn-utils.c \
- msn-utils.h
+ msnutils.c \
+ msnutils.h
AM_CFLAGS = $(st)
diff --git a/libpurple/protocols/msn/Makefile.mingw b/libpurple/protocols/msn/Makefile.mingw
index 0a825a7510..c1c7d4ac19 100644
--- a/libpurple/protocols/msn/Makefile.mingw
+++ b/libpurple/protocols/msn/Makefile.mingw
@@ -39,6 +39,7 @@ LIB_PATHS += -L$(GTK_TOP)/lib \
##
C_SRC = cmdproc.c \
command.c \
+ contact.c\
dialog.c \
directconn.c \
error.c \
@@ -50,6 +51,7 @@ C_SRC = cmdproc.c \
nexus.c \
notification.c \
object.c \
+ oim.c\
page.c \
servconn.c \
session.c \
@@ -58,6 +60,7 @@ C_SRC = cmdproc.c \
slplink.c \
slpmsg.c \
slpsession.c \
+ soap.c\
state.c \
switchboard.c \
sync.c \
@@ -65,7 +68,7 @@ C_SRC = cmdproc.c \
transaction.c \
user.c \
userlist.c \
- msn-utils.c
+ msnutils.c
OBJECTS = $(C_SRC:%.c=%.o)
diff --git a/libpurple/protocols/msn/README b/libpurple/protocols/msn/README
new file mode 100644
index 0000000000..ca93d014d2
--- /dev/null
+++ b/libpurple/protocols/msn/README
@@ -0,0 +1,55 @@
+MSNP14 Implementation
+by Ma Yuan<mayuan2006@gmail.com>
+
+1. Introduction
+-------------
+
+MSNP14 Protocol, proposed by Windows Live Messenger, is new, and there is no available implementation except the official one on Windows Platform.
+
+It has introduced many new features attractable to many users, such as:
+* Offline Instant Message
+ You can send the offline Message to the offline User,
+ The message will be posted to that user the next time when he is online.
+
+* Communicate with Yahoo User
+ U can chat with the Yahoo User in MSN, That's Fantastic! Till now ,
+ you can send text/Nudge to Yahoo User.
+
+* Windows Live ID authentition
+ WLM use the Window Live ID Authentication process,Known as Passport 3.0,
+ The procedure is totally different to the previous Passport 2.0
+
+* Video/Audio Conversation
+ U can communicate with other's via Video/Audio.
+(Though very interesting, not implemented in this version)
+
+2.New Features Added
+-----------------
+
+Till now, This project has implemented the following Feature:
+* Windows Live ID authentication.
+
+* Offline Instant Message
+Now can send and receive the Offline Instant Message to MSN user and Yahoo User.
+
+*contact management
+Can add/delete Contact
+Can add/delete Group
+
+* Communicate with Yahoo User
+Can send/receive Message/Nudge to Yahoo User.
+
+*. Changes to made to fit MSNP14 Protocol
+
+3. Reference
+-------------
+
+The very useful sites of MSN Protocol:
+MSNpiki site:
+reverse engineer of MSN Protocol.up to dated.
+http://msnpiki.msnfanatic.com/index.php/MSN_Protocol_Version_13
+
+hypothetic site:
+old MSN Protocol Introduction,but very useful for basic idea of MSN protocol
+http://www.hypothetic.org/docs/msn/index.php
+
diff --git a/libpurple/protocols/msn/cmdproc.c b/libpurple/protocols/msn/cmdproc.c
index d13e1adba8..80e32c4d4f 100644
--- a/libpurple/protocols/msn/cmdproc.c
+++ b/libpurple/protocols/msn/cmdproc.c
@@ -258,8 +258,10 @@ msn_cmdproc_process_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
trans = msn_history_find(cmdproc->history, cmd->trId);
if (trans != NULL)
- if (trans->timer)
+ if (trans->timer) {
purple_timeout_remove(trans->timer);
+ trans->timer = 0;
+ }
if (g_ascii_isdigit(cmd->command[0]))
{
diff --git a/libpurple/protocols/msn/command.c b/libpurple/protocols/msn/command.c
index 76ecd85ff2..753141ea56 100644
--- a/libpurple/protocols/msn/command.c
+++ b/libpurple/protocols/msn/command.c
@@ -36,6 +36,50 @@ is_num(char *str)
return TRUE;
}
+/*
+ * check the command is the command with payload content
+ * if it is return TRUE
+ * else return FALSE
+ */
+static gboolean
+msn_check_payload_cmd(char *str)
+{
+ if( (!strcmp(str,"ADL")) ||
+ (!strcmp(str,"GCF")) ||
+ (!strcmp(str,"SG")) ||
+ (!strcmp(str,"MSG")) ||
+ (!strcmp(str,"RML")) ||
+ (!strcmp(str,"UBX")) ||
+ (!strcmp(str,"UBN")) ||
+ (!strcmp(str,"UUM")) ||
+ (!strcmp(str,"UBM")) ||
+ (!strcmp(str,"FQY")) ||
+ (!strcmp(str,"UUN")) ||
+ (!strcmp(str,"UUX")) ||
+ (is_num(str))){
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * set command Payload length
+ */
+static void
+msn_set_payload_len(MsnCommand *cmd)
+{
+ char *param;
+ int len = 0;
+
+ if (msn_check_payload_cmd(cmd->command) && (cmd->param_count > 0)){
+ param = cmd->params[cmd->param_count - 1];
+ len = is_num(param) ? atoi(param) : 0;
+ }
+
+ cmd->payload_len = len;
+}
+
MsnCommand *
msn_command_from_string(const char *string)
{
@@ -70,7 +114,13 @@ msn_command_from_string(const char *string)
cmd->trId = is_num(param) ? atoi(param) : 0;
}
else
+ {
cmd->trId = 0;
+ }
+
+ /*add payload Length checking*/
+ msn_set_payload_len(cmd);
+ purple_debug_info("MSNP14","get payload len:%d\n",cmd->payload_len);
msn_command_ref(cmd);
diff --git a/libpurple/protocols/msn/contact.c b/libpurple/protocols/msn/contact.c
new file mode 100644
index 0000000000..b8e60bdbd5
--- /dev/null
+++ b/libpurple/protocols/msn/contact.c
@@ -0,0 +1,1883 @@
+/**
+ * @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"
+
+const char *MsnSoapPartnerScenarioText[] =
+{
+ "Initial",
+ "ContactSave",
+ "MessengerPendingList",
+ "ContactMsgrAPI",
+ "BlockUnblock"
+};
+
+const char *MsnMemberRole[] =
+{
+ "Forward",
+ "Allow",
+ "Block",
+ "Reverse",
+ "Pending"
+};
+
+/* new a contact */
+MsnContact *
+msn_contact_new(MsnSession *session)
+{
+ MsnContact *contact;
+
+ contact = g_new0(MsnContact, 1);
+ contact->session = session;
+ contact->soapconn = msn_soap_new(session,contact,1);
+
+ return contact;
+}
+
+/* destroy the contact */
+void
+msn_contact_destroy(MsnContact *contact)
+{
+ msn_soap_destroy(contact->soapconn);
+ g_free(contact);
+}
+
+MsnCallbackState *
+msn_callback_state_new(void)
+{
+ return g_new0(MsnCallbackState, 1);
+}
+
+void
+msn_callback_state_free(MsnCallbackState *state)
+{
+ if (state == NULL)
+ return;
+
+ if (state->who != NULL)
+ g_free(state->who);
+
+ if (state->old_group_name != NULL)
+ g_free(state->old_group_name);
+
+ if (state->new_group_name != NULL)
+ g_free(state->new_group_name);
+
+ if (state->guid != NULL)
+ g_free(state->guid);
+
+ g_free(state);
+}
+
+void
+msn_callback_state_set_who(MsnCallbackState *state, const gchar *who)
+{
+ gchar *new_str = NULL;
+
+ g_return_if_fail(state != NULL);
+
+ if (who != NULL)
+ new_str = g_strdup(who);
+
+ if (state->who != NULL)
+ g_free(state->who);
+
+ state->who = new_str;
+}
+
+void
+msn_callback_state_set_old_group_name(MsnCallbackState *state, const gchar *old_group_name)
+{
+ gchar *new_str = NULL;
+
+ g_return_if_fail(state != NULL);
+
+ if (old_group_name != NULL)
+ new_str = g_strdup(old_group_name);
+
+ if (state->old_group_name != NULL)
+ g_free(state->old_group_name);
+
+ state->old_group_name = new_str;
+}
+
+void
+msn_callback_state_set_new_group_name(MsnCallbackState *state, const gchar *new_group_name)
+{
+ gchar *new_str = NULL;
+
+ g_return_if_fail(state != NULL);
+
+ if (new_group_name != NULL)
+ new_str = g_strdup(new_group_name);
+
+ if (state->new_group_name != NULL)
+ g_free(state->new_group_name);
+
+ state->new_group_name = new_str;
+}
+
+void
+msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid)
+{
+ gchar *new_str = NULL;
+
+ g_return_if_fail(state != NULL);
+
+ if (guid != NULL)
+ new_str = g_strdup(guid);
+
+ if (state->guid != NULL)
+ g_free(state->guid);
+
+ state->guid = new_str;
+}
+
+
+void
+msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id)
+{
+ g_return_if_fail(state != NULL);
+
+ state->list_id = list_id;
+}
+
+void
+msn_callback_state_set_action(MsnCallbackState *state, MsnCallbackAction action)
+{
+ g_return_if_fail(state != NULL);
+
+ state->action |= action;
+}
+
+/*contact SOAP server login error*/
+static void
+msn_contact_login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+{
+ MsnSoapConn *soapconn = data;
+ MsnSession *session;
+
+ session = soapconn->session;
+ g_return_if_fail(session != NULL);
+
+ msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to contact server"));
+}
+
+/*msn contact SOAP server connect process*/
+static void
+msn_contact_login_connect_cb(gpointer data, PurpleSslConnection *gsc,
+ PurpleInputCondition cond)
+{
+ MsnSoapConn *soapconn = data;
+ MsnSession * session;
+ MsnContact *contact;
+
+ contact = soapconn->parent;
+ g_return_if_fail(contact != NULL);
+
+ session = contact->session;
+ g_return_if_fail(session != NULL);
+
+ /*login ok!We can retrieve the contact list*/
+// msn_get_contact_list(contact, MSN_PS_INITIAL, NULL);
+}
+
+/*get MSN member role utility*/
+static MsnListId
+msn_get_memberrole(char * role)
+{
+
+ 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 User Type*/
+static int
+msn_get_user_type(char * type)
+{
+ if (!strcmp(type,"Regular")) {
+ return MSN_USER_TYPE_PASSPORT;
+ }
+ if (!strcmp(type,"Live")) {
+ return MSN_USER_TYPE_PASSPORT;
+ }
+ if (!strcmp(type,"LivePending")) {
+ return MSN_USER_TYPE_PASSPORT;
+ }
+
+ return MSN_USER_TYPE_UNKNOWN;
+}
+
+/* Create the AddressBook in the server, if we don't have one */
+static void
+msn_create_address_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn *soapconn = data;
+ MsnContact *contact;
+
+ if (soapconn->body == NULL)
+ return;
+
+ contact = soapconn->parent;
+ g_return_if_fail(contact != NULL);
+
+ purple_debug_info("MSN AddressBook", "Address Book successfully created!\n");
+ msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL);
+
+// msn_soap_free_read_buf(soapconn);
+ return;
+}
+
+static void
+msn_create_address_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSN AddressBook","AddressBookAdd written\n");
+ soapconn->read_cb = msn_create_address_cb;
+
+ return;
+}
+
+static void
+msn_create_address_book(MsnContact * contact)
+{
+ MsnSoapReq *soap_request;
+ gchar *body;
+
+ g_return_if_fail(contact != NULL);
+ g_return_if_fail(contact->session != NULL);
+ g_return_if_fail(contact->session->user != NULL);
+ g_return_if_fail(contact->session->user->passport != NULL);
+
+ purple_debug_info("MSN AddressBook","Creating an Address Book.\n");
+
+ body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport);
+
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,MSN_ADD_ADDRESSBOOK_SOAP_ACTION,
+ body,
+ NULL,
+ msn_create_address_cb,
+ msn_create_address_written_cb);
+ msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+ g_free(body);
+
+ return;
+}
+
+/*parse contact list*/
+static void
+msn_parse_contact_list(MsnContact * contact)
+{
+ MsnSession * session;
+ MsnListOp list_op = 0;
+ MsnListId list;
+ char * passport, *typedata;
+ xmlnode *fault, *faultstringnode, *faultdetail, *errorcode;
+ xmlnode *node, *body, *response, *result, *services;
+ xmlnode *service, *memberships, *info, *handle, *handletype;
+ xmlnode *LastChangeNode;
+ xmlnode *membershipnode, *members, *member, *passportNode;
+ char *LastChangeStr;
+
+ session = contact->session;
+ node = xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len);
+
+ if (node == NULL) {
+ purple_debug_error("MSNCL","Unable to parse SOAP data!\n");
+ return;
+ }
+
+ purple_debug_misc("MSNCL","Parsing contact list with size %d\n", contact->soapconn->body_len);
+
+ purple_debug_misc("MSNCL","Root node @ %p: Name: '%s', child: '%s', lastchild: '%s'\n",node,node->name,node->child->name,node->lastchild->name);
+ body = xmlnode_get_child(node,"Body");
+
+ if (body == NULL) {
+ purple_debug_warning("MSNCL", "Failed to parse contact list Body node\n");
+ xmlnode_free(node);
+ return;
+ }
+ purple_debug_info("MSNCL","Body @ %p: Name: '%s'\n",body,body->name);
+
+ /* Did we receive a <Fault> ? */
+ if ( (fault = xmlnode_get_child(body, "Fault")) != NULL) {
+ purple_debug_info("MSNCL","Fault received from SOAP server!\n");
+
+ if ( (faultstringnode = xmlnode_get_child(fault, "faultstring")) != NULL ) {
+ gchar * faultstring = xmlnode_get_data(faultstringnode);
+ purple_debug_info("MSNCL","Faultstring: %s\n", faultstring);
+ g_free(faultstring);
+ }
+ if ( (faultdetail = xmlnode_get_child(fault, "detail")) != NULL ) {
+ purple_debug_info("MSNCL","detail @ %p, name: %s\n",faultdetail, faultdetail->name);
+
+ if ( (errorcode = xmlnode_get_child(faultdetail, "errorcode")) != NULL ) {
+ purple_debug_info("MSNCL","errorcode @ %p, name: %s\n",errorcode, errorcode->name);
+
+ if (errorcode->child != NULL) {
+ gchar *errorcodestring = xmlnode_get_data(errorcode);
+ purple_debug_info("MSNCL", "Error Code: %s\n", errorcodestring);
+
+ if ( !strncmp(errorcodestring, "ABDoesNotExist", 14) ) {
+ xmlnode_free(node);
+ g_free(errorcodestring);
+ msn_create_address_book(contact);
+ return;
+ }
+ g_free(errorcodestring);
+ }
+ }
+ }
+ xmlnode_free(node);
+ msn_get_contact_list(contact, MSN_PS_INITIAL, NULL);
+ return;
+ }
+
+ response = xmlnode_get_child(body,"FindMembershipResponse");
+
+ if (response == NULL) {
+ /* 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>
+ */
+ xmlnode_free(node);
+ msn_get_contact_list(contact, MSN_PS_INITIAL, NULL);
+ return;
+ }
+ purple_debug_info("MSNCL","FindMembershipResponse @ %p: Name: '%s'\n",response,response->name);
+
+ result = xmlnode_get_child(response,"FindMembershipResult");
+ if (result == NULL) {
+ purple_debug_warning("MSNCL","Received No Update!\n");
+ xmlnode_free(node);
+ return;
+ }
+ purple_debug_info("MSNCL","Result @ %p: Name: '%s'\n", result, result->name);
+
+ if ( (services = xmlnode_get_child(result,"Services")) == NULL) {
+ purple_debug_misc("MSNCL","No <Services> received.\n");
+ xmlnode_free(node);
+ return;
+ }
+
+ purple_debug_info("MSNCL","Services @ %p\n",services);
+
+ for (service = xmlnode_get_child(services, "Service"); service;
+ service = xmlnode_get_next_twin(service)) {
+ purple_debug_info("MSNCL","Service @ %p\n",service);
+
+ if ( (info = xmlnode_get_child(service,"Info")) == NULL ) {
+ purple_debug_error("MSNCL","Error getting 'Info' child node\n");
+ continue;
+ }
+ if ( (handle = xmlnode_get_child(info,"Handle")) == NULL ) {
+ purple_debug_error("MSNCL","Error getting 'Handle' child node\n");
+ continue;
+ }
+ if ( (handletype = xmlnode_get_child(handle,"Type")) == NULL ) {
+ purple_debug_error("MSNCL","Error getting 'Type' child node\n");
+ continue;
+ }
+
+ if ( (typedata = xmlnode_get_data(handletype)) == NULL) {
+ purple_debug_error("MSNCL","Error retrieving data from 'Type' child node\n");
+ g_free(typedata);
+ continue;
+ }
+
+ purple_debug_info("MSNCL","processing '%s' Service\n", typedata);
+
+ if ( !g_strcasecmp(typedata, "Profile") ) {
+ /* Process Windows Live 'Messenger Roaming Identity' */
+ g_free(typedata);
+ continue;
+ }
+
+ if ( !g_strcasecmp(typedata, "Messenger") ) {
+
+ /*Last Change Node*/
+ LastChangeNode = xmlnode_get_child(service, "LastChange");
+ LastChangeStr = xmlnode_get_data(LastChangeNode);
+ purple_debug_info("MSNCL","LastChangeNode: '%s'\n",LastChangeStr);
+ purple_account_set_string(session->account, "CLLastChange", LastChangeStr);
+ g_free(LastChangeStr);
+
+ memberships = xmlnode_get_child(service,"Memberships");
+ if (memberships == NULL) {
+ purple_debug_warning("MSNCL","Memberships = NULL, cleaning up and returning.\n");
+ g_free(typedata);
+ xmlnode_free(node);
+ return;
+ }
+ purple_debug_info("MSNCL","Memberships @ %p: Name: '%s'\n",memberships,memberships->name);
+ for (membershipnode = xmlnode_get_child(memberships, "Membership"); membershipnode;
+ membershipnode = xmlnode_get_next_twin(membershipnode)){
+ xmlnode *roleNode;
+ char *role;
+
+ roleNode = xmlnode_get_child(membershipnode,"MemberRole");
+ role = xmlnode_get_data(roleNode);
+ list = msn_get_memberrole(role);
+ list_op = 1 << list;
+
+ purple_debug_info("MSNCL","MemberRole role: %s, list_op: %d\n",role,list_op);
+
+ g_free(role);
+
+ members = xmlnode_get_child(membershipnode,"Members");
+ for (member = xmlnode_get_child(members, "Member"); member;
+ member = xmlnode_get_next_twin(member)){
+ MsnUser *user = NULL;
+ xmlnode *typeNode, *membershipIdNode=NULL;
+ gchar *type, *membershipId = NULL;
+
+ purple_debug_info("MSNCL","Member type: %s\n", xmlnode_get_attrib(member,"type"));
+
+ if( !g_strcasecmp(xmlnode_get_attrib(member,"type"), "PassportMember") ) {
+ passportNode = xmlnode_get_child(member,"PassportName");
+ passport = xmlnode_get_data(passportNode);
+ typeNode = xmlnode_get_child(member,"Type");
+ type = xmlnode_get_data(typeNode);
+ purple_debug_info("MSNCL","Passport name: '%s', Type: %s\n",passport,type);
+ g_free(type);
+
+ user = msn_userlist_find_add_user(session->userlist,passport,NULL);
+
+ membershipIdNode = xmlnode_get_child(member,"MembershipId");
+ if (membershipIdNode != NULL) {
+ membershipId = xmlnode_get_data(membershipIdNode);
+ if (membershipId != NULL) {
+ user->membership_id[list] = atoi(membershipId);
+ g_free(membershipId);
+ }
+ }
+
+ msn_got_lst_user(session, user, list_op, NULL);
+
+ g_free(passport);
+ }
+
+ if (!g_strcasecmp(xmlnode_get_attrib(member,"type"),"PhoneMember")) {
+ }
+
+ if (!g_strcasecmp(xmlnode_get_attrib(member,"type"),"EmailMember")) {
+ xmlnode *emailNode;
+
+ emailNode = xmlnode_get_child(member,"Email");
+ passport = xmlnode_get_data(emailNode);
+ purple_debug_info("MSNCL","Email Member: Name: '%s', list_op: %d\n", passport, list_op);
+ user = msn_userlist_find_add_user(session->userlist, passport, NULL);
+
+ membershipIdNode = xmlnode_get_child(member,"MembershipId");
+ if (membershipIdNode != NULL) {
+ membershipId = xmlnode_get_data(membershipIdNode);
+ if (membershipId != NULL) {
+ user->membership_id[list] = atoi(membershipId);
+ g_free(membershipId);
+ }
+ }
+
+ msn_got_lst_user(session, user, list_op, NULL);
+ g_free(passport);
+ }
+ }
+ }
+ g_free(typedata); /* Free 'Type' node data after processing 'Messenger' Service */
+ }
+ }
+
+ xmlnode_free(node); /* Free the whole XML tree */
+}
+
+static void
+msn_get_contact_list_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn *soapconn = data;
+ MsnContact *contact;
+ MsnSession *session;
+ const char *abLastChange;
+ const char *dynamicItemLastChange;
+ gchar *partner_scenario;
+
+ if (soapconn->body == NULL)
+ return;
+
+ purple_debug_misc("MSNCL","Got the contact list!\n");
+
+ contact = soapconn->parent;
+ g_return_if_fail(contact != NULL);
+ session = soapconn->session;
+ g_return_if_fail(session != NULL);
+ g_return_if_fail(soapconn->data_cb != NULL);
+
+ partner_scenario = soapconn->data_cb;
+
+ msn_parse_contact_list(contact);
+ /*free the read buffer*/
+ msn_soap_free_read_buf(soapconn);
+
+ abLastChange = purple_account_get_string(session->account, "ablastChange", NULL);
+ dynamicItemLastChange = purple_account_get_string(session->account, "dynamicItemLastChange", NULL);
+
+ if (!strcmp(partner_scenario, MsnSoapPartnerScenarioText[MSN_PS_INITIAL])) {
+
+#ifdef MSN_PARTIAL_LISTS
+ /* XXX: this should be enabled when we can correctly do partial
+ syncs with the server. Currently we need to retrieve the whole
+ list to detect sync issues */
+ msn_get_address_book(contact, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange);
+#else
+ msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL);
+#endif
+ } else {
+ msn_soap_free_read_buf(soapconn);
+ }
+}
+
+static void
+msn_get_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_misc("MSNCL","Sent SOAP request for the contact list.\n");
+ soapconn->read_cb = msn_get_contact_list_cb;
+// msn_soap_read_cb(data,source,cond);
+}
+
+/*SOAP get contact list*/
+void
+msn_get_contact_list(MsnContact * contact, const MsnSoapPartnerScenario partner_scenario, const char *update_time)
+{
+ MsnSoapReq *soap_request;
+ gchar *body = NULL;
+ gchar * update_str;
+ const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario];
+
+ purple_debug_misc("MSNCL","Getting Contact List.\n");
+
+ if ( update_time != NULL ) {
+ purple_debug_info("MSNCL","Last update time: %s\n",update_time);
+ update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML,update_time);
+ } else {
+ update_str = g_strdup("");
+ }
+
+ body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, update_str);
+ g_free(update_str);
+
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_GET_CONTACT_POST_URL,
+ MSN_GET_CONTACT_SOAP_ACTION,
+ body,
+ (gpointer) partner_scenario_str,
+ msn_get_contact_list_cb,
+ msn_get_contact_written_cb);
+ msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+ g_free(body);
+}
+
+static void
+msn_parse_addressbook_groups(MsnContact *contact, xmlnode *node)
+{
+ MsnSession *session = contact->session;
+ xmlnode *group;
+
+ purple_debug_info("MsnAb","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, *group_name;
+
+ groupId = xmlnode_get_child(group,"groupId");
+ group_id = xmlnode_get_data(groupId);
+ 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("MsnAB","group_id: %s, name: %s\n",group_id,group_name);
+ 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 void
+msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
+{
+ MsnSession *session = contact->session;
+ xmlnode *contactNode;
+
+ for(contactNode = xmlnode_get_child(node, "Contact"); contactNode;
+ contactNode = xmlnode_get_next_twin(contactNode)){
+ xmlnode *contactId,*contactInfo,*contactType,*passportName,*displayName,*guid;
+ xmlnode *groupIds;
+ MsnUser *user;
+ MsnUserType usertype;
+ char *passport = NULL, *Name = NULL, *uid = NULL, *type = NULL;
+
+ contactId= xmlnode_get_child(contactNode,"contactId");
+ uid = xmlnode_get_data(contactId);
+
+ contactInfo = xmlnode_get_child(contactNode,"contactInfo");
+ contactType = xmlnode_get_child(contactInfo,"contactType");
+ type = xmlnode_get_data(contactType);
+
+ /*setup the Display Name*/
+ if (!strcmp(type, "Me")){
+ char *friendly;
+ friendly = xmlnode_get_data(xmlnode_get_child(contactInfo, "displayName"));
+ purple_connection_set_display_name(session->account->gc, purple_url_decode(friendly));
+ g_free(friendly);
+ g_free(uid);
+ g_free(type);
+ continue; /* Not adding own account as buddy to buddylist */
+ }
+ usertype = msn_get_user_type(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*/
+ g_free(uid);
+ g_free(type);
+ continue;
+ }
+ for(contactEmailNode = xmlnode_get_child(emailsNode,"ContactEmail");contactEmailNode;
+ contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){
+ messengerEnabledNode = xmlnode_get_child(contactEmailNode,"isMessengerEnabled");
+ if(messengerEnabledNode == NULL){
+ g_free(uid);
+ g_free(type);
+ break;
+ }
+ msnEnabled = xmlnode_get_data(messengerEnabledNode);
+ if(!strcmp(msnEnabled,"true")){
+ /*Messenger enabled, Get the Passport*/
+ emailNode = xmlnode_get_child(contactEmailNode,"email");
+ passport = xmlnode_get_data(emailNode);
+ purple_debug_info("MsnAB","Yahoo User %s\n",passport);
+ usertype = MSN_USER_TYPE_YAHOO;
+ g_free(uid);
+ g_free(type);
+ g_free(passport);
+ g_free(msnEnabled);
+ break;
+ }else{
+ /*TODO maybe we can just ignore it in Purple?*/
+ emailNode = xmlnode_get_child(contactEmailNode,"email");
+ passport = xmlnode_get_data(emailNode);
+ purple_debug_info("MSNAB","Other type user\n");
+ }
+ g_free(msnEnabled);
+ }
+ } else {
+ passport = xmlnode_get_data(passportName);
+ }
+
+ if (passport == NULL) {
+ g_free(uid);
+ g_free(type);
+ continue;
+ }
+
+ displayName = xmlnode_get_child(contactInfo,"displayName");
+ if (displayName == NULL) {
+ Name = g_strdup(passport);
+ } else {
+ Name = xmlnode_get_data(displayName);
+ }
+
+ purple_debug_misc("MsnAB","passport:{%s} uid:{%s} display:{%s}\n",
+ passport,uid,Name);
+
+ user = msn_userlist_find_add_user(session->userlist, passport,Name);
+ msn_user_set_uid(user,uid);
+ msn_user_set_type(user, usertype);
+ g_free(Name);
+ g_free(passport);
+ g_free(uid);
+ g_free(type);
+
+ purple_debug_misc("MsnAB","parse guid...\n");
+ 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;
+ group_id = xmlnode_get_data(guid);
+ msn_user_add_group_id(user,group_id);
+ purple_debug_misc("MsnAB","guid:%s\n",group_id);
+ g_free(group_id);
+ }
+ } else {
+ /*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);
+ }
+}
+
+static gboolean
+msn_parse_addressbook(MsnContact * contact)
+{
+ MsnSession * session;
+ xmlnode * node,*body,*response,*result;
+ xmlnode *groups;
+ xmlnode *contacts;
+ xmlnode *abNode;
+ xmlnode *fault, *faultstringnode, *faultdetail, *errorcode;
+
+ session = contact->session;
+
+
+
+ node = xmlnode_from_str(contact->soapconn->body, contact->soapconn->body_len);
+ if ( node == NULL ) {
+ purple_debug_error("MSN AddressBook","Error parsing Address Book with size %d\n", contact->soapconn->body_len);
+ return FALSE;
+ }
+
+ purple_debug_misc("MSN AddressBook", "Parsing Address Book with size %d\n", contact->soapconn->body_len);
+
+ purple_debug_misc("MSN AddressBook","node{%p},name:%s,child:%s,last:%s\n",node,node->name,node->child->name,node->lastchild->name);
+
+ body = xmlnode_get_child(node,"Body");
+ purple_debug_misc("MSN AddressBook","body{%p},name:%s\n",body,body->name);
+
+ if ( (fault = xmlnode_get_child(body, "Fault")) != NULL) {
+ purple_debug_info("MSN AddressBook","Fault received from SOAP server!\n");
+
+ if ( (faultstringnode = xmlnode_get_child(fault, "faultstring")) != NULL ) {
+ gchar *faultstring = xmlnode_get_data(faultstringnode);
+ purple_debug_info("MSN AddressBook","Faultstring: %s\n", faultstring);
+ g_free(faultstring);
+ }
+ if ( (faultdetail = xmlnode_get_child(fault, "detail")) != NULL ) {
+ purple_debug_info("MSN AddressBook","detail @ %p, name: %s\n",faultdetail, faultdetail->name);
+
+ if ( (errorcode = xmlnode_get_child(faultdetail, "errorcode")) != NULL ) {
+ gchar *errorcodestring;
+ purple_debug_info("MSN AddressBook","errorcode @ %p, name: %s\n",errorcode, errorcode->name);
+
+ errorcodestring = xmlnode_get_data(errorcode);
+ purple_debug_info("MSN AddressBook", "Error Code: %s\n", errorcodestring);
+
+ if ( !strncmp(errorcodestring, "ABDoesNotExist", 14) ) {
+ g_free(errorcodestring);
+ xmlnode_free(node);
+ return TRUE;
+ }
+ g_free(errorcodestring);
+ }
+ }
+ xmlnode_free(node);
+ return FALSE;
+ }
+
+
+ response = xmlnode_get_child(body,"ABFindAllResponse");
+
+ if (response == NULL) {
+ xmlnode_free(node);
+ return FALSE;
+ }
+
+ purple_debug_misc("MSN SOAP","response{%p},name:%s\n",response,response->name);
+ result = xmlnode_get_child(response,"ABFindAllResult");
+ if(result == NULL){
+ purple_debug_misc("MSNAB","receive no address book update\n");
+ xmlnode_free(node);
+ return TRUE;
+ }
+ purple_debug_info("MSN SOAP","result{%p},name:%s\n",result,result->name);
+
+ /*Process Group List*/
+ groups = xmlnode_get_child(result,"groups");
+ if (groups != NULL) {
+ msn_parse_addressbook_groups(contact, 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("MsnAB","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("MsnAB","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("MSNAB","process contact list...\n");
+ contacts =xmlnode_get_child(result,"contacts");
+ if (contacts != NULL) {
+ msn_parse_addressbook_contacts(contact, contacts);
+ }
+
+ abNode =xmlnode_get_child(result,"ab");
+ if(abNode != NULL){
+ xmlnode *LastChangeNode, *DynamicItemLastChangedNode;
+ char *lastchange, *dynamicChange;
+
+ LastChangeNode = xmlnode_get_child(abNode,"lastChange");
+ lastchange = xmlnode_get_data(LastChangeNode);
+ purple_debug_info("MsnAB"," lastchanged Time:{%s}\n",lastchange);
+ purple_account_set_string(session->account, "ablastChange", lastchange);
+
+ DynamicItemLastChangedNode = xmlnode_get_child(abNode,"DynamicItemLastChanged");
+ dynamicChange = xmlnode_get_data(DynamicItemLastChangedNode);
+ purple_debug_info("MsnAB"," DynamicItemLastChanged :{%s}\n",dynamicChange);
+ purple_account_set_string(session->account, "DynamicItemLastChanged", lastchange);
+ g_free(dynamicChange);
+ g_free(lastchange);
+ }
+
+ xmlnode_free(node);
+ msn_soap_free_read_buf(contact->soapconn);
+ return TRUE;
+}
+
+static void
+msn_get_address_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+ MsnContact *contact;
+ MsnSession *session;
+
+ if (soapconn->body == NULL)
+ return;
+
+ contact = soapconn->parent;
+ g_return_if_fail(contact != NULL);
+ session = soapconn->session;
+ g_return_if_fail(session != NULL);
+
+ purple_debug_misc("MSN AddressBook", "Got the Address Book!\n");
+
+ if ( msn_parse_addressbook(contact) ) {
+ //msn_soap_free_read_buf(soapconn);
+
+ if (!session->logged_in) {
+ msn_send_privacy(session->account->gc);
+ msn_notification_dump_contact(session);
+ }
+ } else {
+ /* This is making us loop infinitely when we fail to parse the address book,
+ disable for now (we should re-enable when we send timestamps)
+ */
+ /*
+ msn_get_address_book(contact, NULL, NULL);
+ */
+ msn_session_disconnect(session);
+ purple_connection_error(session->account->gc, _("Unable to retrieve MSN Address Book"));
+ }
+
+ /*free the read buffer*/
+ msn_soap_free_read_buf(soapconn);
+}
+
+/**/
+static void
+msn_address_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_misc("MSN AddressBook","Sent SOAP request for the Address Book.\n");
+ soapconn->read_cb = msn_get_address_cb;
+}
+
+/*get the address book*/
+void
+msn_get_address_book(MsnContact *contact, const MsnSoapPartnerScenario partner_scenario, const char *LastChanged, const char *dynamicItemLastChange)
+{
+ MsnSoapReq *soap_request;
+ char *body = NULL;
+ char *ab_update_str,*update_str;
+
+ purple_debug_misc("MSN AddressBook","Getting Address Book\n");
+
+ /*build SOAP and POST it*/
+ if ( LastChanged != NULL ) {
+ ab_update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML,LastChanged);
+ } else {
+ ab_update_str = g_strdup("");
+ }
+ if ( dynamicItemLastChange != NULL ) {
+ update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML,
+ dynamicItemLastChange);
+ } else {
+ update_str = g_strdup(ab_update_str);
+ }
+ g_free(ab_update_str);
+
+
+ body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], update_str);
+ g_free(update_str);
+
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,MSN_GET_ADDRESS_SOAP_ACTION,
+ body,
+ NULL,
+ msn_get_address_cb,
+ msn_address_written_cb);
+ msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+ g_free(body);
+}
+
+static void
+msn_add_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+ MsnCallbackState *state = NULL;
+ MsnUserList *userlist;
+ MsnUser *user;
+
+ g_return_if_fail(soapconn->data_cb != NULL);
+ g_return_if_fail(soapconn->session != NULL);
+ g_return_if_fail(soapconn->session->userlist != NULL);
+
+ state = (MsnCallbackState *) soapconn->data_cb;
+
+ if (soapconn->body == NULL) {
+ msn_callback_state_free(state);
+ return;
+ }
+
+ userlist = soapconn->session->userlist;
+
+ purple_debug_info("MSNCL","Contact added successfully\n");
+
+ // the code this block is replacing didn't send ADL for yahoo contacts,
+ // but i haven't confirmed this is WLM's behaviour wrt yahoo contacts
+
+ if ( !msn_user_is_yahoo(soapconn->session->account, state->who) ) {
+
+ msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
+ msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
+ }
+ msn_notification_send_fqy(soapconn->session, state->who);
+
+ user = msn_userlist_find_add_user(userlist, state->who, state->who);
+ msn_user_add_group_id(user, state->guid);
+
+ if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
+ msn_del_contact_from_list(soapconn->session->contact, NULL, state->who, MSN_LIST_PL);
+ } else {
+ msn_soap_free_read_buf(soapconn);
+ }
+
+ msn_callback_state_free(state);
+}
+
+static void
+msn_add_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSNCL","Add contact request written\n");
+ soapconn->read_cb = msn_add_contact_read_cb;
+}
+
+/* add a Contact in MSN_INDIVIDUALS_GROUP */
+void
+msn_add_contact(MsnContact *contact, MsnCallbackState *state, const char *passport)
+{
+ MsnSoapReq *soap_request;
+ gchar *body = NULL;
+ gchar *contact_xml = NULL;
+ gchar *soap_action;
+/* 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);
+*/
+ purple_debug_info("MSNCL","Adding contact %s to contact list\n", passport);
+
+// if ( !strcmp(state->guid, MSN_INDIVIDUALS_GROUP_ID) ) {
+ contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
+// }
+ body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE, contact_xml);
+
+ g_free(contact_xml);
+
+ /*build SOAP and POST it*/
+ soap_action = g_strdup(MSN_CONTACT_ADD_SOAP_ACTION);
+
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,
+ soap_action,
+ body,
+ state,
+ msn_add_contact_read_cb,
+ msn_add_contact_written_cb);
+ msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+ g_free(soap_action);
+ g_free(body);
+}
+
+static void
+msn_add_contact_to_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+ MsnCallbackState *state;
+ MsnUserList *userlist;
+
+ g_return_if_fail(soapconn->data_cb != NULL);
+ g_return_if_fail(soapconn->session != NULL);
+ g_return_if_fail(soapconn->session->userlist != NULL);
+
+ userlist = soapconn->session->userlist;
+
+ state = (MsnCallbackState *) soapconn->data_cb;
+
+ if (soapconn->body == NULL) {
+ msn_callback_state_free(state);
+ return;
+ }
+
+ if (msn_userlist_add_buddy_to_group(userlist, state->who, state->new_group_name) == TRUE) {
+ purple_debug_info("MSNCL", "Contact %s added to group %s successfully!\n", state->who, state->new_group_name);
+ } else {
+ purple_debug_info("MSNCL","Contact %s added to group %s successfully on server, but failed in the local list\n", state->who, state->new_group_name);
+ }
+
+ if (state->action & MSN_ADD_BUDDY) {
+ MsnUser *user = msn_userlist_find_user(userlist, state->who);
+
+ if ( !msn_user_is_yahoo(soapconn->session->account, state->who) ) {
+
+ msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
+ msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
+ }
+ msn_notification_send_fqy(soapconn->session, state->who);
+
+ if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
+ msn_del_contact_from_list(soapconn->session->contact, NULL, state->who, MSN_LIST_PL);
+ msn_callback_state_free(state);
+ return;
+ }
+ }
+
+ if (state->action & MSN_MOVE_BUDDY) {
+ msn_del_contact_from_group(soapconn->session->contact, state->who, state->old_group_name);
+ } else {
+ msn_callback_state_free(state);
+ msn_soap_free_read_buf(soapconn);
+ }
+}
+
+static void
+msn_add_contact_to_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSNCL","Add contact to group request sent!\n");
+ soapconn->read_cb = msn_add_contact_to_group_read_cb;
+}
+
+void
+msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
+ const char *passport, const char *groupId)
+{
+ MsnSoapReq *soap_request;
+ MsnUserList *userlist;
+ MsnUser *user;
+ gchar *body = NULL, *soap_action, *contact_xml;
+
+ g_return_if_fail(passport != NULL);
+ g_return_if_fail(groupId != NULL);
+
+ g_return_if_fail(contact != NULL);
+ g_return_if_fail(contact->session != NULL);
+ g_return_if_fail(contact->session->userlist != NULL);
+
+ userlist = contact->session->userlist;
+
+ 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(contact, state, passport);
+ return;
+ }
+
+ if (state->action & MSN_MOVE_BUDDY) {
+ msn_user_add_group_id(user, groupId);
+ msn_del_contact_from_group(contact, passport, state->old_group_name);
+ } else {
+ msn_callback_state_free(state);
+ }
+
+ return;
+ }
+
+
+ purple_debug_info("MSNCL", "Adding user %s to group %s\n", passport,
+ msn_userlist_find_group_name(userlist, groupId));
+
+ user = msn_userlist_find_user(userlist, passport);
+ if (user == NULL) {
+ purple_debug_warning("MSN CL", "Unable to retrieve user %s from the userlist!\n", passport);
+ }
+
+ if (user->uid != NULL) {
+ contact_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
+ } else {
+ contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport);
+ }
+
+ body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE, groupId, contact_xml);
+ g_free(contact_xml);
+
+ /*build SOAP and POST it*/
+ soap_action = g_strdup(MSN_ADD_CONTACT_GROUP_SOAP_ACTION);
+
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,
+ soap_action,
+ body,
+ state,
+ msn_add_contact_to_group_read_cb,
+ msn_add_contact_to_group_written_cb);
+ msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+ g_free(soap_action);
+ g_free(body);
+}
+
+
+
+static void
+msn_delete_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ if (soapconn->body == NULL)
+ return;
+
+ // we should probably delete it from the userlist aswell
+ purple_debug_info("MSNCL","Delete contact successful\n");
+ msn_soap_free_read_buf(soapconn);
+}
+
+static void
+msn_delete_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSNCL","Delete contact request written\n");
+ soapconn->read_cb = msn_delete_contact_read_cb;
+}
+
+/*delete a Contact*/
+void
+msn_delete_contact(MsnContact *contact, const char *contactId)
+{
+ gchar *body = NULL;
+ gchar *contact_id_xml = NULL ;
+ MsnSoapReq *soap_request;
+
+ g_return_if_fail(contactId != NULL);
+ contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, contactId);
+
+ /* build SOAP request */
+ purple_debug_info("MSNCL","Deleting contact with contactId: %s\n", contactId);
+ body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE, contact_id_xml);
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,
+ MSN_CONTACT_DEL_SOAP_ACTION,
+ body,
+ NULL,
+ msn_delete_contact_read_cb,
+ msn_delete_contact_written_cb);
+
+ g_free(contact_id_xml);
+
+ /* POST the SOAP request */
+ msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+ g_free(body);
+}
+
+static void
+msn_del_contact_from_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+ MsnCallbackState *state = (MsnCallbackState *) soapconn->data_cb;
+
+ if (soapconn->body == NULL) {
+ msn_callback_state_free(state);
+ return;
+ }
+
+ if (msn_userlist_rem_buddy_from_group(soapconn->session->userlist, state->who, state->old_group_name)) {
+ purple_debug_info("MSN CL", "Contact %s deleted successfully from group %s\n", state->who, state->old_group_name);
+ } else {
+ purple_debug_info("MSN CL", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name);
+ }
+
+ msn_callback_state_free(state);
+ msn_soap_free_read_buf(soapconn);
+ return;
+}
+
+static void
+msn_del_contact_from_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSN CL","Del contact from group request sent!\n");
+ soapconn->read_cb = msn_del_contact_from_group_read_cb;
+}
+
+void
+msn_del_contact_from_group(MsnContact *contact, const char *passport, const char *group_name)
+{
+ MsnSoapReq *soap_request;
+ MsnUserList * userlist;
+ MsnUser *user;
+ MsnCallbackState *state;
+ gchar *body = NULL, *soap_action, *contact_id_xml;
+ const gchar *groupId;
+
+ g_return_if_fail(passport != NULL);
+ g_return_if_fail(group_name != NULL);
+ g_return_if_fail(contact != NULL);
+ g_return_if_fail(contact->session != NULL);
+ g_return_if_fail(contact->session->userlist != NULL);
+
+ userlist = contact->session->userlist;
+
+ groupId = msn_userlist_find_group_id(userlist, group_name);
+ if (groupId != NULL) {
+ purple_debug_info("MSN CL", "Deleting user %s from group %s\n", passport, group_name);
+ } else {
+ purple_debug_warning("MSN CL", "Unable to retrieve group id from group %s !\n", group_name);
+ return;
+ }
+
+ user = msn_userlist_find_user(userlist, passport);
+
+ if (user == NULL) {
+ purple_debug_warning("MSN CL", "Unable to retrieve user from passport %s!\n", passport);
+ return;
+ }
+
+ 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();
+ msn_callback_state_set_who(state, passport);
+ msn_callback_state_set_guid(state, groupId);
+ msn_callback_state_set_old_group_name(state, group_name);
+
+ contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid);
+ body = g_strdup_printf(MSN_CONTACT_DEL_GROUP_TEMPLATE, contact_id_xml, groupId);
+ g_free(contact_id_xml);
+
+ /*build SOAP and POST it*/
+ soap_action = g_strdup(MSN_CONTACT_DEL_GROUP_SOAP_ACTION);
+
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,
+ soap_action,
+ body,
+ state,
+ msn_del_contact_from_group_read_cb,
+ msn_del_contact_from_group_written_cb);
+ msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+ g_free(soap_action);
+ g_free(body);
+}
+
+
+static void
+msn_update_contact_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn *soapconn = data;
+
+ if (soapconn->body == NULL)
+ return;
+
+ purple_debug_info("MSN CL","Contact updated successfully\n");
+}
+
+static void
+msn_update_contact_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSN CL","Update contact information request sent\n");
+ soapconn->read_cb = msn_update_contact_read_cb;
+}
+
+/* Update a contact's nickname */
+
+void
+msn_update_contact(MsnContact *contact, const char* nickname)
+{
+ MsnSoapReq *soap_request;
+ gchar *body = NULL, *escaped_nickname;
+
+ purple_debug_info("MSN CL","Update contact information with new friendly name: %s\n", nickname);
+
+ escaped_nickname = g_markup_escape_text(nickname, -1);
+
+ body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, escaped_nickname);
+
+ g_free(escaped_nickname);
+ /*build SOAP and POST it*/
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,
+ MSN_CONTACT_UPDATE_SOAP_ACTION,
+ body,
+ NULL,
+ msn_update_contact_read_cb,
+ msn_update_contact_written_cb);
+ msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+ g_free(body);
+}
+
+
+static void
+msn_del_contact_from_list_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+ MsnCallbackState *state = NULL;
+
+ g_return_if_fail(soapconn->data_cb != NULL);
+ g_return_if_fail(soapconn->session != NULL);
+ g_return_if_fail(soapconn->session->contact != NULL);
+
+ state = (MsnCallbackState *) soapconn->data_cb;
+
+ if (soapconn->body == NULL) {
+ msn_callback_state_free(state);
+ return;
+ }
+
+ purple_debug_info("MSN CL", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
+
+ if (state->list_id == MSN_LIST_PL) {
+ msn_add_contact_to_list(soapconn->session->contact, state, state->who, MSN_LIST_RL);
+ return;
+ }
+
+ if (state->list_id == MSN_LIST_AL) {
+ purple_privacy_permit_remove(soapconn->session->account, state->who, TRUE);
+ msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_BL);
+ msn_callback_state_free(state);
+ return;
+ }
+
+ if (state->list_id == MSN_LIST_BL) {
+ purple_privacy_deny_remove(soapconn->session->account, state->who, TRUE);
+ msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_AL);
+ msn_callback_state_free(state);
+ return;
+ }
+
+ msn_callback_state_free(state);
+ msn_soap_free_read_buf(soapconn);
+}
+
+static void
+msn_del_contact_from_list_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSN CL","Delete contact from list SOAP request sent!\n");
+ soapconn->read_cb = msn_del_contact_from_list_read_cb;
+}
+
+void
+msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state,
+ const gchar *passport, const MsnListId list)
+{
+ MsnSoapReq *soap_request;
+ gchar *body = NULL, *member = NULL;
+ MsnSoapPartnerScenario partner_scenario;
+ MsnUser *user;
+
+ g_return_if_fail(contact != NULL);
+ g_return_if_fail(passport != NULL);
+ g_return_if_fail(list < 5);
+
+ purple_debug_info("MSN CL", "Deleting contact %s from %s list\n", passport, MsnMemberRole[list]);
+
+ if (state == NULL) {
+ state = msn_callback_state_new();
+ }
+ msn_callback_state_set_list_id(state, list);
+ msn_callback_state_set_who(state, passport);
+
+ if (list == MSN_LIST_PL) {
+ g_return_if_fail(contact->session != NULL);
+ g_return_if_fail(contact->session->userlist != NULL);
+
+ user = msn_userlist_find_user(contact->session->userlist, passport);
+
+ partner_scenario = MSN_PS_CONTACT_API;
+ member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, user->membership_id[MSN_LIST_PL]);
+ } else {
+ /* list == MSN_LIST_AL || list == MSN_LIST_BL */
+ partner_scenario = MSN_PS_BLOCK_UNBLOCK;
+
+ member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
+ }
+
+ body = g_strdup_printf( MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE,
+ MsnSoapPartnerScenarioText[partner_scenario],
+ MsnMemberRole[list],
+ member);
+ g_free(member);
+
+ soap_request = msn_soap_request_new( MSN_CONTACT_SERVER,
+ MSN_SHARE_POST_URL,
+ MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION,
+ body,
+ state,
+ msn_del_contact_from_list_read_cb,
+ msn_del_contact_from_list_written_cb);
+
+ msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+ g_free(body);
+}
+
+static void
+msn_add_contact_to_list_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+ MsnCallbackState *state = NULL;
+
+ g_return_if_fail(soapconn->data_cb != NULL);
+
+ state = (MsnCallbackState *) soapconn->data_cb;
+
+ if (soapconn->body == NULL) {
+ msn_callback_state_free(state);
+ return;
+ }
+
+ purple_debug_info("MSN CL", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
+
+ if (state->list_id == MSN_LIST_RL && (state->action & MSN_DENIED_BUDDY) ) {
+ g_return_if_fail(soapconn->session != NULL);
+ g_return_if_fail(soapconn->session->contact != NULL);
+
+ msn_add_contact_to_list(soapconn->session->contact, NULL, state->who, MSN_LIST_BL);
+ return;
+ }
+
+ if (state->list_id == MSN_LIST_AL) {
+ purple_privacy_permit_add(soapconn->session->account, state->who, TRUE);
+ } else if (state->list_id == MSN_LIST_BL) {
+ purple_privacy_deny_add(soapconn->session->account, state->who, TRUE);
+ }
+
+ msn_callback_state_free(state);
+ msn_soap_free_read_buf(soapconn);
+}
+
+
+static void
+msn_add_contact_to_list_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSN CL","Add contact to list SOAP request sent!\n");
+ soapconn->read_cb = msn_add_contact_to_list_read_cb;
+}
+
+void
+msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
+ const gchar *passport, const MsnListId list)
+{
+ MsnSoapReq *soap_request;
+ gchar *body = NULL, *member = NULL;
+ MsnSoapPartnerScenario partner_scenario;
+
+ g_return_if_fail(contact != NULL);
+ g_return_if_fail(passport != NULL);
+ g_return_if_fail(list < 5);
+
+ purple_debug_info("MSN CL", "Adding contact %s to %s list\n", passport, MsnMemberRole[list]);
+
+ if (state == NULL) {
+ state = msn_callback_state_new();
+ }
+ msn_callback_state_set_list_id(state, list);
+ msn_callback_state_set_who(state, passport);
+
+ partner_scenario = (list == MSN_LIST_RL) ? MSN_PS_CONTACT_API : MSN_PS_BLOCK_UNBLOCK;
+
+ member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
+
+ body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE,
+ MsnSoapPartnerScenarioText[partner_scenario],
+ MsnMemberRole[list],
+ member);
+
+ g_free(member);
+
+ soap_request = msn_soap_request_new( MSN_CONTACT_SERVER,
+ MSN_SHARE_POST_URL,
+ MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION,
+ body,
+ state,
+ msn_add_contact_to_list_read_cb,
+ msn_add_contact_to_list_written_cb);
+
+ msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+ g_free(body);
+}
+
+
+#if 0
+static void
+msn_gleams_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ purple_debug_info("MSNP14","Gleams read done\n");
+}
+
+static void
+msn_gleams_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSNP14","finish Group written\n");
+ soapconn->read_cb = msn_gleams_read_cb;
+// msn_soap_read_cb(data,source,cond);
+}
+
+/*get the gleams info*/
+void
+msn_get_gleams(MsnContact *contact)
+{
+ MsnSoapReq *soap_request;
+
+ purple_debug_info("MSNP14","msn get gleams info...\n");
+ /*build SOAP and POST it*/
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,
+ MSN_GET_GLEAMS_SOAP_ACTION,
+ MSN_GLEAMS_TEMPLATE,
+ NULL,
+ msn_gleams_read_cb,
+ msn_gleams_written_cb);
+ msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+}
+#endif
+
+
+/***************************************************************
+ * Group Operations
+ ***************************************************************/
+
+static void
+msn_group_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+ MsnUserList *userlist;
+ MsnCallbackState *state = NULL;
+
+ purple_debug_info("MSN CL", "Group request successful.\n");
+
+ g_return_if_fail(soapconn->session != NULL);
+ g_return_if_fail(soapconn->session->userlist != NULL);
+ g_return_if_fail(soapconn->session->contact != NULL);
+
+ state = (MsnCallbackState *) soapconn->data_cb;
+
+ if (soapconn->body == NULL) {
+ msn_callback_state_free(state);
+ return;
+ }
+
+ if (state) {
+ userlist = soapconn->session->userlist;
+
+ if (state->action & MSN_RENAME_GROUP) {
+ msn_userlist_rename_group_id(soapconn->session->userlist,
+ state->guid,
+ state->new_group_name);
+ }
+
+ if (state->action & MSN_ADD_GROUP) {
+ gchar *guid, *endguid;
+
+ guid = g_strstr_len(soapconn->read_buf, soapconn->read_len, "<guid>");
+ guid += 6;
+ endguid = g_strstr_len(soapconn->read_buf, soapconn->read_len, "</guid>");
+ *endguid = '\0';
+ /* create and add the new group to the userlist */
+ purple_debug_info("MSN CL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid);
+ msn_group_new(soapconn->session->userlist, guid, state->new_group_name);
+
+ if (state->action & MSN_ADD_BUDDY) {
+ msn_userlist_add_buddy(soapconn->session->userlist,
+ state->who,
+ state->new_group_name);
+ msn_callback_state_free(state);
+ return;
+ }
+
+ if (state->action & MSN_MOVE_BUDDY) {
+ msn_add_contact_to_group(soapconn->session->contact, state, state->who, guid);
+ return;
+ }
+ }
+
+ if (state->action & MSN_DEL_GROUP) {
+ GList *l;
+
+ msn_userlist_remove_group_id(soapconn->session->userlist, state->guid);
+ for (l = userlist->users; l != NULL; l = l->next) {
+ msn_user_remove_group_id( (MsnUser *)l->data, state->guid);
+ }
+
+ }
+
+ msn_callback_state_free(state);
+ }
+
+ msn_soap_free_read_buf(soapconn);
+}
+
+static void
+msn_group_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ purple_debug_info("MSN CL","Sent group request.\n");
+ soapconn->read_cb = msn_group_read_cb;
+}
+
+/* add group */
+void
+msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_name)
+{
+ MsnSoapReq *soap_request;
+ MsnContact *contact;
+ char *body = NULL;
+ gchar *escaped_group_name;
+
+ g_return_if_fail(session != NULL);
+ g_return_if_fail(group_name != NULL);
+
+ contact = session->contact;
+ purple_debug_info("MSN CL","Adding group %s to contact list.\n", group_name);
+
+ if (state == NULL) {
+ state = msn_callback_state_new();
+ }
+
+ msn_callback_state_set_action(state, MSN_ADD_GROUP);
+ msn_callback_state_set_new_group_name(state, group_name);
+
+ /* escape group name's html special chars so it can safely be sent
+ * in a XML SOAP request
+ */
+ escaped_group_name = g_markup_escape_text(group_name, -1);
+ body = g_strdup_printf(MSN_GROUP_ADD_TEMPLATE, escaped_group_name);
+ g_free(escaped_group_name);
+
+ /*build SOAP and POST it*/
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,
+ MSN_GROUP_ADD_SOAP_ACTION,
+ body,
+ state,
+ msn_group_read_cb,
+ msn_group_written_cb);
+ msn_soap_post(contact->soapconn,soap_request,msn_contact_connect_init);
+
+ g_free(body);
+}
+
+/* delete group */
+void
+msn_del_group(MsnSession *session, const gchar *group_name)
+{
+ MsnSoapReq *soap_request;
+ MsnContact *contact;
+ MsnCallbackState *state;
+ char *body = NULL;
+ const gchar *guid;
+
+ g_return_if_fail(session != NULL);
+
+ g_return_if_fail(group_name != NULL);
+ contact = session->contact;
+ purple_debug_info("MSN CL","Deleting group %s from contact list\n", group_name);
+
+ guid = msn_userlist_find_group_id(session->userlist, group_name);
+
+ /* if group uid we need to del is NULL,
+ * we need to delete nothing
+ */
+ if (guid == NULL) {
+ purple_debug_info("MSN CL", "Group %s guid not found, returning.\n", group_name);
+ return;
+ }
+
+ 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();
+ msn_callback_state_set_action(state, MSN_DEL_GROUP);
+ msn_callback_state_set_guid(state, guid);
+
+ body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE, guid);
+ /*build SOAP and POST it*/
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,
+ MSN_GROUP_DEL_SOAP_ACTION,
+ body,
+ state,
+ msn_group_read_cb,
+ msn_group_written_cb);
+ msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+ g_free(body);
+}
+
+/* rename group */
+void
+msn_contact_rename_group(MsnSession *session, const char *old_group_name, const char *new_group_name)
+{
+ MsnSoapReq *soap_request;
+ MsnContact *contact;
+ gchar * escaped_group_name, *body = NULL;
+ const gchar * guid;
+ MsnCallbackState *state = msn_callback_state_new();
+
+ g_return_if_fail(session != NULL);
+ g_return_if_fail(session->userlist != NULL);
+ g_return_if_fail(old_group_name != NULL);
+ g_return_if_fail(new_group_name != NULL);
+
+ contact = session->contact;
+ purple_debug_info("MSN CL", "Renaming group %s to %s.\n", old_group_name, new_group_name);
+
+ guid = msn_userlist_find_group_id(session->userlist, old_group_name);
+ if (guid == NULL)
+ return;
+
+ msn_callback_state_set_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) ) {
+ msn_add_group(session, 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);
+
+ /* escape group name's html special chars so it can safely be sent
+ * in a XML SOAP request
+ */
+ escaped_group_name = g_markup_escape_text(new_group_name, -1);
+
+ body = g_strdup_printf(MSN_GROUP_RENAME_TEMPLATE, guid, escaped_group_name);
+
+ soap_request = msn_soap_request_new(MSN_CONTACT_SERVER,
+ MSN_ADDRESS_BOOK_POST_URL,
+ MSN_GROUP_RENAME_SOAP_ACTION,
+ body,
+ state,
+ msn_group_read_cb,
+ msn_group_written_cb);
+ msn_soap_post(contact->soapconn, soap_request, msn_contact_connect_init);
+
+ g_free(escaped_group_name);
+ g_free(body);
+}
+
+void
+msn_contact_connect_init(MsnSoapConn *soapconn)
+{
+ msn_soap_init(soapconn, MSN_CONTACT_SERVER, 1,
+ msn_contact_login_connect_cb,
+ msn_contact_login_error_cb);
+}
diff --git a/libpurple/protocols/msn/contact.h b/libpurple/protocols/msn/contact.h
new file mode 100644
index 0000000000..ebea72f40f
--- /dev/null
+++ b/libpurple/protocols/msn/contact.h
@@ -0,0 +1,446 @@
+/**
+ * @file contact.h Header file for contact.c
+ * Author
+ * 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
+ */
+#ifndef _MSN_CONTACT_H_
+#define _MSN_CONTACT_H_
+
+#define MSN_CONTACT_SERVER "omega.contacts.msn.com"
+
+/* Get Contact List */
+
+#define MSN_GET_CONTACT_POST_URL "/abservice/SharingService.asmx"
+#define MSN_GET_CONTACT_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/FindMembership"
+#define MSN_GET_CONTACT_UPDATE_XML "<View>Full</View>"\
+ "<deltasOnly>true</deltasOnly>"\
+ "<lastChange>%s</lastChange>"
+#define MSN_GET_CONTACT_TEMPLATE "<?xml version='1.0' encoding='utf-8'?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+ "<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+ "<IsMigration xmlns=\"http://www.msn.com/webservices/AddressBook\">false</IsMigration>"\
+ "<PartnerScenario xmlns=\"http://www.msn.com/webservices/AddressBook\">%s</PartnerScenario>"\
+ "</ABApplicationHeader>"\
+ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ManagedGroupRequest xmlns=\"http://www.msn.com/webservices/AddressBook\">false</ManagedGroupRequest>"\
+ "</ABAuthHeader>"\
+ "</soap:Header>"\
+ "<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+ "<FindMembership xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<serviceFilter xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<Types xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Messenger</ServiceType>"\
+ "<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Invitation</ServiceType>"\
+ "<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">SocialNetwork</ServiceType>"\
+ "<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Space</ServiceType>"\
+ "<ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Profile</ServiceType>"\
+ "</Types>"\
+ "</serviceFilter>"\
+ "%s"\
+ "</FindMembership>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+/************************************************
+ * Address Book SOAP
+ * *********************************************/
+
+#define MSN_ADDRESS_BOOK_POST_URL "/abservice/abservice.asmx"
+
+/* Create AddressBook template */
+#define MSN_ADD_ADDRESSBOOK_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABAdd"
+
+#define MSN_ADD_ADDRESSBOOK_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+ "<soap:Header>"\
+ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+ "<IsMigration>false</IsMigration>"\
+ "<PartnerScenario>Initial</PartnerScenario>"\
+ "</ABApplicationHeader>"\
+ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ManagedGroupRequest>false</ManagedGroupRequest>"\
+ "</ABAuthHeader>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<ABAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<abInfo>"\
+ "<name/>"\
+ "<ownerPuid>0</ownerPuid>"\
+ "<ownerEmail>%s</ownerEmail>"\
+ "<fDefault>true</fDefault>"\
+ "</abInfo>"\
+ "</ABAdd>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+/* Get AddressBook */
+#define MSN_GET_ADDRESS_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll"
+#define MSN_GET_ADDRESS_FULL_TIME "0001-01-01T00:00:00.0000000-08:00"
+#define MSN_GET_ADDRESS_UPDATE_XML "<deltasOnly>true</deltasOnly>"\
+ "<lastChange>%s</lastChange>"
+
+#define MSN_GET_GLEAM_UPDATE_XML \
+ "%s"\
+ "<dynamicItemView>Gleam</dynamicItemView>"\
+ "<dynamicItemLastChange>%s</dynamicItemLastChange>"
+
+#define MSN_GET_ADDRESS_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+ "<soap:Header>"\
+ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+ "<IsMigration>false</IsMigration>"\
+ "<PartnerScenario>%s</PartnerScenario>"\
+ "</ABApplicationHeader>"\
+ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ManagedGroupRequest>false</ManagedGroupRequest>"\
+ "</ABAuthHeader>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<abId>00000000-0000-0000-0000-000000000000</abId>"\
+ "<abView>Full</abView>"\
+ "%s"\
+ "</ABFindAll>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+
+/*Gleams SOAP request template*/
+#define MSN_GET_GLEAMS_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll"
+#define MSN_GLEAMS_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+ "<soap:Header>"\
+ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+ "<IsMigration>false</IsMigration>"\
+ "<PartnerScenario>Initial</PartnerScenario>"\
+ "</ABApplicationHeader>"\
+ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ManagedGroupRequest>false</ManagedGroupRequest>"\
+ "</ABAuthHeader>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<abId>00000000-0000-0000-0000-000000000000</abId>"\
+ "<abView>Full</abView>"\
+ "<dynamicItemView>Gleam</dynamicItemView>"\
+ "<dynamicItemLastChange>0001-01-01T00:00:00.0000000-08:00</dynamicItemLastChange>"\
+ "</ABFindAll>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+
+/*******************************************************
+ * Contact Management SOAP actions
+ *******************************************************/
+
+/* Add a new contact t*/
+#define MSN_CONTACT_ADD_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactAdd"
+#define MSN_CONTACT_LIVE_PENDING_XML "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><contactType>LivePending</contactType><passportName>%s</passportName><isMessengerUser>true</isMessengerUser></contactInfo></Contact>"
+
+#define MSN_CONTACT_XML "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<contactInfo>"\
+ "<passportName>%s</passportName>"\
+ "<isSmtp>false</isSmtp>"\
+ "<isMessengerUser>true</isMessengerUser>"\
+ "</contactInfo>"\
+ "</Contact>"
+
+#define MSN_CONTACT_DISPLAYNAME_XML "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><displayName>%s</displayName><passportName>%s</passportName><isMessengerUser>true</isMessengerUser></contactInfo></Contact>"
+
+#define MSN_ADD_CONTACT_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>ContactSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABContactAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts><options><EnableAllowListManagement>true</EnableAllowListManagement></options></ABContactAdd></soap:Body></soap:Envelope>"
+
+/* Add a contact to a group */
+#define MSN_ADD_CONTACT_GROUP_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupContactAdd"
+#define MSN_ADD_CONTACT_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+ "<soap:Header>"\
+ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+ "<IsMigration>false</IsMigration>"\
+ "<PartnerScenario>ContactSave</PartnerScenario>"\
+ "</ABApplicationHeader>"\
+ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ManagedGroupRequest>false</ManagedGroupRequest>"\
+ "</ABAuthHeader>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<ABGroupContactAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<abId>00000000-0000-0000-0000-000000000000</abId>"\
+ "<groupFilter>"\
+ "<groupIds>"\
+ "<guid>%s</guid>"\
+ "</groupIds>"\
+ "</groupFilter>"\
+ "<contacts>%s</contacts>"\
+ "<groupContactAddOptions>"\
+ "<fGenerateMissingQuickName>true</fGenerateMissingQuickName>"\
+ "<EnableAllowListManagement>true</EnableAllowListManagement>"\
+ "</groupContactAddOptions>"\
+ "</ABGroupContactAdd>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+/* Delete a contact from the Contact List */
+#define MSN_CONTACT_DEL_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactDelete"
+#define MSN_CONTACT_ID_XML "<Contact><contactId>%s</contactId></Contact>"
+#define MSN_DEL_CONTACT_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts></ABContactDelete></soap:Body></soap:Envelope>"
+
+/* Remove a contact from a group */
+#define MSN_CONTACT_DEL_GROUP_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupContactDelete"
+#define MSN_CONTACT_DEL_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts><groupFilter><groupIds><guid>%s</guid></groupIds></groupFilter></ABGroupContactDelete></soap:Body></soap:Envelope>"
+
+
+/* Update Contact Nickname */
+#define MSN_CONTACT_UPDATE_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactUpdate"
+#define MSN_CONTACT_UPDATE_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+ "<soap:Header>"\
+ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+ "<IsMigration>false</IsMigration>"\
+ "<PartnerScenario>Timer</PartnerScenario>"\
+ "</ABApplicationHeader>"\
+ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ManagedGroupRequest>false</ManagedGroupRequest>"\
+ "</ABAuthHeader>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<ABContactUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<abId>00000000-0000-0000-0000-000000000000</abId>"\
+ "<contacts>"\
+ "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<contactInfo>"\
+ "<contactType>Me</contactType>"\
+ "<displayName>%s</displayName>"\
+ "</contactInfo>"\
+ "<propertiesChanged>DisplayName</propertiesChanged>"\
+ "</Contact>"\
+ "</contacts>"\
+ "</ABContactUpdate>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+
+/*******************************************************
+ * Add/Delete contact from lists SOAP actions
+ *******************************************************/
+
+/* block means delete from allow list and add contact to block list */
+#define MSN_SHARE_POST_URL "/abservice/SharingService.asmx"
+
+#define MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/AddMember"
+#define MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/DeleteMember"
+
+#define MSN_MEMBER_PASSPORT_XML "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+ "<Type>Passport</Type>"\
+ "<State>Accepted</State>"\
+ "<PassportName>%s</PassportName>"\
+ "</Member>"
+
+#define MSN_MEMBER_MEMBERSHIPID_XML "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\
+ "<Type>Passport</Type>"\
+ "<MembershipId>%u</MembershipId>"\
+ "<State>Accepted</State>"\
+ "</Member>"
+
+/* first delete contact from allow list */
+
+#define MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+ "<soap:Header>"\
+ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+ "<IsMigration>false</IsMigration>"\
+ "<PartnerScenario>%s</PartnerScenario>"\
+ "</ABApplicationHeader>"\
+ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ManagedGroupRequest>false</ManagedGroupRequest>"\
+ "</ABAuthHeader>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<DeleteMember xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<serviceHandle>"\
+ "<Id>0</Id>"\
+ "<Type>Messenger</Type>"\
+ "<ForeignId></ForeignId>"\
+ "</serviceHandle>"\
+ "<memberships>"\
+ "<Membership>"\
+ "<MemberRole>%s</MemberRole>"\
+ "<Members>"\
+ "%s"\
+ "</Members>"\
+ "</Membership>"\
+ "</memberships>"\
+ "</DeleteMember>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+#define MSN_CONTACT_ADD_TO_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\
+ "<soap:Header>"\
+ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\
+ "<IsMigration>false</IsMigration>"\
+ "<PartnerScenario>%s</PartnerScenario>"\
+ "</ABApplicationHeader>"\
+ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<ManagedGroupRequest>false</ManagedGroupRequest>"\
+ "</ABAuthHeader>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<AddMember xmlns=\"http://www.msn.com/webservices/AddressBook\">"\
+ "<serviceHandle>"\
+ "<Id>0</Id>"\
+ "<Type>Messenger</Type>"\
+ "<ForeignId></ForeignId>"\
+ "</serviceHandle>"\
+ "<memberships>"\
+ "<Membership>"\
+ "<MemberRole>%s</MemberRole>"\
+ "<Members>"\
+ "%s"\
+ "</Members>"\
+ "</Membership>"\
+ "</memberships>"\
+ "</AddMember>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+
+
+/*******************************************************
+ * Group management SOAP actions
+ *******************************************************/
+
+/* add a group */
+#define MSN_GROUP_ADD_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupAdd"
+#define MSN_GROUP_ADD_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>GroupSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupAddOptions><fRenameOnMsgrConflict>false</fRenameOnMsgrConflict></groupAddOptions><groupInfo><GroupInfo><name>%s</name><groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType><fMessenger>false</fMessenger><annotations><Annotation><Name>MSN.IM.Display</Name><Value>1</Value></Annotation></annotations></GroupInfo></groupInfo></ABGroupAdd></soap:Body></soap:Envelope>"
+
+/* delete a group */
+#define MSN_GROUP_DEL_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupDelete"
+#define MSN_GROUP_DEL_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupFilter><groupIds><guid>%s</guid></groupIds></groupFilter></ABGroupDelete></soap:Body></soap:Envelope>"
+
+/* change a group's name */
+#define MSN_GROUP_RENAME_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupUpdate"
+#define MSN_GROUP_RENAME_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groups><Group><groupId>%s</groupId><groupInfo><name>%s</name></groupInfo><propertiesChanged>GroupName </propertiesChanged></Group></groups></ABGroupUpdate></soap:Body></soap:Envelope>"
+
+typedef enum
+{
+ MSN_ADD_BUDDY = 0x01,
+ MSN_MOVE_BUDDY = 0x02,
+ MSN_ACCEPTED_BUDDY = 0x04,
+ MSN_DENIED_BUDDY = 0x08,
+ MSN_ADD_GROUP = 0x10,
+ MSN_DEL_GROUP = 0x20,
+ MSN_RENAME_GROUP = 0x40,
+} MsnCallbackAction;
+
+typedef struct _MsnContact MsnContact;
+
+struct _MsnContact
+{
+ MsnSession *session;
+
+ MsnSoapConn *soapconn;
+};
+
+typedef struct _MsnCallbackState MsnCallbackState;
+
+struct _MsnCallbackState
+{
+ gchar * who;
+ gchar * old_group_name;
+ gchar * new_group_name;
+ gchar * guid;
+ MsnListId list_id;
+ MsnCallbackAction action;
+};
+
+typedef enum
+{
+ MSN_PS_INITIAL,
+ MSN_PS_SAVE_CONTACT,
+ MSN_PS_PENDING_LIST,
+ MSN_PS_CONTACT_API,
+ MSN_PS_BLOCK_UNBLOCK
+} MsnSoapPartnerScenario;
+
+/************************************************
+ * function prototype
+ ************************************************/
+MsnContact * msn_contact_new(MsnSession *session);
+void msn_contact_destroy(MsnContact *contact);
+
+MsnCallbackState * msn_callback_state_new(void);
+void msn_callback_state_free(MsnCallbackState *state);
+void msn_callback_state_set_who(MsnCallbackState *state, const gchar *who);
+void msn_callback_state_set_old_group_name(MsnCallbackState *state,
+ const gchar *old_group_name);
+void msn_callback_state_set_new_group_name(MsnCallbackState *state,
+ const gchar *new_group_name);
+void msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid);
+void msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id);
+void msn_callback_state_set_action(MsnCallbackState *state,
+ MsnCallbackAction action);
+
+void msn_contact_connect(MsnContact *contact);
+void msn_get_contact_list(MsnContact * contact,
+ const MsnSoapPartnerScenario partner_scenario,
+ const char *update);
+void msn_get_address_book(MsnContact *contact,
+ const MsnSoapPartnerScenario partner_scenario,
+ const char * update, const char * gupdate);
+
+/* contact SOAP operations */
+void msn_update_contact(MsnContact *contact, const char* nickname);
+
+void msn_add_contact(MsnContact *contact, MsnCallbackState *state,
+ const char *passport);
+void msn_delete_contact(MsnContact *contact, const char *contactId);
+
+void msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
+ const char *passport, const char *groupId);
+void msn_del_contact_from_group(MsnContact *contact, const char *passport,
+ const char *group_name);
+/* group operations */
+void msn_add_group(MsnSession *session, MsnCallbackState *state,
+ const char* group_name);
+void msn_del_group(MsnSession *session, const gchar *group_name);
+void msn_contact_rename_group(MsnSession *session, const char *old_group_name,
+ const char *new_group_name);
+
+/* lists operations */
+void msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
+ const gchar *passport, const MsnListId list);
+void msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state,
+ const gchar *passport, const MsnListId list);
+
+void msn_contact_connect_init(MsnSoapConn *soapconn);
+
+#endif /* _MSN_CONTACT_H_ */
+
diff --git a/libpurple/protocols/msn/dialog.c b/libpurple/protocols/msn/dialog.c
index d713f58b83..d9d3f09c04 100644
--- a/libpurple/protocols/msn/dialog.c
+++ b/libpurple/protocols/msn/dialog.c
@@ -36,6 +36,7 @@ typedef struct
/* Remove the buddy referenced by the MsnAddRemData before the serverside list is changed.
* If the buddy will be added, he'll be added back; if he will be removed, he won't be. */
+/* Actually with our MSNP14 code that isn't true yet, he won't be added back :( */
static void
msn_complete_sync_issue(MsnAddRemData *data)
{
@@ -44,28 +45,32 @@ msn_complete_sync_issue(MsnAddRemData *data)
if (data->group != NULL)
group = purple_find_group(data->group);
-
+
if (group != NULL)
buddy = purple_find_buddy_in_group(purple_connection_get_account(data->gc), data->who, group);
else
buddy = purple_find_buddy(purple_connection_get_account(data->gc), data->who);
-
+
if (buddy != NULL)
purple_blist_remove_buddy(buddy);
}
+
static void
msn_add_cb(MsnAddRemData *data)
{
- MsnSession *session;
- MsnUserList *userlist;
-
+#if 0
+ /* this *should* be necessary !! */
msn_complete_sync_issue(data);
+#endif
- session = data->gc->proto_data;
- userlist = session->userlist;
+ if (g_list_find(purple_connections_get_all(), data->gc) != NULL)
+ {
+ MsnSession *session = data->gc->proto_data;
+ MsnUserList *userlist = session->userlist;
- msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, data->group);
+ msn_userlist_add_buddy(userlist, data->who, data->group);
+ }
g_free(data->group);
g_free(data->who);
@@ -75,17 +80,20 @@ msn_add_cb(MsnAddRemData *data)
static void
msn_rem_cb(MsnAddRemData *data)
{
- MsnSession *session;
- MsnUserList *userlist;
-
msn_complete_sync_issue(data);
- session = data->gc->proto_data;
- userlist = session->userlist;
-
- msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->group);
+ if (g_list_find(purple_connections_get_all(), data->gc) != NULL)
+ {
+ MsnSession *session = data->gc->proto_data;
+ MsnUserList *userlist = session->userlist;
+
+ if (data->group == NULL) {
+ msn_userlist_rem_buddy_from_list(userlist, data->who, MSN_LIST_FL);
+ } else {
+ g_free(data->group);
+ }
+ }
- g_free(data->group);
g_free(data->who);
g_free(data);
}
@@ -104,7 +112,7 @@ msn_show_sync_issue(MsnSession *session, const char *passport,
data = g_new0(MsnAddRemData, 1);
data->who = g_strdup(passport);
- data->group = g_strdup(group_name);
+ data->group = group_name != NULL ? g_strdup(group_name) : NULL;
data->gc = gc;
msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"),
diff --git a/libpurple/protocols/msn/directconn.c b/libpurple/protocols/msn/directconn.c
index 86eba0ceda..cd935c60a8 100644
--- a/libpurple/protocols/msn/directconn.c
+++ b/libpurple/protocols/msn/directconn.c
@@ -76,7 +76,6 @@ msn_directconn_send_handshake(MsnDirectConn *directconn)
* Connection Functions
**************************************************************************/
-#if 0
static int
create_listener(int port)
{
@@ -160,7 +159,6 @@ create_listener(int port)
return fd;
}
-#endif
static size_t
msn_directconn_write(MsnDirectConn *directconn,
@@ -288,6 +286,11 @@ read_cb(gpointer data, gint source, PurpleInputCondition cond)
/* ERROR */
purple_debug_error("msn", "error reading\n");
+ if (directconn->inpa)
+ purple_input_remove(directconn->inpa);
+
+ close(directconn->fd);
+
msn_directconn_destroy(directconn);
return;
@@ -302,6 +305,11 @@ read_cb(gpointer data, gint source, PurpleInputCondition cond)
/* ERROR */
purple_debug_error("msn", "error reading\n");
+ if (directconn->inpa)
+ purple_input_remove(directconn->inpa);
+
+ close(directconn->fd);
+
msn_directconn_destroy(directconn);
return;
@@ -348,17 +356,22 @@ read_cb(gpointer data, gint source, PurpleInputCondition cond)
/* ERROR */
purple_debug_error("msn", "error reading\n");
+ if (directconn->inpa)
+ purple_input_remove(directconn->inpa);
+
+ close(directconn->fd);
+
msn_directconn_destroy(directconn);
}
}
static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
+connect_cb(gpointer data, gint source, PurpleInputCondition cond)
{
MsnDirectConn* directconn;
int fd;
- purple_debug_misc("msn", "directconn: connect_cb: %d\n", source);
+ purple_debug_misc("msn", "directconn: connect_cb: %d, %d.\n", source, cond);
directconn = data;
directconn->connect_data = NULL;
@@ -434,7 +447,6 @@ msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port)
return FALSE;
}
-#if 0
void
msn_directconn_listen(MsnDirectConn *directconn)
{
@@ -454,7 +466,6 @@ msn_directconn_listen(MsnDirectConn *directconn)
directconn->port = port;
directconn->c = 0;
}
-#endif
MsnDirectConn*
msn_directconn_new(MsnSlpLink *slplink)
diff --git a/libpurple/protocols/msn/directconn.h b/libpurple/protocols/msn/directconn.h
index 2ac404c884..759e664255 100644
--- a/libpurple/protocols/msn/directconn.h
+++ b/libpurple/protocols/msn/directconn.h
@@ -52,9 +52,7 @@ struct _MsnDirectConn
MsnDirectConn *msn_directconn_new(MsnSlpLink *slplink);
gboolean msn_directconn_connect(MsnDirectConn *directconn,
const char *host, int port);
-#if 0
void msn_directconn_listen(MsnDirectConn *directconn);
-#endif
void msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg);
void msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce);
void msn_directconn_destroy(MsnDirectConn *directconn);
diff --git a/libpurple/protocols/msn/group.c b/libpurple/protocols/msn/group.c
index c5aecb63b2..060535c65a 100644
--- a/libpurple/protocols/msn/group.c
+++ b/libpurple/protocols/msn/group.c
@@ -25,18 +25,18 @@
#include "group.h"
MsnGroup *
-msn_group_new(MsnUserList *userlist, int id, const char *name)
+msn_group_new(MsnUserList *userlist, const char *id, const char *name)
{
MsnGroup *group;
- g_return_val_if_fail(id >= 0, NULL);
+ g_return_val_if_fail(id != NULL, NULL);
g_return_val_if_fail(name != NULL, NULL);
group = g_new0(MsnGroup, 1);
msn_userlist_add_group(userlist, group);
- group->id = id;
+ group->id = g_strdup(id);
group->name = g_strdup(name);
return group;
@@ -47,17 +47,18 @@ msn_group_destroy(MsnGroup *group)
{
g_return_if_fail(group != NULL);
+ g_free(group->id);
g_free(group->name);
g_free(group);
}
void
-msn_group_set_id(MsnGroup *group, int id)
+msn_group_set_id(MsnGroup *group, const char *id)
{
g_return_if_fail(group != NULL);
- g_return_if_fail(id >= 0);
+ g_return_if_fail(id != NULL);
- group->id = id;
+ group->id = g_strdup(id);
}
void
@@ -72,10 +73,10 @@ msn_group_set_name(MsnGroup *group, const char *name)
group->name = g_strdup(name);
}
-int
+char*
msn_group_get_id(const MsnGroup *group)
{
- g_return_val_if_fail(group != NULL, -1);
+ g_return_val_if_fail(group != NULL, NULL);
return group->id;
}
diff --git a/libpurple/protocols/msn/group.h b/libpurple/protocols/msn/group.h
index c04f9d5d14..03cdaeccf9 100644
--- a/libpurple/protocols/msn/group.h
+++ b/libpurple/protocols/msn/group.h
@@ -30,17 +30,31 @@ typedef struct _MsnGroup MsnGroup;
#include "session.h"
#include "user.h"
-
+#include "soap.h"
#include "userlist.h"
+#define MSN_ADD_GROUPS "<GroupInfo><name>test111</name><groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType><fMessenger>false</fMessenger><annotations><Annotation><Name>MSN.IM.Display</Name><Value>1</Value></Annotation></annotations></GroupInfo>"
+
+#define MSN_ADD_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>GroupSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupAddOptions><fRenameOnMsgrConflict>false</fRenameOnMsgrConflict></groupAddOptions><groupInfo>%s</groupInfo></ABGroupAdd></soap:Body></soap:Envelope>"
+
+#define MSN_GROUP_IDS "<guid>9e57e654-59f0-44d1-aedc-0a7500b7e51f</guid>"
+#define MSN_DELETE_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupFilter><groupIds>%s</groupIds></groupFilter></ABGroupDelete></soap:Body></soap:Envelope>"
+
+#define MSN_INDIVIDUALS_GROUP_ID "1983"
+#define MSN_INDIVIDUALS_GROUP_NAME "Other Contacts"
+
+#define MSN_NON_IM_GROUP_ID "email"
+#define MSN_NON_IM_GROUP_NAME "Non-IM Contacts"
+
/**
* A group.
*/
struct _MsnGroup
{
MsnSession *session; /**< The MSN session. */
+ MsnSoapConn *soapconn;
- int id; /**< The group ID. */
+ char *id; /**< The group ID. */
char *name; /**< The name of the group. */
};
@@ -58,7 +72,7 @@ struct _MsnGroup
*
* @return A new group structure.
*/
-MsnGroup *msn_group_new(MsnUserList *userlist, int id, const char *name);
+MsnGroup *msn_group_new(MsnUserList *userlist, const char *id, const char *name);
/**
* Destroys a group structure.
@@ -73,7 +87,7 @@ void msn_group_destroy(MsnGroup *group);
* @param group The group.
* @param id The ID.
*/
-void msn_group_set_id(MsnGroup *group, int id);
+void msn_group_set_id(MsnGroup *group, const char *id);
/**
* Sets the name for a group.
@@ -90,7 +104,7 @@ void msn_group_set_name(MsnGroup *group, const char *name);
*
* @return The ID.
*/
-int msn_group_get_id(const MsnGroup *group);
+char* msn_group_get_id(const MsnGroup *group);
/**
* Returns the name for a group.
diff --git a/libpurple/protocols/msn/history.c b/libpurple/protocols/msn/history.c
index 27e35d85f8..3c45a6d10e 100644
--- a/libpurple/protocols/msn/history.c
+++ b/libpurple/protocols/msn/history.c
@@ -84,3 +84,4 @@ msn_history_add(MsnHistory *history, MsnTransaction *trans)
msn_transaction_destroy(trans);
}
}
+
diff --git a/libpurple/protocols/msn/msg.c b/libpurple/protocols/msn/msg.c
index 72d031f968..07aa47a874 100644
--- a/libpurple/protocols/msn/msg.c
+++ b/libpurple/protocols/msn/msg.c
@@ -126,7 +126,7 @@ msn_message_new_plain(const char *message)
msn_message_set_charset(msg, "UTF-8");
msn_message_set_flag(msg, 'A');
msn_message_set_attr(msg, "X-MMS-IM-Format",
- "FN=MS%20Sans%20Serif; EF=; CO=0; PF=0");
+ "FN=MS%20Sans%20Serif; EF=; CO=0; CS=86;PF=0");
message_cr = purple_str_add_cr(message);
msn_message_set_bin_data(msg, message_cr, strlen(message_cr));
@@ -206,7 +206,8 @@ msn_message_parse_slp_body(MsnMessage *msg, const char *body, size_t len)
void
msn_message_parse_payload(MsnMessage *msg,
- const char *payload, size_t payload_len)
+ const char *payload, size_t payload_len,
+ const char *line_dem,const char *body_dem)
{
char *tmp_base, *tmp;
const char *content_type;
@@ -214,12 +215,11 @@ msn_message_parse_payload(MsnMessage *msg,
char **elems, **cur, **tokens;
g_return_if_fail(payload != NULL);
-
tmp_base = tmp = g_malloc0(payload_len + 1);
memcpy(tmp_base, payload, payload_len);
/* Parse the attributes. */
- end = strstr(tmp, "\r\n\r\n");
+ end = strstr(tmp, body_dem);
/* TODO? some clients use \r delimiters instead of \r\n, the official client
* doesn't send such messages, but does handle receiving them. We'll just
* avoid crashing for now */
@@ -229,7 +229,7 @@ msn_message_parse_payload(MsnMessage *msg,
}
*end = '\0';
- elems = g_strsplit(tmp, "\r\n", 0);
+ elems = g_strsplit(tmp, line_dem, 0);
for (cur = elems; *cur != NULL; cur++)
{
@@ -240,6 +240,7 @@ msn_message_parse_payload(MsnMessage *msg,
key = tokens[0];
value = tokens[1];
+ /*if not MIME content ,then return*/
if (!strcmp(key, "MIME-Version"))
{
g_strfreev(tokens);
@@ -274,7 +275,7 @@ msn_message_parse_payload(MsnMessage *msg,
g_strfreev(elems);
/* Proceed to the end of the "\r\n\r\n" */
- tmp = end + 4;
+ tmp = end + strlen(body_dem);
/* Now we *should* be at the body. */
content_type = msn_message_get_content_type(msg);
@@ -480,6 +481,7 @@ msn_message_gen_payload(MsnMessage *msg, size_t *ret_size)
{
memcpy(n, body, body_len);
n += body_len;
+ *n = '\0';
}
}
diff --git a/libpurple/protocols/msn/msg.h b/libpurple/protocols/msn/msg.h
index 54c1c65e07..eadbc53dbc 100644
--- a/libpurple/protocols/msn/msg.h
+++ b/libpurple/protocols/msn/msg.h
@@ -34,6 +34,12 @@ typedef struct _MsnMessage MsnMessage;
typedef void (*MsnMsgCb)(MsnMessage *, void *data);
+#define MSG_BODY_DEM "\r\n\r\n"
+#define MSG_LINE_DEM "\r\n"
+
+#define MSG_OIM_BODY_DEM "\n\n"
+#define MSG_OIM_LINE_DEM "\n"
+
/*
typedef enum
{
@@ -180,7 +186,8 @@ MsnMessage *msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd);
* @param payload_len The length of the payload.
*/
void msn_message_parse_payload(MsnMessage *msg, const char *payload,
- size_t payload_len);
+ size_t payload_len,
+ const char *line_dem,const char *body_dem);
/**
* Destroys a message.
diff --git a/libpurple/protocols/msn/msn.c b/libpurple/protocols/msn/msn.c
index 911d8cabd3..bb8f7693a1 100644
--- a/libpurple/protocols/msn/msn.c
+++ b/libpurple/protocols/msn/msn.c
@@ -37,7 +37,7 @@
#include "cmds.h"
#include "core.h"
#include "prpl.h"
-#include "msn-utils.h"
+#include "msnutils.h"
#include "version.h"
#include "switchboard.h"
@@ -160,7 +160,7 @@ msn_cmd_nudge(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **
return PURPLE_CMD_RET_OK;
}
-static void
+void
msn_act_id(PurpleConnection *gc, const char *entry)
{
MsnCmdProc *cmdproc;
@@ -184,9 +184,12 @@ msn_act_id(PurpleConnection *gc, const char *entry)
return;
}
- msn_cmdproc_send(cmdproc, "REA", "%s %s",
- purple_account_get_username(account),
- alias);
+ if (*alias == '\0') {
+ alias = purple_url_encode(purple_account_get_username(account));
+ }
+
+ msn_cmdproc_send(cmdproc, "PRP", "MFN %s", alias);
+
}
static void
@@ -423,6 +426,28 @@ msn_offline_message(const PurpleBuddy *buddy) {
return user && user->mobile;
}
+void
+msn_send_privacy(PurpleConnection *gc)
+{
+ PurpleAccount *account;
+ MsnSession *session;
+ MsnCmdProc *cmdproc;
+
+ account = purple_connection_get_account(gc);
+ session = gc->proto_data;
+ cmdproc = session->notification->cmdproc;
+
+ if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
+ account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
+ {
+ msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
+ }
+ else
+ {
+ msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
+ }
+}
+
static void
initiate_chat_cb(PurpleBlistNode *node, gpointer data)
{
@@ -469,6 +494,7 @@ msn_new_xfer(PurpleConnection *gc, const char *who)
session = gc->proto_data;
xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
+
if (xfer)
{
slplink = msn_session_get_slplink(session, who);
@@ -520,20 +546,27 @@ msn_list_icon(PurpleAccount *a, PurpleBuddy *b)
return "msn";
}
+/*
+ * Set the User status text
+ * Add the PSM String Using "Name - PSM String" format
+ */
static char *
msn_status_text(PurpleBuddy *buddy)
{
PurplePresence *presence;
PurpleStatus *status;
+ const char *msg, *cmedia;
presence = purple_buddy_get_presence(buddy);
status = purple_presence_get_active_status(presence);
- if (!purple_presence_is_available(presence) && !purple_presence_is_idle(presence))
- {
- return g_strdup(purple_status_get_name(status));
- }
+ msg = purple_status_get_attr_string(status, "message");
+ cmedia = purple_status_get_attr_string(status, "currentmedia");
+ if (cmedia)
+ return g_markup_escape_text(cmedia, -1);
+ else if (msg)
+ return g_markup_escape_text(msg, -1);
return NULL;
}
@@ -549,8 +582,41 @@ msn_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean f
if (purple_presence_is_online(presence))
{
- purple_notify_user_info_add_pair(user_info, _("Status"),
- (purple_presence_is_idle(presence) ? _("Idle") : purple_status_get_name(status)));
+ const char *psm, *currentmedia, *name;
+ char *tmp;
+
+ psm = purple_status_get_attr_string(status, "message");
+ currentmedia = purple_status_get_attr_string(status, "currentmedia");
+
+ if (!purple_presence_is_available(presence)) {
+ name = purple_status_get_name(status);
+ } else {
+ name = NULL;
+ }
+
+ if (name != NULL && *name) {
+ char *tmp2 = g_markup_escape_text(name, -1);
+
+ if (psm != NULL && *psm) {
+ tmp = g_markup_escape_text(psm, -1);
+ purple_notify_user_info_add_pair(user_info, tmp2, tmp);
+ g_free(tmp);
+ } else {
+ purple_notify_user_info_add_pair(user_info, _("Status"), tmp2);
+ }
+
+ g_free(tmp2);
+ } else {
+ tmp = g_markup_escape_text(psm, -1);
+ purple_notify_user_info_add_pair(user_info, _("Status"), tmp);
+ g_free(tmp);
+ }
+
+ if (currentmedia) {
+ tmp = g_markup_escape_text(currentmedia, -1);
+ purple_notify_user_info_add_pair(user_info, _("Current media"), tmp);
+ g_free(tmp);
+ }
}
if (full && user)
@@ -575,29 +641,48 @@ msn_status_types(PurpleAccount *account)
{
PurpleStatusType *status;
GList *types = NULL;
-
+#if 0
status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE,
NULL, NULL, FALSE, TRUE, FALSE);
+#endif
+ status = purple_status_type_new_with_attrs(
+ PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE,
+ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+ NULL);
types = g_list_append(types, status);
- status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
- NULL, NULL, FALSE, TRUE, FALSE);
+ status = purple_status_type_new_with_attrs(
+ PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
+ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+ NULL);
types = g_list_append(types, status);
- status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
- "brb", _("Be Right Back"), FALSE, TRUE, FALSE);
+ status = purple_status_type_new_with_attrs(
+ PURPLE_STATUS_AWAY, "brb", _("Be Right Back"), TRUE, TRUE, FALSE,
+ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+ NULL);
types = g_list_append(types, status);
- status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
- "busy", _("Busy"), FALSE, TRUE, FALSE);
+ status = purple_status_type_new_with_attrs(
+ PURPLE_STATUS_UNAVAILABLE, "busy", _("Busy"), TRUE, TRUE, FALSE,
+ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+ NULL);
types = g_list_append(types, status);
-
- status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
- "phone", _("On the Phone"), FALSE, TRUE, FALSE);
+ status = purple_status_type_new_with_attrs(
+ PURPLE_STATUS_UNAVAILABLE, "phone", _("On the Phone"), TRUE, TRUE, FALSE,
+ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+ NULL);
types = g_list_append(types, status);
-
- status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
- "lunch", _("Out to Lunch"), FALSE, TRUE, FALSE);
+ status = purple_status_type_new_with_attrs(
+ PURPLE_STATUS_AWAY, "lunch", _("Out to Lunch"), TRUE, TRUE, FALSE,
+ "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
+ "currentmedia", _("Current media"), purple_value_new(PURPLE_TYPE_STRING),
+ NULL);
types = g_list_append(types, status);
status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
@@ -607,11 +692,11 @@ msn_status_types(PurpleAccount *account)
status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
NULL, NULL, FALSE, TRUE, FALSE);
types = g_list_append(types, status);
-
+
status = purple_status_type_new_full(PURPLE_STATUS_MOBILE,
"mobile", NULL, FALSE, FALSE, TRUE);
types = g_list_append(types, status);
-
+
return types;
}
@@ -794,6 +879,7 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
char *msgformat;
char *msgtext;
+ purple_debug_info("MSNP14","send IM {%s} to %s\n",message,who);
account = purple_connection_get_account(gc);
if (buddy) {
@@ -807,63 +893,92 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
}
msn_import_html(message, &msgformat, &msgtext);
+ if(msn_user_is_online(account, who)||
+ msn_user_is_yahoo(account, who)){
+ /*User online,then send Online Instant Message*/
- if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564)
- {
- g_free(msgformat);
- g_free(msgtext);
+ if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564)
+ {
+ g_free(msgformat);
+ g_free(msgtext);
- return -E2BIG;
- }
+ return -E2BIG;
+ }
- msg = msn_message_new_plain(msgtext);
- msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
+ msg = msn_message_new_plain(msgtext);
+ msg->remote_user = g_strdup(who);
+ msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
- g_free(msgformat);
- g_free(msgtext);
+ g_free(msgformat);
+ g_free(msgtext);
- if (g_ascii_strcasecmp(who, purple_account_get_username(account)))
- {
+ purple_debug_info("MSNP14","prepare to send online Message\n");
+ if (g_ascii_strcasecmp(who, purple_account_get_username(account)))
+ {
+ MsnSession *session;
+ MsnSwitchBoard *swboard;
+
+ session = gc->proto_data;
+ if(msn_user_is_yahoo(account,who)){
+ /*we send the online and offline Message to Yahoo User via UBM*/
+ purple_debug_info("MSNP14","send to Yahoo User\n");
+ uum_send_msg(session,msg);
+ }else{
+ purple_debug_info("MSNP14","send via switchboard\n");
+ swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM);
+ msn_switchboard_send_msg(swboard, msg, TRUE);
+ }
+ }
+ else
+ {
+ char *body_str, *body_enc, *pre, *post;
+ const char *format;
+ MsnIMData *imdata = g_new0(MsnIMData, 1);
+ /*
+ * In MSN, you can't send messages to yourself, so
+ * we'll fake like we received it ;)
+ */
+ body_str = msn_message_to_string(msg);
+ body_enc = g_markup_escape_text(body_str, -1);
+ g_free(body_str);
+
+ format = msn_message_get_attr(msg, "X-MMS-IM-Format");
+ msn_parse_format(format, &pre, &post);
+ body_str = g_strdup_printf("%s%s%s", pre ? pre : "",
+ body_enc ? body_enc : "", post ? post : "");
+ g_free(body_enc);
+ g_free(pre);
+ g_free(post);
+
+ serv_got_typing_stopped(gc, who);
+ imdata->gc = gc;
+ imdata->who = who;
+ imdata->msg = body_str;
+ imdata->flags = flags;
+ imdata->when = time(NULL);
+ g_idle_add(msn_send_me_im, imdata);
+ }
+
+ msn_message_destroy(msg);
+ }else {
+ /*send Offline Instant Message,only to MSN Passport User*/
MsnSession *session;
- MsnSwitchBoard *swboard;
+ MsnOim *oim;
+ char *friendname;
+ purple_debug_info("MSNP14","prepare to send offline Message\n");
session = gc->proto_data;
- swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM);
+ /* XXX/khc: hack */
+ if (!session->oim)
+ session->oim = msn_oim_new(session);
- msn_switchboard_send_msg(swboard, msg, TRUE);
- }
- else
- {
- char *body_str, *body_enc, *pre, *post;
- const char *format;
- MsnIMData *imdata = g_new0(MsnIMData, 1);
- /*
- * In MSN, you can't send messages to yourself, so
- * we'll fake like we received it ;)
- */
- body_str = msn_message_to_string(msg);
- body_enc = g_markup_escape_text(body_str, -1);
- g_free(body_str);
-
- format = msn_message_get_attr(msg, "X-MMS-IM-Format");
- msn_parse_format(format, &pre, &post);
- body_str = g_strdup_printf("%s%s%s", pre ? pre : "",
- body_enc ? body_enc : "", post ? post : "");
- g_free(body_enc);
- g_free(pre);
- g_free(post);
-
- serv_got_typing_stopped(gc, who);
- imdata->gc = gc;
- imdata->who = who;
- imdata->msg = body_str;
- imdata->flags = flags;
- imdata->when = time(NULL);
- g_idle_add(msn_send_me_im, imdata);
+ oim = session->oim;
+ friendname = msn_encode_mime(account->username);
+ msn_oim_prep_send_msg_info(oim, purple_account_get_username(account),
+ friendname, who, message);
+ msn_oim_send_msg(oim);
}
- msn_message_destroy(msg);
-
return 1;
}
@@ -1001,6 +1116,7 @@ msn_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
userlist = session->userlist;
who = msn_normalize(gc->account, buddy->name);
+ purple_debug_info("MSN","Add user:%s to group:%s\n", who, group->name);
if (!session->logged_in)
{
#if 0
@@ -1034,8 +1150,7 @@ msn_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
/* XXX - Would group ever be NULL here? I don't think so...
* shx: Yes it should; MSN handles non-grouped buddies, and this is only
* internal. */
- msn_userlist_add_buddy(userlist, who, MSN_LIST_FL,
- group ? group->name : NULL);
+ msn_userlist_add_buddy(userlist, who, group ? group->name : NULL);
}
static void
@@ -1051,7 +1166,7 @@ msn_rem_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
return;
/* XXX - Does buddy->name need to be msn_normalize'd here? --KingAnt */
- msn_userlist_rem_buddy(userlist, buddy->name, MSN_LIST_FL, group->name);
+ msn_userlist_rem_buddy(userlist, buddy->name);
}
static void
@@ -1068,10 +1183,18 @@ msn_add_permit(PurpleConnection *gc, const char *who)
if (!session->logged_in)
return;
- if (user != NULL && user->list_op & MSN_LIST_BL_OP)
- msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
+ if (user != NULL && user->list_op & MSN_LIST_BL_OP) {
+ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
+
+ /* delete contact from Block list and add it to Allow in the callback */
+ msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL);
+ } else {
+ /* just add the contact to Allow list */
+ msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_AL);
+ }
+
- msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
+ msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
}
static void
@@ -1088,10 +1211,17 @@ msn_add_deny(PurpleConnection *gc, const char *who)
if (!session->logged_in)
return;
- if (user != NULL && user->list_op & MSN_LIST_AL_OP)
- msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
+ if (user != NULL && user->list_op & MSN_LIST_AL_OP) {
+ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
+
+ /* delete contact from Allow list and add it to Block in the callback */
+ msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL);
+ } else {
+ /* just add the contact to Block list */
+ msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_BL);
+ }
- msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
+ msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
}
static void
@@ -1109,10 +1239,12 @@ msn_rem_permit(PurpleConnection *gc, const char *who)
user = msn_userlist_find_user(userlist, who);
- msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL);
+ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL);
+
+ msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL);
if (user != NULL && user->list_op & MSN_LIST_RL_OP)
- msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL);
+ msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL);
}
static void
@@ -1130,32 +1262,18 @@ msn_rem_deny(PurpleConnection *gc, const char *who)
user = msn_userlist_find_user(userlist, who);
- msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL);
+ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
+
+ msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL);
if (user != NULL && user->list_op & MSN_LIST_RL_OP)
- msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL);
+ msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL);
}
static void
msn_set_permit_deny(PurpleConnection *gc)
{
- PurpleAccount *account;
- MsnSession *session;
- MsnCmdProc *cmdproc;
-
- account = purple_connection_get_account(gc);
- session = gc->proto_data;
- cmdproc = session->notification->cmdproc;
-
- if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
- account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
- {
- msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
- }
- else
- {
- msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
- }
+ msn_send_privacy(gc);
}
static void
@@ -1292,24 +1410,20 @@ msn_rename_group(PurpleConnection *gc, const char *old_name,
PurpleGroup *group, GList *moved_buddies)
{
MsnSession *session;
- MsnCmdProc *cmdproc;
- int old_gid;
- const char *enc_new_group_name;
session = gc->proto_data;
- cmdproc = session->notification->cmdproc;
- enc_new_group_name = purple_url_encode(group->name);
-
- old_gid = msn_userlist_find_group_id(session->userlist, old_name);
-
- if (old_gid >= 0)
+
+ g_return_if_fail(session != NULL);
+ g_return_if_fail(session->userlist != NULL);
+
+ if (msn_userlist_find_group_with_name(session->userlist, old_name) != NULL)
{
- msn_cmdproc_send(cmdproc, "REG", "%d %s 0", old_gid,
- enc_new_group_name);
+ msn_contact_rename_group(session, old_name, group->name);
}
else
{
- msn_cmdproc_send(cmdproc, "ADG", "%s 0", enc_new_group_name);
+ /* not found */
+ msn_add_group(session, NULL, group->name);
}
}
@@ -1369,15 +1483,20 @@ msn_remove_group(PurpleConnection *gc, PurpleGroup *group)
{
MsnSession *session;
MsnCmdProc *cmdproc;
- int group_id;
session = gc->proto_data;
cmdproc = session->notification->cmdproc;
- if ((group_id = msn_userlist_find_group_id(session->userlist, group->name)) >= 0)
+ purple_debug_info("MSN", "Remove group %s\n", group->name);
+ /*we can't delete the default group*/
+ if(!strcmp(group->name, MSN_INDIVIDUALS_GROUP_NAME)||
+ !strcmp(group->name, MSN_NON_IM_GROUP_NAME))
{
- msn_cmdproc_send(cmdproc, "RMG", "%d", group_id);
+ purple_debug_info("MSN", "This group can't be removed, returning.\n");
+ return ;
}
+
+ msn_del_group(session, group->name);
}
/**
@@ -1427,12 +1546,11 @@ msn_get_photo_url(const char *url_text)
{
char *p, *q;
- if ((p = strstr(url_text, " contactparams:photopreauthurl=\"")) != NULL)
+ if ((p = strstr(url_text, PHOTO_URL)) != NULL)
{
- p += strlen(" contactparams:photopreauthurl=\"");
+ p += strlen(PHOTO_URL);
}
-
- if (p && (strncmp(p, "http://", 8) == 0) && ((q = strchr(p, '"')) != NULL))
+ if (p && (strncmp(p, "http://",strlen("http://")) == 0) && ((q = strchr(p, '"')) != NULL))
return g_strndup(p, q - p);
return NULL;
@@ -1497,7 +1615,7 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
MsnGetInfoStepTwoData *info2_data = NULL;
#endif
- purple_debug_info("msn", "In msn_got_info\n");
+ purple_debug_info("msn", "In msn_got_info,url_text:{%s}\n",url_text);
/* Make sure the connection is still valid */
if (g_list_find(purple_connections_get_all(), info_data->gc) == NULL)
@@ -1881,6 +1999,7 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
#if PHOTO_SUPPORT
/* Find the URL to the photo; must be before the marshalling [Bug 994207] */
photo_url_text = msn_get_photo_url(url_text);
+ purple_debug_info("MSNP14","photo url:{%s}\n",photo_url_text);
/* Marshall the existing state */
info2_data = g_malloc0(sizeof(MsnGetInfoStepTwoData));
@@ -2105,7 +2224,7 @@ static PurplePluginProtocolInfo prpl_info =
msn_add_deny, /* add_deny */
msn_rem_permit, /* rem_permit */
msn_rem_deny, /* rem_deny */
- msn_set_permit_deny, /* set_permit_deny */
+ msn_set_permit_deny, /* set_permit_deny */
NULL, /* join_chat */
NULL, /* reject chat invite */
NULL, /* get_chat_name */
@@ -2166,10 +2285,11 @@ static PurplePluginInfo info =
"MSN", /**< name */
VERSION, /**< version */
/** summary */
- N_("MSN Protocol Plugin"),
+ N_("Windows Live Messenger Protocol Plugin"),
/** description */
- N_("MSN Protocol Plugin"),
- "Christian Hammond <chipx86@gnupdate.org>", /**< author */
+ N_("Windows Live Messenger Protocol Plugin"),
+ "Christian Hammond <chipx86@gnupdate.org>, "
+ "MaYuan <mayuan2006@gmail.com>", /**< author */
PURPLE_WEBSITE, /**< homepage */
msn_load, /**< load */
@@ -2194,11 +2314,11 @@ init_plugin(PurplePlugin *plugin)
PurpleAccountOption *option;
option = purple_account_option_string_new(_("Server"), "server",
- MSN_SERVER);
+ WLM_SERVER);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
option);
- option = purple_account_option_int_new(_("Port"), "port", 1863);
+ option = purple_account_option_int_new(_("Port"), "port", WLM_PORT);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
option);
diff --git a/libpurple/protocols/msn/msn.h b/libpurple/protocols/msn/msn.h
index 928e1b463d..e90def5462 100644
--- a/libpurple/protocols/msn/msn.h
+++ b/libpurple/protocols/msn/msn.h
@@ -62,12 +62,23 @@
#define USEROPT_MSNPORT 4
#define MSN_PORT 1863
+/* Windows Live Messenger Server*/
+#define WLM_SERVER "muser.messenger.hotmail.com"
+#define WLM_PORT 1863
+#define WLM_PROT_VER 13
+/*This MSNP14 Support chat with Yahoo Messenger*/
+#define WLM_YAHOO_PROT_VER 14
+
+#define WLM_MAX_PROTOCOL 14
+#define WLM_MIN_PROTOCOL 13
+
#define MSN_TYPING_RECV_TIMEOUT 6
#define MSN_TYPING_SEND_TIMEOUT 4
-#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"
+#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"w3
#define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login="
#define PROFILE_URL "http://spaces.live.com/profile.aspx?mem="
+#define PHOTO_URL " contactparams:photopreauthurl=\""
#define USEROPT_HOTMAIL 0
@@ -89,9 +100,11 @@ typedef enum
MSN_LIST_FL_OP = 0x01,
MSN_LIST_AL_OP = 0x02,
MSN_LIST_BL_OP = 0x04,
- MSN_LIST_RL_OP = 0x08
+ MSN_LIST_RL_OP = 0x08,
+ MSN_LIST_PL_OP = 0x10
} MsnListOp;
+#define MSN_LIST_OP_MASK 0x07
typedef enum
{
@@ -132,4 +145,7 @@ typedef enum
(MSN_CLIENT_ID_RESERVED_2 << 8) | \
(MSN_CLIENT_ID_CAPABILITIES))
+void msn_act_id(PurpleConnection *gc, const char *entry);
+void msn_send_privacy(PurpleConnection *gc);
+
#endif /* _MSN_H_ */
diff --git a/libpurple/protocols/msn/msn-utils.c b/libpurple/protocols/msn/msnutils.c
index 407166023a..0713e3ed60 100644
--- a/libpurple/protocols/msn/msn-utils.c
+++ b/libpurple/protocols/msn/msnutils.c
@@ -1,5 +1,5 @@
/**
- * @file msn-utils.c Utility functions
+ * @file msnutils.c Utility functions
*
* purple
*
@@ -22,7 +22,28 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "msn.h"
-#include "msn-utils.h"
+#include "msnutils.h"
+#include "time.h"
+//#include <openssl/md5.h>
+
+char *rand_guid(void);
+
+/**************************************************************************
+ * Util
+ **************************************************************************/
+char *
+rand_guid()
+{
+ return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
+ rand() % 0xAAFF + 0x1111,
+ rand() % 0xAAFF + 0x1111,
+ rand() % 0xAAFF + 0x1111,
+ rand() % 0xAAFF + 0x1111,
+ rand() % 0xAAFF + 0x1111,
+ rand() % 0xAAFF + 0x1111,
+ rand() % 0xAAFF + 0x1111,
+ rand() % 0xAAFF + 0x1111);
+}
void
msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
@@ -139,6 +160,18 @@ msn_parse_format(const char *mime, char **pre_ret, char **post_ret)
g_free(cur);
}
+/*encode the str to RFC2047 style
+ * Currently only support the UTF-8 and base64 encode
+ */
+char *
+msn_encode_mime(const char *str)
+{
+ char *base64;
+
+ base64 = purple_base64_encode((guchar *)str, strlen(str));
+ return g_strdup_printf("=?utf-8?B?%s?=", base64);
+}
+
/*
* We need this because we're only supposed to encode spaces in the font
* names. purple_url_encode() isn't acceptable.
@@ -427,14 +460,132 @@ msn_parse_socket(const char *str, char **ret_host, int *ret_port)
host = g_strdup(str);
- if ((c = strchr(host, ':')) != NULL)
- {
+ if ((c = strchr(host, ':')) != NULL){
*c = '\0';
port = atoi(c + 1);
- }
- else
+ }else{
port = 1863;
+ }
*ret_host = host;
*ret_port = port;
}
+/***************************************************************************
+ * MSN Time Related Funciton
+ ***************************************************************************/
+#if 0
+int
+msn_convert_iso8601(const char *timestr,struct tm tm_time)
+{
+ char temp[64];
+ struct tm ctime;
+ time_t ts;
+
+ purple_debug_info("MSNP14","convert string is{%s}\n",timestr);
+ tzset();
+ /*copy string first*/
+ memset(temp, 0, sizeof(temp));
+ strncpy(temp, timestr, strlen(timestr));
+
+ /*convert via strptime()*/
+ memset(&ctime, 0, sizeof(struct tm));
+ strptime(temp, "%d %b %Y %T %Z", &ctime);
+ ts = mktime(&ctime) - timezone;
+ localtime_r(&ts, tm_time);
+}
+#endif
+
+/***************************************************************************
+ * MSN Challenge Computing Function
+ ***************************************************************************/
+
+/*
+ * Handle MSN Chanllege computation
+ *This algorithm reference with http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
+ */
+#define BUFSIZE 256
+void
+msn_handle_chl(char *input, char *output)
+{
+ PurpleCipher *cipher;
+ PurpleCipherContext *context;
+ char *productKey = MSNP13_WLM_PRODUCT_KEY,
+ *productID = MSNP13_WLM_PRODUCT_ID,
+ *hexChars = "0123456789abcdef",
+ buf[BUFSIZE];
+ unsigned char md5Hash[16], *newHash;
+ unsigned int *md5Parts, *chlStringParts, newHashParts[5];
+
+ long long nHigh=0, nLow=0;
+
+ int i;
+
+ /* Create the MD5 hash by using Purple MD5 algorithm*/
+ cipher = purple_ciphers_find_cipher("md5");
+ context = purple_cipher_context_new(cipher, NULL);
+
+ purple_cipher_context_append(context, (const guchar *)input,
+ strlen(input));
+ purple_cipher_context_append(context, (const guchar *)productKey,
+ strlen(productKey));
+ purple_cipher_context_digest(context, sizeof(md5Hash), md5Hash, NULL);
+ purple_cipher_context_destroy(context);
+
+ /* Split it into four integers */
+ md5Parts = (unsigned int *)md5Hash;
+ for(i=0; i<4; i++){
+ /* adjust endianess */
+ md5Parts[i] = GUINT_TO_LE(md5Parts[i]);
+
+ /* & each integer with 0x7FFFFFFF */
+ /* and save one unmodified array for later */
+ newHashParts[i] = md5Parts[i];
+ md5Parts[i] &= 0x7FFFFFFF;
+ }
+
+ /* make a new string and pad with '0' */
+ snprintf(buf, BUFSIZE-5, "%s%s", input, productID);
+ i = strlen(buf);
+ memset(&buf[i], '0', 8 - (i % 8));
+ buf[i + (8 - (i % 8))]='\0';
+
+ /* split into integers */
+ chlStringParts = (unsigned int *)buf;
+
+ /* this is magic */
+ for (i=0; i<(strlen(buf)/4)-1; i+=2){
+ long long temp;
+
+ chlStringParts[i] = GUINT_TO_LE(chlStringParts[i]);
+ chlStringParts[i+1] = GUINT_TO_LE(chlStringParts[i+1]);
+
+ temp=(md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF;
+ nHigh=(md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF;
+ nLow=nLow + nHigh + temp;
+ }
+ nHigh=(nHigh+md5Parts[1]) % 0x7FFFFFFF;
+ nLow=(nLow+md5Parts[3]) % 0x7FFFFFFF;
+
+ newHashParts[0]^=nHigh;
+ newHashParts[1]^=nLow;
+ newHashParts[2]^=nHigh;
+ newHashParts[3]^=nLow;
+
+ /* adjust endianness */
+ for(i=0; i<4; i++)
+ newHashParts[i] = GUINT_TO_LE(newHashParts[i]);
+
+ /* make a string of the parts */
+ newHash = (unsigned char *)newHashParts;
+
+ /* convert to hexadecimal */
+ for (i=0; i<16; i++)
+ {
+ output[i*2]=hexChars[(newHash[i]>>4)&0xF];
+ output[(i*2)+1]=hexChars[newHash[i]&0xF];
+ }
+
+ output[32]='\0';
+
+// purple_debug_info("MSNP14","chl output{%s}\n",output);
+}
diff --git a/libpurple/protocols/msn/msn-utils.h b/libpurple/protocols/msn/msnutils.h
index 30cd897aed..2b8a11035e 100644
--- a/libpurple/protocols/msn/msn-utils.h
+++ b/libpurple/protocols/msn/msnutils.h
@@ -1,5 +1,5 @@
/**
- * @file msn-utils.h Utility functions
+ * @file msnutils.h Utility functions
*
* purple
*
@@ -24,6 +24,14 @@
#ifndef _MSN_UTILS_H_
#define _MSN_UTILS_H_
+/*encode the str to RFC2047 style*/
+char * msn_encode_mime(const char *str);
+
+/**
+ * Generate the Random GUID
+ */
+char * rand_guid(void);
+
/**
* Parses the MSN message formatting into a format compatible with Purple.
*
@@ -47,5 +55,6 @@ void msn_parse_format(const char *mime, char **pre_ret, char **post_ret);
void msn_import_html(const char *html, char **attributes, char **message);
void msn_parse_socket(const char *str, char **ret_host, int *ret_port);
+void msn_handle_chl(char *input, char *output);
#endif /* _MSN_UTILS_H_ */
diff --git a/libpurple/protocols/msn/nexus.c b/libpurple/protocols/msn/nexus.c
index 202372222f..121614241c 100644
--- a/libpurple/protocols/msn/nexus.c
+++ b/libpurple/protocols/msn/nexus.c
@@ -22,9 +22,15 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "msn.h"
+#include "soap.h"
#include "nexus.h"
#include "notification.h"
+#undef NEXUS_LOGIN_TWN
+
+/*Local Function Prototype*/
+static void nexus_login_connect_cb(gpointer data, PurpleSslConnection *gsc,PurpleInputCondition cond);
+
/**************************************************************************
* Main
**************************************************************************/
@@ -36,6 +42,9 @@ msn_nexus_new(MsnSession *session)
nexus = g_new0(MsnNexus, 1);
nexus->session = session;
+ /*we must use SSL connection to do Windows Live ID authentication*/
+ nexus->soapconn = msn_soap_new(session,nexus,1);
+
nexus->challenge_data = g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, g_free);
@@ -45,24 +54,14 @@ msn_nexus_new(MsnSession *session)
void
msn_nexus_destroy(MsnNexus *nexus)
{
- if (nexus->gsc)
- purple_ssl_close(nexus->gsc);
-
- g_free(nexus->login_host);
-
- g_free(nexus->login_path);
-
if (nexus->challenge_data != NULL)
g_hash_table_destroy(nexus->challenge_data);
- if (nexus->input_handler > 0)
- purple_input_remove(nexus->input_handler);
- g_free(nexus->write_buf);
- g_free(nexus->read_buf);
-
+ msn_soap_destroy(nexus->soapconn);
g_free(nexus);
}
+#if 0 /* khc */
/**************************************************************************
* Util
**************************************************************************/
@@ -121,285 +120,242 @@ nexus_write_cb(gpointer data, gint source, PurpleInputCondition cond)
nexus->written_cb(nexus, source, 0);
}
+#endif
/**************************************************************************
* Login
**************************************************************************/
-
-static void
-login_connect_cb(gpointer data, PurpleSslConnection *gsc,
- PurpleInputCondition cond);
-
static void
-login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+nexus_login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
{
- MsnNexus *nexus;
+ MsnSoapConn * soapconn = data;
MsnSession *session;
- nexus = data;
- g_return_if_fail(nexus != NULL);
-
- nexus->gsc = NULL;
-
- session = nexus->session;
+ session = soapconn->session;
g_return_if_fail(session != NULL);
- msn_session_set_error(session, MSN_ERROR_AUTH, _("Unable to connect"));
+ soapconn->gsc = NULL;
+
+ msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication:Unable to connect"));
/* the above line will result in nexus being destroyed, so we don't want
* to destroy it here, or we'd crash */
}
+/*process the SOAP reply, get the Authentication Info*/
static void
-nexus_login_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+nexus_login_read_cb(gpointer data, gint source, PurpleInputCondition cond)
{
- MsnNexus *nexus = data;
+ MsnSoapConn * soapconn = data;
+ MsnNexus *nexus;
MsnSession *session;
- int len;
+ char *base, *c;
+ char *msn_twn_t,*msn_twn_p;
+ char *login_params;
+ char **elems, **cur, **tokens;
+ char * cert_str;
+
+ nexus = soapconn->parent;
+ g_return_if_fail(nexus != NULL);
session = nexus->session;
g_return_if_fail(session != NULL);
- if (nexus->input_handler == 0)
- /* TODO: Use purple_ssl_input_add()? */
- nexus->input_handler = purple_input_add(nexus->gsc->fd,
- PURPLE_INPUT_READ, nexus_login_written_cb, nexus);
+ /*reply OK, we should process the SOAP body*/
+ purple_debug_info("MSN Nexus","TWN Server Windows Live ID Reply OK!\n");
+
+ //TODO: we should parse it using XML
+#ifdef NEXUS_LOGIN_TWN
+ base = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_START_TOKEN);
+ base += strlen(TWN_START_TOKEN);
+ c = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_END_TOKEN);
+#else
+ base = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_LIVE_START_TOKEN);
+ base += strlen(TWN_LIVE_START_TOKEN);
+ c = g_strstr_len(soapconn->read_buf, soapconn->read_len, TWN_LIVE_END_TOKEN);
+#endif
+ login_params = g_strndup(base, c - base);
+ // purple_debug_info("msn", "TWN Cert: {%s}\n", login_params);
- len = msn_ssl_read(nexus);
+ /* Parse the challenge data. */
+ elems = g_strsplit(login_params, "&amp;", 0);
- if (len < 0 && errno == EAGAIN)
- return;
- else if (len < 0) {
- purple_input_remove(nexus->input_handler);
- nexus->input_handler = 0;
- g_free(nexus->read_buf);
- nexus->read_buf = NULL;
- nexus->read_len = 0;
- /* TODO: error handling */
- return;
+ for (cur = elems; *cur != NULL; cur++){
+ tokens = g_strsplit(*cur, "=", 2);
+ g_hash_table_insert(nexus->challenge_data, tokens[0], tokens[1]);
+ /* Don't free each of the tokens, only the array. */
+ g_free(tokens);
}
- if (g_strstr_len(nexus->read_buf, nexus->read_len,
- "\r\n\r\n") == NULL)
- return;
-
- purple_input_remove(nexus->input_handler);
- nexus->input_handler = 0;
-
- purple_ssl_close(nexus->gsc);
- nexus->gsc = NULL;
-
- purple_debug_misc("msn", "ssl buffer: {%s}", nexus->read_buf);
-
- if (strstr(nexus->read_buf, "HTTP/1.1 302") != NULL)
- {
- /* Redirect. */
- char *location, *c;
-
- location = strstr(nexus->read_buf, "Location: ");
- if (location == NULL)
- {
- g_free(nexus->read_buf);
- nexus->read_buf = NULL;
- nexus->read_len = 0;
-
- return;
- }
- location = strchr(location, ' ') + 1;
-
- if ((c = strchr(location, '\r')) != NULL)
- *c = '\0';
-
- /* Skip the http:// */
- if ((c = strchr(location, '/')) != NULL)
- location = c + 2;
-
- if ((c = strchr(location, '/')) != NULL)
- {
- g_free(nexus->login_path);
- nexus->login_path = g_strdup(c);
-
- *c = '\0';
- }
+ g_strfreev(elems);
- g_free(nexus->login_host);
- nexus->login_host = g_strdup(location);
+ msn_twn_t = (char *)g_hash_table_lookup(nexus->challenge_data, "t");
+ msn_twn_p = (char *)g_hash_table_lookup(nexus->challenge_data, "p");
- nexus->gsc = purple_ssl_connect(session->account,
- nexus->login_host, PURPLE_SSL_DEFAULT_PORT,
- login_connect_cb, login_error_cb, nexus);
+ /*setup the t and p parameter for session*/
+ if (session->passport_info.t != NULL){
+ g_free(session->passport_info.t);
}
- else if (strstr(nexus->read_buf, "HTTP/1.1 401 Unauthorized") != NULL)
- {
- const char *error;
+ session->passport_info.t = g_strdup(msn_twn_t);
- if ((error = strstr(nexus->read_buf, "WWW-Authenticate")) != NULL)
- {
- if ((error = strstr(error, "cbtxt=")) != NULL)
- {
- const char *c;
- char *temp;
-
- error += strlen("cbtxt=");
-
- if ((c = strchr(error, '\n')) == NULL)
- c = error + strlen(error);
-
- temp = g_strndup(error, c - error);
- error = purple_url_decode(temp);
- g_free(temp);
- if ((temp = strstr(error, " Do one of the following or try again:")) != NULL)
- *temp = '\0';
- }
- }
-
- msn_session_set_error(session, MSN_ERROR_AUTH, error);
- }
- else if (strstr(nexus->read_buf, "HTTP/1.1 503 Service Unavailable"))
- {
- msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
- }
- else if (strstr(nexus->read_buf, "HTTP/1.1 200 OK"))
- {
- char *base, *c;
- char *login_params;
-
-#if 0
- /* All your base are belong to us. */
- base = buffer;
-
- /* For great cookie! */
- while ((base = strstr(base, "Set-Cookie: ")) != NULL)
- {
- base += strlen("Set-Cookie: ");
-
- c = strchr(base, ';');
-
- session->login_cookies =
- g_list_append(session->login_cookies,
- g_strndup(base, c - base));
- }
-#endif
+ if (session->passport_info.p != NULL)
+ g_free(session->passport_info.p);
+ session->passport_info.p = g_strdup(msn_twn_p);
- base = strstr(nexus->read_buf, "Authentication-Info: ");
+ cert_str = g_strdup_printf("t=%s&p=%s",msn_twn_t,msn_twn_p);
+ msn_got_login_params(session, cert_str);
- g_return_if_fail(base != NULL);
-
- base = strstr(base, "from-PP='");
- base += strlen("from-PP='");
- c = strchr(base, '\'');
-
- login_params = g_strndup(base, c - base);
-
- msn_got_login_params(session, login_params);
-
- g_free(login_params);
-
- msn_nexus_destroy(nexus);
- session->nexus = NULL;
- return;
- }
-
- g_free(nexus->read_buf);
- nexus->read_buf = NULL;
- nexus->read_len = 0;
+ purple_debug_info("MSN Nexus","Close nexus connection!\n");
+ g_free(cert_str);
+ g_free(login_params);
+ msn_nexus_destroy(nexus);
+ session->nexus = NULL;
+ return;
}
-/* this guards against missing hash entries */
-static char *
-nexus_challenge_data_lookup(GHashTable *challenge_data, const char *key)
+static void
+nexus_login_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
- char *entry;
+ MsnSoapConn * soapconn = data;
- return (entry = (char *)g_hash_table_lookup(challenge_data, key)) ?
- entry : "(null)";
+ soapconn->read_cb = nexus_login_read_cb;
+// msn_soap_read_cb(data,source,cond);
}
+
+/*when connect, do the SOAP Style windows Live ID authentication */
void
-login_connect_cb(gpointer data, PurpleSslConnection *gsc,
+nexus_login_connect_cb(gpointer data, PurpleSslConnection *gsc,
PurpleInputCondition cond)
{
- MsnNexus *nexus;
+ MsnSoapConn *soapconn;
+ MsnNexus * nexus;
MsnSession *session;
+ char *ru,*lc,*id,*tw,*ct,*kpp,*kv,*ver,*rn,*tpf;
+ char *fs0,*fs;
char *username, *password;
char *request_str, *head, *tail;
- char *buffer = NULL;
- guint32 ctint;
+#ifdef NEXUS_LOGIN_TWN
+ char *challenge_str;
+#else
+ char *rst1_str,*rst2_str,*rst3_str;
+#endif
- nexus = data;
+ purple_debug_info("MSN Nexus","Starting Windows Live ID authentication\n");
+
+ soapconn = data;
+ g_return_if_fail(soapconn != NULL);
+
+ nexus = soapconn->parent;
g_return_if_fail(nexus != NULL);
- session = nexus->session;
+ session = soapconn->session;
g_return_if_fail(session != NULL);
msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE);
- username =
- g_strdup(purple_url_encode(purple_account_get_username(session->account)));
+ /*prepare the Windows Live ID authentication token*/
+ username = g_strdup(purple_account_get_username(session->account));
+ password = g_strdup(purple_connection_get_password(session->account->gc));
+
+ lc = (char *)g_hash_table_lookup(nexus->challenge_data, "lc");
+ id = (char *)g_hash_table_lookup(nexus->challenge_data, "id");
+ tw = (char *)g_hash_table_lookup(nexus->challenge_data, "tw");
+ fs0= (char *)g_hash_table_lookup(nexus->challenge_data, "fs");
+ ru = (char *)g_hash_table_lookup(nexus->challenge_data, "ru");
+ ct = (char *)g_hash_table_lookup(nexus->challenge_data, "ct");
+ kpp= (char *)g_hash_table_lookup(nexus->challenge_data, "kpp");
+ kv = (char *)g_hash_table_lookup(nexus->challenge_data, "kv");
+ ver= (char *)g_hash_table_lookup(nexus->challenge_data, "ver");
+ rn = (char *)g_hash_table_lookup(nexus->challenge_data, "rn");
+ tpf= (char *)g_hash_table_lookup(nexus->challenge_data, "tpf");
+
+ /*
+ * add some fail-safe code to avoid windows Purple Crash bug #1540454
+ * If any of these string is NULL, will return Authentication Fail!
+ * for when windows g_strdup_printf() implementation get NULL point,It crashed!
+ */
+ if(!(lc && id && tw && ru && ct && kpp && kv && ver && tpf)){
+ purple_debug_error("MSN Nexus","WLM Authenticate Key Error!\n");
+ msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication Failed"));
+ g_free(username);
+ g_free(password);
+ purple_ssl_close(gsc);
+ msn_nexus_destroy(nexus);
+ session->nexus = NULL;
+ return;
+ }
- password =
- g_strdup(purple_url_encode(purple_connection_get_password(session->account->gc)));
+ /*
+ * in old MSN NS server's "USR TWN S" return,didn't include fs string
+ * so we use a default "1" for fs.
+ */
+ if(fs0){
+ fs = g_strdup(fs0);
+ }else{
+ fs = g_strdup("1");
+ }
- ctint = strtoul((char *)g_hash_table_lookup(nexus->challenge_data, "ct"), NULL, 10) + 200;
+#ifdef NEXUS_LOGIN_TWN
+ challenge_str = g_strdup_printf(
+ "lc=%s&amp;id=%s&amp;tw=%s&amp;fs=%s&amp;ru=%s&amp;ct=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s&amp;tpf=%s\r\n",
+ lc,id,tw,fs,ru,ct,kpp,kv,ver,rn,tpf
+ );
+
+ /*build the SOAP windows Live ID XML body */
+ tail = g_strdup_printf(TWN_ENVELOP_TEMPLATE,username,password,challenge_str );
+ g_free(challenge_str);
+#else
+ rst1_str = g_strdup_printf(
+ "id=%s&amp;tw=%s&amp;fs=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s",
+ id,tw,fs,kpp,kv,ver,rn
+ );
+ rst2_str = g_strdup_printf(
+ "fs=%s&amp;id=%s&amp;kv=%s&amp;rn=%s&amp;tw=%s&amp;ver=%s",
+ fs,id,kv,rn,tw,ver
+ );
+ rst3_str = g_strdup_printf("id=%s",id);
+ tail = g_strdup_printf(TWN_LIVE_ENVELOP_TEMPLATE,username,password,rst1_str,rst2_str,rst3_str);
+ g_free(rst1_str);
+ g_free(rst2_str);
+ g_free(rst3_str);
+#endif
+ g_free(fs);
+ soapconn->login_path = g_strdup(TWN_POST_URL);
head = g_strdup_printf(
- "GET %s HTTP/1.1\r\n"
- "Authorization: Passport1.4 OrgVerb=GET,OrgURL=%s,sign-in=%s",
- nexus->login_path,
- (char *)g_hash_table_lookup(nexus->challenge_data, "ru"),
- username);
-
- tail = g_strdup_printf(
- "lc=%s,id=%s,tw=%s,fs=%s,ru=%s,ct=%" G_GUINT32_FORMAT ",kpp=%s,kv=%s,ver=%s,tpf=%s\r\n"
- "User-Agent: MSMSGS\r\n"
- "Host: %s\r\n"
- "Connection: Keep-Alive\r\n"
- "Cache-Control: no-cache\r\n",
- nexus_challenge_data_lookup(nexus->challenge_data, "lc"),
- nexus_challenge_data_lookup(nexus->challenge_data, "id"),
- nexus_challenge_data_lookup(nexus->challenge_data, "tw"),
- nexus_challenge_data_lookup(nexus->challenge_data, "fs"),
- nexus_challenge_data_lookup(nexus->challenge_data, "ru"),
- ctint,
- nexus_challenge_data_lookup(nexus->challenge_data, "kpp"),
- nexus_challenge_data_lookup(nexus->challenge_data, "kv"),
- nexus_challenge_data_lookup(nexus->challenge_data, "ver"),
- nexus_challenge_data_lookup(nexus->challenge_data, "tpf"),
- nexus->login_host);
-
- buffer = g_strdup_printf("%s,pwd=XXXXXXXX,%s\r\n", head, tail);
- request_str = g_strdup_printf("%s,pwd=%s,%s\r\n", head, password, tail);
-
- purple_debug_misc("msn", "Sending: {%s}\n", buffer);
-
- g_free(buffer);
+ "POST %s HTTP/1.1\r\n"
+ "Accept: text/*\r\n"
+ "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
+ "Host: %s\r\n"
+ "Content-Length: %d\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Cache-Control: no-cache\r\n\r\n",
+ soapconn->login_path,soapconn->login_host,(int)strlen(tail));
+
+ request_str = g_strdup_printf("%s%s", head,tail);
+
+#ifdef MSN_SOAP_DEBUG
+ purple_debug_misc("MSN Nexus", "TWN Sending:\n%s\n", request_str);
+#endif
g_free(head);
g_free(tail);
g_free(username);
g_free(password);
- nexus->write_buf = request_str;
- nexus->written_len = 0;
-
- nexus->read_len = 0;
-
- nexus->written_cb = nexus_login_written_cb;
-
- nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE,
- nexus_write_cb, nexus);
-
- nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
+ /*prepare to send the SOAP request*/
+ msn_soap_write(soapconn,request_str,nexus_login_written_cb);
return;
-
-
}
+#if 0 /* khc */
static void
nexus_connect_written_cb(gpointer data, gint source, PurpleInputCondition cond)
{
MsnNexus *nexus = data;
int len;
+
char *da_login;
char *base, *c;
@@ -408,6 +364,7 @@ nexus_connect_written_cb(gpointer data, gint source, PurpleInputCondition cond)
nexus->input_handler = purple_input_add(nexus->gsc->fd,
PURPLE_INPUT_READ, nexus_connect_written_cb, nexus);
+
/* Get the PassportURLs line. */
len = msn_ssl_read(nexus);
@@ -470,10 +427,13 @@ nexus_connect_written_cb(gpointer data, gint source, PurpleInputCondition cond)
}
+#endif
+
/**************************************************************************
* Connect
**************************************************************************/
+#if 0 /* khc */
static void
nexus_connect_cb(gpointer data, PurpleSslConnection *gsc,
PurpleInputCondition cond)
@@ -502,10 +462,13 @@ nexus_connect_cb(gpointer data, PurpleSslConnection *gsc,
nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE);
}
+#endif
+
void
msn_nexus_connect(MsnNexus *nexus)
{
- nexus->gsc = purple_ssl_connect(nexus->session->account,
- "nexus.passport.com", PURPLE_SSL_DEFAULT_PORT,
- nexus_connect_cb, login_error_cb, nexus);
+ /* Authenticate via Windows Live ID. */
+ purple_debug_info("MSN Nexus","msn_nexus_connect()\n");
+ msn_soap_init(nexus->soapconn,MSN_TWN_SERVER,1,nexus_login_connect_cb,nexus_login_error_cb);
+ msn_soap_connect(nexus->soapconn);
}
diff --git a/libpurple/protocols/msn/nexus.h b/libpurple/protocols/msn/nexus.h
index 3f6c23991d..ebb5e35b3c 100644
--- a/libpurple/protocols/msn/nexus.h
+++ b/libpurple/protocols/msn/nexus.h
@@ -24,25 +24,124 @@
#ifndef _MSN_NEXUS_H_
#define _MSN_NEXUS_H_
+#include "soap.h"
+
+/*#define MSN_TWN_SERVER "loginnet.passport.com"*/
+#define MSN_TWN_SERVER "login.live.com"
+
+#define TWN_START_TOKEN "<wsse:BinarySecurityToken Id=\"PPToken1\">"
+#define TWN_END_TOKEN "</wsse:BinarySecurityToken>"
+
+#define TWN_POST_URL "/RST.srf"
+#define TWN_ENVELOP_TEMPLATE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"\
+ "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\
+ "<Header>"\
+ "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\
+ "<ps:HostingApp>{3:B}</ps:HostingApp>"\
+ "<ps:BinaryVersion>4</ps:BinaryVersion>"\
+ "<ps:UIVersion>1</ps:UIVersion>"\
+ "<ps:Cookies></ps:Cookies>"\
+ "<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>"\
+ "</ps:AuthInfo>"\
+ "<wsse:Security>"\
+ "<wsse:UsernameToken Id=\"user\">"\
+ "<wsse:Username>%s</wsse:Username>"\
+ "<wsse:Password>%s</wsse:Password>"\
+ "</wsse:UsernameToken>"\
+ "</wsse:Security>"\
+ "</Header>"\
+ "<Body>"\
+ "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\
+ "<wst:RequestSecurityToken Id=\"RST0\">"\
+ "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+ "<wsp:AppliesTo>"\
+ "<wsa:EndpointReference>"\
+ "<wsa:Address>http://Passport.NET/tb</wsa:Address>"\
+ "</wsa:EndpointReference>"\
+ "</wsp:AppliesTo>"\
+ "</wst:RequestSecurityToken>"\
+ "<wst:RequestSecurityToken Id=\"RST1\">"\
+ "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+ "<wsp:AppliesTo>"\
+ "<wsa:EndpointReference>"\
+ "<wsa:Address>messenger.msn.com</wsa:Address>"\
+ "</wsa:EndpointReference>"\
+ "</wsp:AppliesTo>"\
+ "<wsse:PolicyReference URI=\"?%s\">"\
+ "</wsse:PolicyReference>"\
+ "</wst:RequestSecurityToken>"\
+ "</ps:RequestMultipleSecurityTokens>"\
+ "</Body>"\
+ "</Envelope>"
+
+#define TWN_LIVE_START_TOKEN "<wsse:BinarySecurityToken Id=\"PPToken1\">"
+#define TWN_LIVE_END_TOKEN "</wsse:BinarySecurityToken>"
+#define TWN_LIVE_ENVELOP_TEMPLATE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"\
+"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\
+ "<Header>"\
+ "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\
+ "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>"\
+ "<ps:BinaryVersion>4</ps:BinaryVersion>"\
+ "<ps:UIVersion>1</ps:UIVersion>"\
+ "<ps:Cookies></ps:Cookies>"\
+ "<ps:RequestParams>AQAAAAIAAABsYwQAAAAyMDUy</ps:RequestParams>"\
+ "</ps:AuthInfo>"\
+ "<wsse:Security>"\
+ "<wsse:UsernameToken Id=\"user\">"\
+ "<wsse:Username>%s</wsse:Username>"\
+ "<wsse:Password>%s</wsse:Password>"\
+ "</wsse:UsernameToken>"\
+ "</wsse:Security>"\
+ "</Header>"\
+ "<Body>"\
+ "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\
+ "<wst:RequestSecurityToken Id=\"RST0\">"\
+ "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+ "<wsp:AppliesTo>"\
+ "<wsa:EndpointReference>"\
+ "<wsa:Address>http://Passport.NET/tb</wsa:Address>"\
+ "</wsa:EndpointReference>"\
+ "</wsp:AppliesTo>"\
+ "</wst:RequestSecurityToken>"\
+ "<wst:RequestSecurityToken Id=\"RST1\">"\
+ "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+ "<wsp:AppliesTo>"\
+ "<wsa:EndpointReference>"\
+ "<wsa:Address>messenger.msn.com</wsa:Address>"\
+ "</wsa:EndpointReference>"\
+ "</wsp:AppliesTo>"\
+ "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
+ "</wst:RequestSecurityToken>"\
+ "<wst:RequestSecurityToken Id=\"RST2\">"\
+ "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+ "<wsp:AppliesTo>"\
+ "<wsa:EndpointReference>"\
+ "<wsa:Address>contacts.msn.com</wsa:Address>"\
+ "</wsa:EndpointReference>"\
+ "</wsp:AppliesTo>"\
+ "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
+ " </wst:RequestSecurityToken>"\
+ "<wst:RequestSecurityToken Id=\"RST3\">"\
+ "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\
+ "<wsp:AppliesTo>"\
+ "<wsa:EndpointReference>"\
+ "<wsa:Address>voice.messenger.msn.com</wsa:Address>"\
+ "</wsa:EndpointReference>"\
+ " </wsp:AppliesTo>"\
+ "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\
+ "</wst:RequestSecurityToken>"\
+ "</ps:RequestMultipleSecurityTokens>"\
+ "</Body>"\
+"</Envelope>"
+
typedef struct _MsnNexus MsnNexus;
struct _MsnNexus
{
MsnSession *session;
-
- char *login_host;
- char *login_path;
+ MsnSoapConn *soapconn;
+ char * challenge_data_str;
GHashTable *challenge_data;
- PurpleSslConnection *gsc;
-
- guint input_handler;
-
- char *write_buf;
- gsize written_len;
- PurpleInputFunction written_cb;
-
- char *read_buf;
- gsize read_len;
};
void msn_nexus_connect(MsnNexus *nexus);
diff --git a/libpurple/protocols/msn/notification.c b/libpurple/protocols/msn/notification.c
index acf6832799..859823403a 100644
--- a/libpurple/protocols/msn/notification.c
+++ b/libpurple/protocols/msn/notification.c
@@ -25,7 +25,7 @@
#include "notification.h"
#include "state.h"
#include "error.h"
-#include "msn-utils.h"
+#include "msnutils.h"
#include "page.h"
#include "userlist.h"
@@ -34,6 +34,15 @@
static MsnTable *cbs_table;
+/****************************************************************************
+ * Local Function Prototype
+ ****************************************************************************/
+
+static void msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len);
+static void
+msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport,
+ MsnListOp list_op, MsnUserType type);
+
/**************************************************************************
* Main
**************************************************************************/
@@ -102,9 +111,11 @@ connect_cb(MsnServConn *servconn)
account = session->account;
/* Allocate an array for CVR0, NULL, and all the versions */
- a = c = g_new0(char *, session->protocol_ver - 8 + 3);
+// a = c = g_new0(char *, session->protocol_ver - WLM_MIN_PROTOCOL + 3);
+ a = c = g_new0(char *, WLM_MAX_PROTOCOL - WLM_MIN_PROTOCOL + 3);
- for (i = session->protocol_ver; i >= 8; i--)
+// for (i = session->protocol_ver; i >= WLM_MIN_PROTOCOL; i--)
+ for (i = WLM_MAX_PROTOCOL; i >= WLM_MIN_PROTOCOL; i--)
*c++ = g_strdup_printf("MSNP%d", i);
*c++ = g_strdup("CVR0");
@@ -112,9 +123,13 @@ connect_cb(MsnServConn *servconn)
vers = g_strjoinv(" ", a);
if (session->login_step == MSN_LOGIN_STEP_START)
+ {
msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE);
+ }
else
+ {
msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE2);
+ }
msn_cmdproc_send(cmdproc, "VER", "%s", vers);
@@ -153,7 +168,7 @@ msn_notification_disconnect(MsnNotification *notification)
**************************************************************************/
static void
-group_error_helper(MsnSession *session, const char *msg, int group_id, int error)
+group_error_helper(MsnSession *session, const char *msg, const char *group_id, int error)
{
PurpleAccount *account;
PurpleConnection *gc;
@@ -172,9 +187,7 @@ group_error_helper(MsnSession *session, const char *msg, int group_id, int error
else
{
const char *group_name;
- group_name =
- msn_userlist_find_group_name(session->userlist,
- group_id);
+ group_name = msn_userlist_find_group_name(session->userlist,group_id);
reason = g_strdup_printf(_("%s is not a valid group."),
group_name);
}
@@ -214,7 +227,6 @@ cvr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
PurpleAccount *account;
account = cmdproc->session->account;
-
msn_cmdproc_send(cmdproc, "USR", "TWN I %s",
purple_account_get_username(account));
}
@@ -232,14 +244,17 @@ usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
if (!g_ascii_strcasecmp(cmd->params[1], "OK"))
{
- /* OK */
+ /* authenticate OK */
+ /* friendly name part no longer true in msnp11 */
+#if 0
const char *friendly = purple_url_decode(cmd->params[3]);
purple_connection_set_display_name(gc, friendly);
-
+#endif
msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN);
- msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
+// msn_cmdproc_send(cmdproc, "SYN", "%s", "0");
+ //TODO we should use SOAP contact to fetch contact list
}
else if (!g_ascii_strcasecmp(cmd->params[1], "TWN"))
{
@@ -249,15 +264,20 @@ usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
session->nexus = msn_nexus_new(session);
/* Parse the challenge data. */
-
+ session->nexus->challenge_data_str = g_strdup(cmd->params[3]);
elems = g_strsplit(cmd->params[3], ",", 0);
for (cur = elems; *cur != NULL; cur++)
{
- tokens = g_strsplit(*cur, "=", 2);
+ tokens = g_strsplit(*cur, "=", 2);
+ if(tokens[0]&&tokens[1])
+ {
+ purple_debug_info("MSNP14","challenge %p,key:%s,value:%s\n",
+ session->nexus->challenge_data,tokens[0],tokens[1]);
g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]);
- /* Don't free each of the tokens, only the array. */
- g_free(tokens);
+ }
+ /* Don't free each of the tokens, only the array. */
+ g_free(tokens);
}
g_strfreev(elems);
@@ -322,8 +342,15 @@ ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
return;
}
+ /*
+ * Windows Live Messenger 8.0
+ * Notice :CVR String discriminate!
+ * reference of http://www.microsoft.com/globaldev/reference/oslocversion.mspx
+ * to see the Local ID
+ */
msn_cmdproc_send(cmdproc, "CVR",
- "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s",
+// "0x0409 winnt 5.1 i386 MSG80BETA 8.0.0689 msmsgs %s",
+ "0x0804 winnt 5.1 i386 MSNMSGR 8.0.0792 msmsgs %s",
purple_account_get_username(account));
}
@@ -366,7 +393,7 @@ msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
msg = msn_message_new_from_cmd(cmdproc->session, cmd);
- msn_message_parse_payload(msg, payload, len);
+ msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM);
#ifdef MSN_DEBUG_NS
msn_message_show_readable(msg, "Notification", TRUE);
#endif
@@ -379,9 +406,12 @@ msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
static void
msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
+ purple_debug_info("MSNP14","Processing MSG... \n");
+ if(cmd->payload_len == 0){
+ return;
+ }
/* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued
* command and we are processing it */
-
if (cmd->payload == NULL)
{
cmdproc->last_cmd->payload_cb = msg_cmd_post;
@@ -391,32 +421,145 @@ msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
g_return_if_fail(cmd->payload_cb != NULL);
+ purple_debug_info("MSNP14","MSG payload:{%s}\n",cmd->payload);
cmd->payload_cb(cmdproc, cmd, cmd->payload, cmd->payload_len);
}
}
+/*send Message to Yahoo Messenger*/
+void
+uum_send_msg(MsnSession *session,MsnMessage *msg)
+{
+ MsnCmdProc *cmdproc;
+ MsnTransaction *trans;
+ char *payload;
+ gsize payload_len;
+ int type;
+
+ cmdproc = session->notification->cmdproc;
+ g_return_if_fail(msg != NULL);
+ payload = msn_message_gen_payload(msg, &payload_len);
+ purple_debug_info("MSNP14","send UUM,payload{%s},strlen:%d,len:%d\n",
+ payload,strlen(payload),payload_len);
+ type = msg->type;
+ trans = msn_transaction_new(cmdproc, "UUM","%s 32 %d %d",msg->remote_user,type,payload_len);
+ msn_transaction_set_payload(trans, payload, strlen(payload));
+ msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+static void
+ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+ size_t len)
+{
+ MsnMessage *msg;
+ PurpleConnection *gc;
+ const char *passport;
+ const char *content_type;
+
+ purple_debug_info("MSNP14","Process UBM payload:%s\n",payload);
+ msg = msn_message_new_from_cmd(cmdproc->session, cmd);
+
+ msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM);
+#ifdef MSN_DEBUG_NS
+ msn_message_show_readable(msg, "Notification", TRUE);
+#endif
+
+ gc = cmdproc->session->account->gc;
+ passport = msg->remote_user;
+
+ content_type = msn_message_get_content_type(msg);
+ purple_debug_info("MSNP14","type:%d\n",content_type);
+ if(!strcmp(content_type,"text/plain")){
+ const char *value;
+ const char *body;
+ char *body_str;
+ char *body_enc;
+ char *body_final = NULL;
+ size_t body_len;
+
+ body = msn_message_get_bin_data(msg, &body_len);
+ body_str = g_strndup(body, body_len);
+ body_enc = g_markup_escape_text(body_str, -1);
+ g_free(body_str);
+
+ if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) {
+ char *pre, *post;
+
+ msn_parse_format(value, &pre, &post);
+ body_final = g_strdup_printf("%s%s%s", pre ? pre : "",
+ body_enc ? body_enc : "", post ? post : "");
+ g_free(pre);
+ g_free(post);
+ }
+ g_free(body_enc);
+ serv_got_im(gc, passport, body_final, 0, time(NULL));
+ }
+ if(!strcmp(content_type,"text/x-msmsgscontrol")){
+ if(msn_message_get_attr(msg, "TypingUser") != NULL){
+ serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT,
+ PURPLE_TYPING);
+ }
+ }
+ if(!strcmp(content_type,"text/x-msnmsgr-datacast")){
+ char *username, *str;
+ PurpleAccount *account;
+ PurpleBuddy *buddy;
+ const char *user;
+
+ account = cmdproc->session->account;
+ user = msg->remote_user;
+
+ if ((buddy = purple_find_buddy(account, user)) != NULL){
+ username = g_markup_escape_text(purple_buddy_get_alias(buddy), -1);
+ }else{
+ username = g_markup_escape_text(user, -1);
+ }
+
+ str = g_strdup_printf(_("%s just sent you a Nudge!"), username);
+ g_free(username);
+ msn_session_report_user(cmdproc->session,user,str,PURPLE_MESSAGE_SYSTEM);
+ g_free(str);
+ }
+ msn_message_destroy(msg);
+}
+
+/*Yahoo msg process*/
+static void
+ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ purple_debug_info("MSNP14","Processing UBM... \n");
+ if(cmd->payload_len == 0){
+ return;
+ }
+ /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued
+ * command and we are processing it */
+ if (cmd->payload == NULL){
+ cmdproc->last_cmd->payload_cb = ubm_cmd_post;
+ cmdproc->servconn->payload_len = atoi(cmd->params[2]);
+ }else{
+ g_return_if_fail(cmd->payload_cb != NULL);
+
+ purple_debug_info("MSNP14","UBM payload:{%s}\n",cmd->payload);
+ ubm_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len);
+ }
+}
+
/**************************************************************************
* Challenges
+ * we use MD5 to caculate the Challenges
**************************************************************************/
-
static void
chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnTransaction *trans;
char buf[33];
- const char *challenge_resp;
- PurpleCipher *cipher;
- PurpleCipherContext *context;
- guchar digest[16];
- int i;
+#if 0
cipher = purple_ciphers_find_cipher("md5");
context = purple_cipher_context_new(cipher, NULL);
-
purple_cipher_context_append(context, (const guchar *)cmd->params[1],
strlen(cmd->params[1]));
-
- challenge_resp = "VT6PX?UQTM4WM%YR";
+ challenge_resp = MSNP13_WLM_PRODUCT_KEY;
purple_cipher_context_append(context, (const guchar *)challenge_resp,
strlen(challenge_resp));
@@ -424,9 +567,14 @@ chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
purple_cipher_context_destroy(context);
for (i = 0; i < 16; i++)
+ {
g_snprintf(buf + (i*2), 3, "%02x", digest[i]);
-
- trans = msn_transaction_new(cmdproc, "QRY", "%s 32", "PROD0038W!61ZTF9");
+ }
+#else
+ msn_handle_chl(cmd->params[1], buf);
+#endif
+// purple_debug_info("MSNP14","<<challenge:{%s}:{%s}\n",cmd->params[1],buf);
+ trans = msn_transaction_new(cmdproc, "QRY", "%s 32", MSNP13_WLM_PRODUCT_ID);
msn_transaction_set_payload(trans, buf, 32);
@@ -436,43 +584,301 @@ chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
/**************************************************************************
* Buddy Lists
**************************************************************************/
+/* add contact to xmlnode */
+static void
+msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnUserType type)
+{
+ xmlnode *d_node,*c_node;
+ char **tokens;
+ char *email,*domain;
+ char *list_op_str,*type_str;
+
+ purple_debug_info("MSNP14","Passport: %s, type: %d\n", passport, type);
+ tokens = g_strsplit(passport, "@", 2);
+ email = tokens[0];
+ domain = tokens[1];
+
+ /*find a domain Node*/
+ for(d_node = xmlnode_get_child(mlNode,"d"); d_node; d_node = xmlnode_get_next_twin(d_node))
+ {
+ const char * attr = NULL;
+ purple_debug_info("MSNP14","d_node: %s\n",d_node->name);
+ attr = xmlnode_get_attrib(d_node,"n");
+ if(attr == NULL){
+ continue;
+ }
+ if(!strcmp(attr,domain)){
+ break;
+ }
+ }
+ if(d_node == NULL)
+ {
+ /*domain not found, create a new domain Node*/
+ purple_debug_info("MSNP14","get No d_node\n");
+ d_node = xmlnode_new("d");
+ xmlnode_set_attrib(d_node,"n",domain);
+ xmlnode_insert_child(mlNode,d_node);
+ }
+
+ /*create contact node*/
+ c_node = xmlnode_new("c");
+ xmlnode_set_attrib(c_node,"n",email);
+
+ list_op_str = g_strdup_printf("%d",list_op);
+ purple_debug_info("MSNP14","list_op: %d\n",list_op);
+ xmlnode_set_attrib(c_node,"l",list_op_str);
+ g_free(list_op_str);
+
+ if (type != MSN_USER_TYPE_UNKNOWN) {
+ type_str = g_strdup_printf("%d", type);
+ } else {
+ if (msn_user_is_yahoo(session->account, passport))
+ type_str = g_strdup_printf("%d", MSN_USER_TYPE_YAHOO);
+ else
+ type_str = g_strdup_printf("%d", MSN_USER_TYPE_PASSPORT);
+ }
+ /*mobile*/
+ //type_str = g_strdup_printf("4");
+ xmlnode_set_attrib(c_node,"t",type_str);
+ g_free(type_str);
+
+ xmlnode_insert_child(d_node, c_node);
+
+ g_strfreev(tokens);
+}
static void
-add_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len)
+{
+ MsnTransaction *trans;
+ purple_debug_info("MSN Notification","Sending ADL with payload: %s\n", payload);
+ trans = msn_transaction_new(cmdproc, "ADL","%d", strlen(payload));
+ msn_transaction_set_payload(trans, payload, strlen(payload));
+ msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+/*dump contact info to NS*/
+void
+msn_notification_dump_contact(MsnSession *session)
{
- MsnSession *session;
MsnUser *user;
- const char *list;
- const char *passport;
- const char *friendly;
- MsnListId list_id;
- int group_id;
+ GList *l;
+ xmlnode *adl_node;
+ char *payload;
+ int payload_len;
+ int adl_count = 0;
+ const char *display_name;
+
+ adl_node = xmlnode_new("ml");
+ adl_node->child = NULL;
+ xmlnode_set_attrib(adl_node, "l", "1");
+
+ /*get the userlist*/
+ for (l = session->userlist->users; l != NULL; l = l->next){
+ user = l->data;
+
+ /* skip RL & PL during initial dump */
+ if (!(user->list_op & MSN_LIST_OP_MASK))
+ continue;
+
+ msn_add_contact_xml(session, adl_node, user->passport,
+ user->list_op & MSN_LIST_OP_MASK, user->type);
+
+ /* each ADL command may contain up to 150 contacts */
+ if (++adl_count % 150 == 0 || l->next == NULL) {
+ payload = xmlnode_to_str(adl_node,&payload_len);
+
+ msn_notification_post_adl(session->notification->cmdproc,
+ payload, payload_len);
+
+ g_free(payload);
+ xmlnode_free(adl_node);
+
+ if (l->next) {
+ adl_node = xmlnode_new("ml");
+ adl_node->child = NULL;
+ xmlnode_set_attrib(adl_node, "l", "1");
+ }
+ }
+ }
- list = cmd->params[1];
- passport = cmd->params[3];
- friendly = purple_url_decode(cmd->params[4]);
+ if (adl_count == 0) {
+ payload = xmlnode_to_str(adl_node,&payload_len);
- session = cmdproc->session;
+ msn_notification_post_adl(session->notification->cmdproc, payload, payload_len);
- user = msn_userlist_find_user(session->userlist, passport);
+ g_free(payload);
+ xmlnode_free(adl_node);
+ }
- if (user == NULL)
- {
- user = msn_user_new(session->userlist, passport, friendly);
- msn_userlist_add_user(session->userlist, user);
+ display_name = purple_connection_get_display_name(session->account->gc);
+ if (display_name
+ && strcmp(display_name,
+ purple_account_get_username(session->account))) {
+ msn_act_id(session->account->gc, display_name);
}
- else
- msn_user_set_friendly_name(user, friendly);
- list_id = msn_get_list_id(list);
+}
- if (cmd->param_count >= 6)
- group_id = atoi(cmd->params[5]);
- else
- group_id = -1;
+/*Post FQY to NS,Inform add a Yahoo User*/
+void
+msn_notification_send_fqy(MsnSession *session, const char *passport)
+{
+ MsnTransaction *trans;
+ MsnCmdProc *cmdproc;
+ char* email,*domain,*payload;
+ char **tokens;
- msn_got_add_user(session, user, list_id, group_id);
- msn_user_update(user);
+ cmdproc = session->notification->cmdproc;
+
+ tokens = g_strsplit(passport, "@", 2);
+ email = tokens[0];
+ domain = tokens[1];
+
+ payload = g_strdup_printf("<ml><d n=\"%s\"><c n=\"%s\"/></d></ml>", domain, email);
+ trans = msn_transaction_new(cmdproc, "FQY","%d", strlen(payload));
+ msn_transaction_set_payload(trans, payload, strlen(payload));
+ msn_cmdproc_send_trans(cmdproc, trans);
+
+ g_free(payload);
+ g_free(tokens);
+}
+
+static void
+blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+}
+
+static void
+adl_cmd_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+ size_t len)
+{
+ xmlnode *root, *domain_node;
+
+ purple_debug_misc("MSN Notification", "Parsing received ADL XML data\n");
+
+ g_return_if_fail(payload != NULL);
+
+ root = xmlnode_from_str(payload, (gssize) len);
+
+ if (root == NULL) {
+ purple_debug_info("MSN Notification", "Invalid XML!\n");
+ return;
+ }
+ for (domain_node = xmlnode_get_child(root, "d"); domain_node; domain_node = xmlnode_get_next_twin(domain_node)) {
+ const gchar * domain = NULL;
+ xmlnode *contact_node = NULL;
+
+ domain = xmlnode_get_attrib(domain_node, "n");
+
+ for (contact_node = xmlnode_get_child(domain_node, "c"); contact_node; contact_node = xmlnode_get_next_twin(contact_node)) {
+// gchar *name = NULL, *friendlyname = NULL, *passport= NULL;
+ const gchar *list;
+ gint list_op = 0;
+
+// name = xmlnode_get_attrib(contact_node, "n");
+ list = xmlnode_get_attrib(contact_node, "l");
+ if (list != NULL) {
+ list_op = atoi(list);
+ }
+// friendlyname = xmlnode_get_attrib(contact_node, "f");
+
+// passport = g_strdup_printf("%s@%s", name, domain);
+
+// if (friendlyname != NULL) {
+// decoded_friendlyname = g_strdup(purple_url_decode(friendlyname));
+// } else {
+// decoded_friendlyname = g_strdup(passport);
+// }
+
+ if (list_op & MSN_LIST_RL_OP) {
+ /* someone is adding us */
+// got_new_entry(cmdproc->session->account->gc, passport, decoded_friendly_name);
+ msn_get_contact_list(cmdproc->session->contact, MSN_PS_PENDING_LIST, NULL);
+ }
+
+// g_free(decoded_friendly_name);
+// g_free(passport);
+ }
+ }
+
+ xmlnode_free(root);
+}
+
+static void
+adl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ MsnSession *session;
+
+ g_return_if_fail(cmdproc != NULL);
+ g_return_if_fail(cmdproc->session != NULL);
+ g_return_if_fail(cmdproc->last_cmd != NULL);
+ g_return_if_fail(cmd != NULL);
+
+ session = cmdproc->session;
+
+ if ( !strcmp(cmd->params[1], "OK")) {
+ /* ADL ack */
+ msn_session_finish_login(session);
+ } else {
+ cmdproc->last_cmd->payload_cb = adl_cmd_parse;
+ }
+
+ return;
+}
+
+static void
+adl_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
+{
+ MsnSession *session;
+ PurpleAccount *account;
+ PurpleConnection *gc;
+ char *reason = NULL;
+
+ session = cmdproc->session;
+ account = session->account;
+ gc = purple_account_get_connection(account);
+
+ purple_debug_error("msn","ADL error\n");
+ reason = g_strdup_printf(_("Unknown error (%d)"), error);
+ purple_notify_error(gc, NULL, _("Unable to add user"), reason);
+ g_free(reason);
+}
+
+static void
+fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+ size_t len)
+{
+ purple_debug_info("MSN Notification","FQY payload:\n%s\n", payload);
+ g_return_if_fail(cmdproc->session != NULL);
+ g_return_if_fail(cmdproc->session->contact != NULL);
+// msn_notification_post_adl(cmdproc, payload, len);
+// msn_get_address_book(cmdproc->session->contact, MSN_AB_SAVE_CONTACT, NULL, NULL);
+}
+
+static void
+fqy_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ purple_debug_info("MSNP14","Process FQY\n");
+ cmdproc->last_cmd->payload_cb = fqy_cmd_post;
+}
+
+static void
+rml_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+#if 0
+ MsnTransaction *trans;
+ char * payload;
+#endif
+
+ purple_debug_info("MSNP14","Process RML\n");
+#if 0
+ trans = msn_transaction_new(cmdproc, "RML","");
+
+ msn_transaction_set_payload(trans, payload, strlen(payload));
+
+ msn_cmdproc_send_trans(cmdproc, trans);
+#endif
}
static void
@@ -567,24 +973,22 @@ adg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
group_name = purple_url_decode(cmd->params[2]);
- msn_group_new(session->userlist, group_id, group_name);
+ msn_group_new(session->userlist, cmd->params[3], group_name);
- /* There is a user that must me moved to this group */
+ /* There is a user that must be moved to this group */
if (cmd->trans->data)
{
/* msn_userlist_move_buddy(); */
MsnUserList *userlist = cmdproc->session->userlist;
- MsnMoveBuddy *data = cmd->trans->data;
+ MsnCallbackState *data = cmd->trans->data;
if (data->old_group_name != NULL)
{
- msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->old_group_name);
+ msn_userlist_move_buddy(userlist, data->who, data->old_group_name, group_name);
g_free(data->old_group_name);
+ } else {
+ // msn_add_contact_to_group(userlist, data, data->who, group_name);
}
-
- msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, group_name);
- g_free(data->who);
-
}
}
@@ -642,6 +1046,7 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
PurpleConnection *gc;
MsnUser *user;
MsnObject *msnobj;
+ int wlmclient;
const char *state, *passport, *friendly;
session = cmdproc->session;
@@ -650,7 +1055,9 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
state = cmd->params[1];
passport = cmd->params[2];
- friendly = purple_url_decode(cmd->params[3]);
+ /*if a contact is actually on the WLM part or the yahoo part*/
+ wlmclient = atoi(cmd->params[3]);
+ friendly = purple_url_decode(cmd->params[4]);
user = msn_userlist_find_user(session->userlist, passport);
@@ -658,9 +1065,9 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
msn_user_set_friendly_name(user, friendly);
- if (session->protocol_ver >= 9 && cmd->param_count == 6)
+ if (session->protocol_ver >= 9 && cmd->param_count == 8)
{
- msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
+ msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6]));
msn_user_set_object(user, msnobj);
}
@@ -692,6 +1099,7 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
MsnUser *user;
MsnObject *msnobj;
int clientid;
+ int wlmclient;
const char *state, *passport, *friendly, *old_friendly;
session = cmdproc->session;
@@ -700,7 +1108,8 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
state = cmd->params[0];
passport = cmd->params[1];
- friendly = purple_url_decode(cmd->params[2]);
+ wlmclient = atoi(cmd->params[2]);
+ friendly = purple_url_decode(cmd->params[3]);
user = msn_userlist_find_user(session->userlist, passport);
@@ -713,10 +1122,9 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
if (session->protocol_ver >= 9)
{
- if (cmd->param_count == 5)
+ if (cmd->param_count == 7)
{
- msnobj =
- msn_object_new_from_string(purple_url_decode(cmd->params[4]));
+ msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
msn_user_set_object(user, msnobj);
}
else
@@ -725,7 +1133,7 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
}
}
- clientid = atoi(cmd->params[3]);
+ clientid = atoi(cmd->params[4]);
user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE);
msn_user_set_state(user, state);
@@ -797,7 +1205,9 @@ static void
prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session = cmdproc->session;
- const char *type, *value;
+ const char *type, *value, *friendlyname;
+
+ purple_debug_info("MSN Notification", "prp_cmd()\n");
g_return_if_fail(cmd->param_count >= 3);
@@ -821,6 +1231,18 @@ prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
msn_user_set_work_phone(session->user, NULL);
else if (!strcmp(type, "PHM"))
msn_user_set_mobile_phone(session->user, NULL);
+ else {
+ type = cmd->params[1];
+ if (!strcmp(type, "MFN")) {
+ friendlyname = purple_url_decode(cmd->params[2]);
+
+ msn_update_contact(session->contact, friendlyname);
+
+ purple_connection_set_display_name(
+ purple_account_get_connection(session->account),
+ friendlyname);
+ }
+ }
}
}
@@ -828,11 +1250,10 @@ static void
reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
- int group_id;
- const char *group_name;
+ const char *group_id, *group_name;
session = cmdproc->session;
- group_id = atoi(cmd->params[2]);
+ group_id = cmd->params[2];
group_name = purple_url_decode(cmd->params[3]);
msn_userlist_rename_group_id(session->userlist, group_id, group_name);
@@ -841,27 +1262,26 @@ reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
static void
reg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
{
- int group_id;
+ const char * group_id;
char **params;
params = g_strsplit(trans->params, " ", 0);
- group_id = atoi(params[0]);
+ group_id = params[0];
group_error_helper(cmdproc->session, _("Unable to rename group"), group_id, error);
g_strfreev(params);
}
+#if 0
static void
rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
MsnUser *user;
- const char *list;
- const char *passport;
+ const char *group_id, *list, *passport;
MsnListId list_id;
- int group_id;
session = cmdproc->session;
list = cmd->params[1];
@@ -873,22 +1293,23 @@ rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
list_id = msn_get_list_id(list);
if (cmd->param_count == 5)
- group_id = atoi(cmd->params[4]);
+ group_id = cmd->params[4];
else
- group_id = -1;
+ group_id = NULL;
msn_got_rem_user(session, user, list_id, group_id);
msn_user_update(user);
}
+#endif
static void
rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
- int group_id;
+ const char *group_id;
session = cmdproc->session;
- group_id = atoi(cmd->params[2]);
+ group_id = cmd->params[2];
msn_userlist_remove_group_id(session->userlist, group_id);
}
@@ -896,18 +1317,19 @@ rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
static void
rmg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
{
- int group_id;
+ const char *group_id;
char **params;
params = g_strsplit(trans->params, " ", 0);
- group_id = atoi(params[0]);
+ group_id = params[0];
group_error_helper(cmdproc->session, _("Unable to delete group"), group_id, error);
g_strfreev(params);
}
+#if 0
static void
syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
@@ -938,6 +1360,7 @@ syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
session->sync = sync;
cmdproc->cbs_table = sync->cbs_table;
}
+#endif
/**************************************************************************
* Misc commands
@@ -1146,6 +1569,103 @@ xfr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
g_free(host);
}
+static void
+gcf_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+ size_t len)
+{
+ xmlnode * root;
+ gchar * buf;
+
+ g_return_if_fail(cmd->payload != NULL);
+
+ if ( (root = xmlnode_from_str(cmd->payload, cmd->payload_len)) == NULL)
+ {
+ purple_debug_error("MSN","Unable to parse GCF payload into a XML tree");
+ return;
+ }
+
+ buf = xmlnode_to_formatted_str(root, NULL);
+
+ /* get the payload content */
+ purple_debug_info("MSNP14","GCF command payload:\n%s\n",buf);
+
+ g_free(buf);
+ xmlnode_free(root);
+}
+
+static void
+gcf_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ purple_debug_info("MSNP14","Processing GCF command\n");
+ cmdproc->last_cmd->payload_cb = gcf_cmd_post;
+ return;
+}
+
+static void
+sbs_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ purple_debug_info("MSNP14","Processing SBS... \n");
+ if(cmd->payload_len == 0){
+ return;
+ }
+ /*get the payload content*/
+}
+
+/*
+ * Get the UBX's PSM info
+ * Post it to the User status
+ * Thanks for Chris <ukdrizzle@yahoo.co.uk>'s code
+ */
+static void
+ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+ size_t len)
+{
+ MsnSession *session;
+ PurpleAccount *account;
+ PurpleConnection *gc;
+ MsnUser *user;
+ const char *passport;
+ char *psm_str, *currentmedia_str, *str;
+
+ /*get the payload content*/
+// purple_debug_info("MSNP14","UBX {%s} payload{%s}\n",cmd->params[0], cmd->payload);
+
+ session = cmdproc->session;
+ account = session->account;
+ gc = purple_account_get_connection(account);
+
+ passport = cmd->params[0];
+ user = msn_userlist_find_user(session->userlist, passport);
+
+ psm_str = msn_get_psm(cmd->payload,len);
+ currentmedia_str = msn_parse_currentmedia(
+ str = msn_get_currentmedia(cmd->payload, len));
+ g_free(str);
+
+ msn_user_set_statusline(user, psm_str);
+ msn_user_set_currentmedia(user, currentmedia_str);
+ msn_user_update(user);
+
+ g_free(psm_str);
+ g_free(currentmedia_str);
+}
+
+static void
+ubx_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ purple_debug_misc("MSNP14","UBX received.\n");
+ if(cmd->payload_len == 0){
+ return;
+ }
+ cmdproc->last_cmd->payload_cb = ubx_cmd_post;
+}
+
+static void
+uux_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ purple_debug_misc("MSNP14","UUX received.\n");
+}
+
/**************************************************************************
* Message Types
**************************************************************************/
@@ -1155,6 +1675,7 @@ profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
MsnSession *session;
const char *value;
+ const char *clLastChange;
session = cmdproc->session;
@@ -1187,10 +1708,27 @@ profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
}
if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL)
+ {
session->passport_info.client_port = ntohs(atoi(value));
+ }
if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL)
session->passport_info.sl = atol(value);
+
+ /*starting retrieve the contact list*/
+ clLastChange = purple_account_get_string(session->account, "CLLastChange", NULL);
+ session->contact = msn_contact_new(session);
+#ifdef MSN_PARTIAL_LISTS
+ /* msn_userlist_load defeats all attempts at trying to detect blist sync issues */
+ msn_userlist_load(session);
+ msn_get_contact_list(session->contact, MSN_PS_INITIAL, clLastChange);
+#else
+ /* always get the full list? */
+ msn_get_contact_list(session->contact, MSN_PS_INITIAL, NULL);
+#endif
+#if 0
+ msn_contact_connect(session->contact);
+#endif
}
static void
@@ -1246,6 +1784,86 @@ initial_email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
g_hash_table_destroy(table);
}
+/*offline Message notification process*/
+static void
+initial_mdata_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+ MsnSession *session;
+ PurpleConnection *gc;
+ GHashTable *table;
+ const char *mdata, *unread;
+
+ session = cmdproc->session;
+ gc = session->account->gc;
+
+ if (strcmp(msg->remote_user, "Hotmail"))
+ /* This isn't an official message. */
+ return;
+
+ /*new a oim session*/
+ session->oim = msn_oim_new(session);
+// msn_oim_connect(session->oim);
+
+ table = msn_message_get_hashtable_from_body(msg);
+
+ mdata = g_hash_table_lookup(table, "Mail-Data");
+
+ if (mdata != NULL)
+ msn_parse_oim_msg(session->oim, mdata);
+
+ if (g_hash_table_lookup(table, "Inbox-URL") == NULL)
+ {
+ g_hash_table_destroy(table);
+ return;
+ }
+
+ if (session->passport_info.file == NULL)
+ {
+ MsnTransaction *trans;
+ trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX");
+ msn_transaction_queue_cmd(trans, msg->cmd);
+
+ msn_cmdproc_send_trans(cmdproc, trans);
+
+ g_hash_table_destroy(table);
+ return;
+ }
+
+ if (!purple_account_get_check_mail(session->account))
+ {
+ g_hash_table_destroy(table);
+ return;
+ }
+
+ unread = g_hash_table_lookup(table, "Inbox-Unread");
+
+ if (unread != NULL)
+ {
+ int count = atoi(unread);
+
+ if (count > 0)
+ {
+ const char *passport;
+ const char *url;
+
+ passport = msn_user_get_passport(session->user);
+ url = session->passport_info.file;
+
+ purple_notify_emails(gc, atoi(unread), FALSE, NULL, NULL,
+ &passport, &url, NULL, NULL);
+ }
+ }
+
+ g_hash_table_destroy(table);
+}
+
+/*offline Message Notification*/
+static void
+delete_oim_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+ purple_debug_misc("MSN Notification","Delete OIM message.\n");
+}
+
static void
email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
@@ -1347,48 +1965,62 @@ system_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
}
void
-msn_notification_add_buddy(MsnNotification *notification, const char *list,
- const char *who, const char *store_name,
- int group_id)
+msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list_id,
+ const char *who)
{
MsnCmdProc *cmdproc;
+ MsnListOp list_op = 1 << list_id;
+ xmlnode *adl_node;
+ char *payload;
+ int payload_len;
+
cmdproc = notification->servconn->cmdproc;
- if (group_id < 0 && !strcmp(list, "FL"))
- group_id = 0;
+ adl_node = xmlnode_new("ml");
+ adl_node->child = NULL;
- if (group_id >= 0)
- {
- msn_cmdproc_send(cmdproc, "ADD", "%s %s %s %d",
- list, who, store_name, group_id);
- }
- else
- {
- msn_cmdproc_send(cmdproc, "ADD", "%s %s %s", list, who, store_name);
- }
+ msn_add_contact_xml(notification->session, adl_node, who, list_op,
+ MSN_USER_TYPE_PASSPORT);
+
+ payload = xmlnode_to_str(adl_node,&payload_len);
+ xmlnode_free(adl_node);
+
+ msn_notification_post_adl(notification->servconn->cmdproc,
+ payload,payload_len);
+ g_free(payload);
}
void
-msn_notification_rem_buddy(MsnNotification *notification, const char *list,
- const char *who, int group_id)
+msn_notification_rem_buddy_from_list(MsnNotification *notification, MsnListId list_id,
+ const char *who)
{
MsnCmdProc *cmdproc;
+ MsnTransaction *trans;
+ MsnListOp list_op = 1 << list_id;
+ xmlnode *rml_node;
+ char *payload;
+ int payload_len;
+
cmdproc = notification->servconn->cmdproc;
- if (group_id >= 0)
- {
- msn_cmdproc_send(cmdproc, "REM", "%s %s %d", list, who, group_id);
- }
- else
- {
- msn_cmdproc_send(cmdproc, "REM", "%s %s", list, who);
- }
+ rml_node = xmlnode_new("ml");
+ rml_node->child = NULL;
+
+ msn_add_contact_xml(notification->session, rml_node, who, list_op, MSN_USER_TYPE_PASSPORT);
+
+ payload = xmlnode_to_str(rml_node, &payload_len);
+ xmlnode_free(rml_node);
+
+ purple_debug_info("MSN Notification","Send RML with payload:\n%s\n", payload);
+ trans = msn_transaction_new(cmdproc, "RML","%d", strlen(payload));
+ msn_transaction_set_payload(trans, payload, strlen(payload));
+ msn_cmdproc_send_trans(cmdproc, trans);
+ g_free(payload);
}
/**************************************************************************
* Init
**************************************************************************/
-
void
msn_notification_init(void)
{
@@ -1399,18 +2031,18 @@ msn_notification_init(void)
/* Synchronous */
msn_table_add_cmd(cbs_table, "CHG", "CHG", NULL);
msn_table_add_cmd(cbs_table, "CHG", "ILN", iln_cmd);
- msn_table_add_cmd(cbs_table, "ADD", "ADD", add_cmd);
- msn_table_add_cmd(cbs_table, "ADD", "ILN", iln_cmd);
- msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd);
+ msn_table_add_cmd(cbs_table, "ADL", "ILN", iln_cmd);
+// msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd); /* Removed as of MSNP13 */
msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
msn_table_add_cmd(cbs_table, "USR", "XFR", xfr_cmd);
- msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd);
+ msn_table_add_cmd(cbs_table, "USR", "GCF", gcf_cmd);
+// msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd); /* Removed as of MSNP13 */
msn_table_add_cmd(cbs_table, "CVR", "CVR", cvr_cmd);
msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd);
msn_table_add_cmd(cbs_table, "REA", "REA", rea_cmd);
msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd);
- /* msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); */
- msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL);
+ msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd);
+// msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL);
msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd);
msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd);
msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd);
@@ -1419,11 +2051,15 @@ msn_notification_init(void)
/* Asynchronous */
msn_table_add_cmd(cbs_table, NULL, "IPG", ipg_cmd);
msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "UBM", ubm_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "GCF", gcf_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "SBS", sbs_cmd);
msn_table_add_cmd(cbs_table, NULL, "NOT", not_cmd);
msn_table_add_cmd(cbs_table, NULL, "CHL", chl_cmd);
- msn_table_add_cmd(cbs_table, NULL, "REM", rem_cmd);
- msn_table_add_cmd(cbs_table, NULL, "ADD", add_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "RML", rml_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "ADL", adl_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "FQY", fqy_cmd);
msn_table_add_cmd(cbs_table, NULL, "QRY", NULL);
msn_table_add_cmd(cbs_table, NULL, "QNG", qng_cmd);
@@ -1433,11 +2069,15 @@ msn_notification_init(void)
msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
msn_table_add_cmd(cbs_table, NULL, "RNG", rng_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "UBX", ubx_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "UUX", uux_cmd);
+
msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd);
msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd);
msn_table_add_error(cbs_table, "ADD", add_error);
+ msn_table_add_error(cbs_table, "ADL", adl_error);
msn_table_add_error(cbs_table, "REG", reg_error);
msn_table_add_error(cbs_table, "RMG", rmg_error);
/* msn_table_add_error(cbs_table, "REA", rea_error); */
@@ -1446,12 +2086,24 @@ msn_notification_init(void)
msn_table_add_msg_type(cbs_table,
"text/x-msmsgsprofile",
profile_msg);
+ /*initial OIM notification*/
+ msn_table_add_msg_type(cbs_table,
+ "text/x-msmsgsinitialmdatanotification",
+ initial_mdata_msg);
+ /*OIM notification when user online*/
+ msn_table_add_msg_type(cbs_table,
+ "text/x-msmsgsoimnotification",
+ initial_mdata_msg);
msn_table_add_msg_type(cbs_table,
"text/x-msmsgsinitialemailnotification",
initial_email_msg);
msn_table_add_msg_type(cbs_table,
"text/x-msmsgsemailnotification",
email_msg);
+ /*delete an offline Message notification*/
+ msn_table_add_msg_type(cbs_table,
+ "text/x-msmsgsactivemailnotification",
+ delete_oim_msg);
msn_table_add_msg_type(cbs_table,
"application/x-msmsgssystemmessage",
system_msg);
@@ -1462,3 +2114,4 @@ msn_notification_end(void)
{
msn_table_destroy(cbs_table);
}
+
diff --git a/libpurple/protocols/msn/notification.h b/libpurple/protocols/msn/notification.h
index f2e57208b1..de36583a7d 100644
--- a/libpurple/protocols/msn/notification.h
+++ b/libpurple/protocols/msn/notification.h
@@ -24,6 +24,14 @@
#ifndef _MSN_NOTIFICATION_H_
#define _MSN_NOTIFICATION_H_
+/*MSN protocol challenge info*/
+/*MSNP13 challenge*/
+#define MSNP13_WLM_PRODUCT_KEY "O4BG@C7BWLYQX?5G"
+#define MSNP13_WLM_PRODUCT_ID "PROD01065C%ZFN6F"
+
+#define MSNP10_PRODUCT_KEY "VT6PX?UQTM4WM%YR"
+#define MSNP10_PRODUCT_ID "PROD0038W!61ZTF9"
+
typedef struct _MsnNotification MsnNotification;
#include "session.h"
@@ -40,21 +48,24 @@ struct _MsnNotification
};
#include "state.h"
+void uum_send_msg(MsnSession *session,MsnMessage *msg);
void msn_notification_end(void);
void msn_notification_init(void);
-void msn_notification_add_buddy(MsnNotification *notification,
- const char *list, const char *who,
- const char *store_name, int group_id);
-void msn_notification_rem_buddy(MsnNotification *notification,
- const char *list, const char *who,
- int group_id);
+void msn_notification_add_buddy_to_list(MsnNotification *notification,
+ MsnListId list_id, const char *who);
+void msn_notification_rem_buddy_from_list(MsnNotification *notification,
+ MsnListId list_id, const char *who);
+
+void msn_notification_send_fqy(MsnSession *session, const char *passport);
+
MsnNotification *msn_notification_new(MsnSession *session);
void msn_notification_destroy(MsnNotification *notification);
gboolean msn_notification_connect(MsnNotification *notification,
- const char *host, int port);
+ const char *host, int port);
void msn_notification_disconnect(MsnNotification *notification);
+void msn_notification_dump_contact(MsnSession *session);
/**
* Closes a notification.
diff --git a/libpurple/protocols/msn/oim.c b/libpurple/protocols/msn/oim.c
new file mode 100644
index 0000000000..3054d26920
--- /dev/null
+++ b/libpurple/protocols/msn/oim.c
@@ -0,0 +1,717 @@
+/**
+ * @file oim.c
+ * get and send MSN offline Instant Message via SOAP request
+ * Author
+ * 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 "soap.h"
+#include "oim.h"
+#include "msnutils.h"
+
+/*Local Function Prototype*/
+static void msn_oim_post_single_get_msg(MsnOim *oim,const char *msgid);
+static MsnOimSendReq *msn_oim_new_send_req(const char *from_member,
+ const char *friendname,
+ const char* to_member,
+ gint send_seq,
+ const char *msg);
+static void msn_oim_retrieve_connect_init(MsnSoapConn *soapconn);
+static void msn_oim_send_connect_init(MsnSoapConn *soapconn);
+static void msn_oim_free_send_req(MsnOimSendReq *req);
+static void msn_oim_report_to_user(MsnOim *oim, const char *msg_str);
+static void msn_oim_get_process(MsnOim *oim, const char *oim_msg);
+static char *msn_oim_msg_to_str(MsnOim *oim, const char *body);
+static void msn_oim_send_process(MsnOim *oim, const char *body, int len);
+
+/*new a OIM object*/
+MsnOim *
+msn_oim_new(MsnSession *session)
+{
+ MsnOim *oim;
+
+ oim = g_new0(MsnOim, 1);
+ oim->session = session;
+ oim->retrieveconn = msn_soap_new(session,oim,1);
+
+ oim->oim_list = NULL;
+ oim->sendconn = msn_soap_new(session,oim,1);
+ oim->run_id = rand_guid();
+ oim->challenge = NULL;
+ oim->send_queue = g_queue_new();
+ oim->send_seq = 1;
+ return oim;
+}
+
+/*destroy the oim object*/
+void
+msn_oim_destroy(MsnOim *oim)
+{
+ MsnOimSendReq *request;
+
+ purple_debug_info("OIM","destroy the OIM \n");
+ msn_soap_destroy(oim->retrieveconn);
+ msn_soap_destroy(oim->sendconn);
+ g_free(oim->run_id);
+ g_free(oim->challenge);
+
+ while((request = g_queue_pop_head(oim->send_queue)) != NULL){
+ msn_oim_free_send_req(request);
+ }
+ g_queue_free(oim->send_queue);
+
+ g_free(oim);
+}
+
+static MsnOimSendReq *
+msn_oim_new_send_req(const char *from_member, const char*friendname,
+ const char* to_member, gint send_seq,
+ const char *msg)
+{
+ MsnOimSendReq *request;
+
+ request = g_new0(MsnOimSendReq, 1);
+ request->from_member =g_strdup(from_member);
+ request->friendname = g_strdup(friendname);
+ request->to_member = g_strdup(to_member);
+ request->send_seq = send_seq;
+ request->oim_msg = g_strdup(msg);
+ return request;
+}
+
+static void
+msn_oim_free_send_req(MsnOimSendReq *req)
+{
+ g_return_if_fail(req != NULL);
+
+ g_free(req->from_member);
+ g_free(req->friendname);
+ g_free(req->to_member);
+ g_free(req->oim_msg);
+
+ g_free(req);
+}
+
+/****************************************
+ * OIM send SOAP request
+ * **************************************/
+/*encode the message to OIM Message Format*/
+static char *
+msn_oim_msg_to_str(MsnOim *oim, const char *body)
+{
+ char *oim_body,*oim_base64;
+
+ purple_debug_info("MSNP14","encode OIM Message...\n");
+ oim_base64 = purple_base64_encode((const guchar *)body, strlen(body));
+ purple_debug_info("MSNP14","encoded base64 body:{%s}\n",oim_base64);
+ oim_body = g_strdup_printf(MSN_OIM_MSG_TEMPLATE,
+ oim->run_id,oim->send_seq,oim_base64);
+
+ return oim_body;
+}
+
+/*oim SOAP server login error*/
+static void
+msn_oim_send_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+{
+ MsnSoapConn *soapconn = data;
+ MsnSession *session;
+
+ session = soapconn->session;
+ g_return_if_fail(session != NULL);
+
+ msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to OIM server"));
+}
+
+/*msn oim SOAP server connect process*/
+static void
+msn_oim_send_connect_cb(gpointer data, PurpleSslConnection *gsc,
+ PurpleInputCondition cond)
+{
+ MsnSoapConn *soapconn = data;
+ MsnSession * session;
+ MsnOim *oim;
+
+ oim = soapconn->parent;
+ g_return_if_fail(oim != NULL);
+
+ session = oim->session;
+ g_return_if_fail(session != NULL);
+}
+
+/*
+ * Process the send return SOAP string
+ * If got SOAP Fault,get the lock key,and resend it.
+ */
+static void
+msn_oim_send_process(MsnOim *oim, const char *body, int len)
+{
+ xmlnode *responseNode, *bodyNode;
+ xmlnode *faultNode, *faultCodeNode, *faultstringNode;
+ xmlnode *detailNode, *challengeNode;
+ char *faultCodeStr = NULL, *faultstring = NULL;
+
+ responseNode = xmlnode_from_str(body,len);
+ g_return_if_fail(responseNode != NULL);
+ bodyNode = xmlnode_get_child(responseNode,"Body");
+ faultNode = xmlnode_get_child(bodyNode,"Fault");
+ if(faultNode == NULL){
+ /*Send OK! return*/
+ MsnOimSendReq *request;
+
+ purple_debug_info("MSNP14","send OIM OK!");
+ xmlnode_free(responseNode);
+ request = g_queue_pop_head(oim->send_queue);
+ msn_oim_free_send_req(request);
+ /*send next buffered Offline Message*/
+ msn_soap_post(oim->sendconn,NULL,msn_oim_send_connect_init);
+ return;
+ }
+ /*get the challenge,and repost it*/
+ faultCodeNode = xmlnode_get_child(faultNode,"faultcode");
+ if(faultCodeNode == NULL){
+ purple_debug_info("MSNP14","faultcode Node is NULL\n");
+ goto oim_send_process_fail;
+ }
+ faultCodeStr = xmlnode_get_data(faultCodeNode);
+ purple_debug_info("MSNP14","fault code:{%s}\n",faultCodeStr);
+#if 0
+ if(!strcmp(faultCodeStr,"q0:AuthenticationFailed")){
+ /*other Fault Reason?*/
+ goto oim_send_process_fail;
+ }
+#endif
+
+ faultstringNode = xmlnode_get_child(faultNode,"faultstring");
+ faultstring = xmlnode_get_data(faultstringNode);
+ purple_debug_info("MSNP14","fault string :{%s}\n",faultstring);
+
+ /* lock key fault reason,
+ * compute the challenge and resend it
+ */
+ detailNode = xmlnode_get_child(faultNode, "detail");
+ if(detailNode == NULL){
+ goto oim_send_process_fail;
+ }
+ challengeNode = xmlnode_get_child(detailNode,"LockKeyChallenge");
+ if (challengeNode == NULL) {
+ goto oim_send_process_fail;
+ }
+
+ g_free(oim->challenge);
+ oim->challenge = xmlnode_get_data(challengeNode);
+ purple_debug_info("MSNP14","lockkey:{%s}\n",oim->challenge);
+
+ /*repost the send*/
+ purple_debug_info("MSNP14","prepare to repost the send...\n");
+ msn_oim_send_msg(oim);
+
+oim_send_process_fail:
+ g_free(faultstring);
+ g_free(faultCodeStr);
+ xmlnode_free(responseNode);
+ return ;
+}
+
+static void
+msn_oim_send_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+ MsnSession *session = soapconn->session;
+ MsnOim * oim;
+
+ if (soapconn->body == NULL)
+ return;
+
+ g_return_if_fail(session != NULL);
+ oim = soapconn->session->oim;
+ g_return_if_fail(oim != NULL);
+
+ purple_debug_info("MSNP14","read buffer:{%s}\n",soapconn->body);
+ msn_oim_send_process(oim,soapconn->body,soapconn->body_len);
+}
+
+static void
+msn_oim_send_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ soapconn->read_cb = msn_oim_send_read_cb;
+// msn_soap_read_cb(data,source,cond);
+}
+
+void
+msn_oim_prep_send_msg_info(MsnOim *oim, const char *membername,
+ const char* friendname, const char *tomember,
+ const char * msg)
+{
+ MsnOimSendReq *request;
+
+ g_return_if_fail(oim != NULL);
+
+ request = msn_oim_new_send_req(membername,friendname,tomember,oim->send_seq,msg);
+ g_queue_push_tail(oim->send_queue,request);
+}
+
+/*post send single message request to oim server*/
+void
+msn_oim_send_msg(MsnOim *oim)
+{
+ MsnSoapReq *soap_request;
+ MsnOimSendReq *oim_request;
+ char *soap_body,*mspauth;
+ char *msg_body;
+ char buf[33];
+
+ g_return_if_fail(oim != NULL);
+ oim_request = g_queue_pop_head(oim->send_queue);
+ g_return_if_fail(oim_request != NULL);
+
+ purple_debug_info("MSNP14","send single OIM Message\n");
+ mspauth = g_strdup_printf("t=%s&amp;p=%s",
+ oim->session->passport_info.t,
+ oim->session->passport_info.p
+ );
+ g_queue_push_head(oim->send_queue,oim_request);
+
+ /* if we got the challenge lock key, we compute it
+ * else we go for the SOAP fault and resend it.
+ */
+ if(oim->challenge != NULL){
+ msn_handle_chl(oim->challenge, buf);
+ }else{
+ purple_debug_info("MSNP14","no lock key challenge,wait for SOAP Fault and Resend\n");
+ buf[0]='\0';
+ }
+ purple_debug_info("MSNP14","get the lock key challenge {%s}\n",buf);
+
+ msg_body = msn_oim_msg_to_str(oim, oim_request->oim_msg);
+ soap_body = g_strdup_printf(MSN_OIM_SEND_TEMPLATE,
+ oim_request->from_member,
+ oim_request->friendname,
+ oim_request->to_member,
+ mspauth,
+ MSNP13_WLM_PRODUCT_ID,
+ buf,
+ oim_request->send_seq,
+ msg_body
+ );
+ soap_request = msn_soap_request_new(MSN_OIM_SEND_HOST,
+ MSN_OIM_SEND_URL,
+ MSN_OIM_SEND_SOAP_ACTION,
+ soap_body,
+ NULL,
+ msn_oim_send_read_cb,
+ msn_oim_send_written_cb);
+ g_free(mspauth);
+ g_free(msg_body);
+ g_free(soap_body);
+
+ /*increase the offline Sequence control*/
+ if(oim->challenge != NULL){
+ oim->send_seq++;
+ }
+ msn_soap_post(oim->sendconn,soap_request,msn_oim_send_connect_init);
+}
+
+/****************************************
+ * OIM delete SOAP request
+ * **************************************/
+static void
+msn_oim_delete_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ if (soapconn->body == NULL)
+ return;
+ purple_debug_info("MSNP14","OIM delete read buffer:{%s}\n",soapconn->body);
+
+ msn_soap_free_read_buf(soapconn);
+ /*get next single Offline Message*/
+ msn_soap_post(soapconn,NULL,msn_oim_retrieve_connect_init);
+}
+
+static void
+msn_oim_delete_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ soapconn->read_cb = msn_oim_delete_read_cb;
+}
+
+/*Post to get the Offline Instant Message*/
+static void
+msn_oim_post_delete_msg(MsnOim *oim,const char *msgid)
+{
+ MsnSoapReq *soap_request;
+ const char *soap_body,*t,*p;
+
+ g_return_if_fail(oim != NULL);
+ g_return_if_fail(msgid != NULL);
+
+ purple_debug_info("MSNP14","Delete single OIM Message {%s}\n",msgid);
+ t = oim->session->passport_info.t;
+ p = oim->session->passport_info.p;
+
+ soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE,
+ t,
+ p,
+ msgid
+ );
+ soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST,
+ MSN_OIM_RETRIEVE_URL,
+ MSN_OIM_DEL_SOAP_ACTION,
+ soap_body,
+ NULL,
+ msn_oim_delete_read_cb,
+ msn_oim_delete_written_cb);
+ msn_soap_post(oim->retrieveconn,soap_request,msn_oim_retrieve_connect_init);
+}
+
+/****************************************
+ * OIM get SOAP request
+ * **************************************/
+/*oim SOAP server login error*/
+static void
+msn_oim_get_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+{
+ MsnSoapConn *soapconn = data;
+ MsnSession *session;
+
+ session = soapconn->session;
+ g_return_if_fail(session != NULL);
+ msn_soap_clean_unhandled_request(soapconn);
+
+// msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to OIM server"));
+}
+
+/*msn oim SOAP server connect process*/
+static void
+msn_oim_get_connect_cb(gpointer data, PurpleSslConnection *gsc,
+ PurpleInputCondition cond)
+{
+ MsnSoapConn *soapconn = data;
+ MsnSession * session;
+ MsnOim *oim;
+
+ oim = soapconn->parent;
+ g_return_if_fail(oim != NULL);
+
+ session = oim->session;
+ g_return_if_fail(session != NULL);
+
+ purple_debug_info("MSNP14","oim get SOAP Server connected!\n");
+}
+
+/* like purple_str_to_time, but different. The format of the timestamp
+ * is like this: 5 Sep 2007 21:42:12 -0700 */
+static time_t
+msn_oim_parse_timestamp(const char *timestamp)
+{
+ char month_str[4], tz_str[6];
+ char *tz_ptr = tz_str;
+ static const char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
+ };
+ struct tm t;
+ memset(&t, 0, sizeof(t));
+
+ if (sscanf(timestamp, "%02d %03s %04d %02d:%02d:%02d %05s",
+ &t.tm_mday, month_str, &t.tm_year,
+ &t.tm_hour, &t.tm_min, &t.tm_sec, tz_str) == 7) {
+ gboolean offset_positive = TRUE;
+ int tzhrs;
+ int tzmins;
+
+ for (t.tm_mon = 0;
+ months[t.tm_mon] != NULL &&
+ strcmp(months[t.tm_mon], month_str) != 0; t.tm_mon++);
+ if (months[t.tm_mon] != NULL) {
+ if (*tz_str == '-') {
+ offset_positive = FALSE;
+ tz_ptr++;
+ } else if (*tz_str == '+') {
+ tz_ptr++;
+ }
+
+ if (sscanf(tz_ptr, "%02d%02d", &tzhrs, &tzmins) == 2) {
+ time_t tzoff = tzhrs * 60 * 60 + tzmins * 60;
+#ifdef _WIN32
+ long sys_tzoff;
+#endif
+
+ if (!offset_positive)
+ tzoff *= -1;
+
+ t.tm_year -= 1900;
+ t.tm_isdst = 0;
+
+#ifdef _WIN32
+ if ((sys_tzoff = wpurple_get_tz_offset()) != -1)
+ tzoff += sys_tzoff;
+#else
+#ifdef HAVE_TM_GMTOFF
+ tzoff += t.tm_gmtoff;
+#else
+# ifdef HAVE_TIMEZONE
+ tzset(); /* making sure */
+ tzoff -= timezone;
+# endif
+#endif
+#endif /* _WIN32 */
+
+ return mktime(&t) + tzoff;
+ }
+ }
+ }
+
+ purple_debug_info("MSNP14:OIM", "Can't parse timestamp %s\n", timestamp);
+ return time(NULL);
+}
+
+/*Post the Offline Instant Message to User Conversation*/
+static void
+msn_oim_report_to_user(MsnOim *oim, const char *msg_str)
+{
+ MsnMessage *message;
+ char *date,*from,*decode_msg;
+ gsize body_len;
+ char **tokens;
+ char *start,*end;
+ int has_nick = 0;
+ char *passport_str, *passport;
+ char *msg_id;
+ time_t stamp;
+
+ message = msn_message_new(MSN_MSG_UNKNOWN);
+
+ msn_message_parse_payload(message, msg_str, strlen(msg_str),
+ MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM);
+ purple_debug_info("MSNP14","oim body:{%s}\n",message->body);
+ decode_msg = (char *)purple_base64_decode(message->body,&body_len);
+ date = (char *)g_hash_table_lookup(message->attr_table, "Date");
+ from = (char *)g_hash_table_lookup(message->attr_table, "From");
+ if(strstr(from," ")){
+ has_nick = 1;
+ }
+ if(has_nick){
+ tokens = g_strsplit(from , " " , 2);
+ passport_str = g_strdup(tokens[1]);
+ purple_debug_info("MSNP14","oim Date:{%s},nickname:{%s},tokens[1]:{%s} passport{%s}\n",
+ date,tokens[0],tokens[1],passport_str);
+ g_strfreev(tokens);
+ }else{
+ passport_str = g_strdup(from);
+ purple_debug_info("MSNP14","oim Date:{%s},passport{%s}\n",
+ date,passport_str);
+ }
+ start = strstr(passport_str,"<");
+ start += 1;
+ end = strstr(passport_str,">");
+ passport = g_strndup(start,end - start);
+ g_free(passport_str);
+ purple_debug_info("MSNP14","oim Date:{%s},passport{%s}\n",date,passport);
+
+ stamp = msn_oim_parse_timestamp(date);
+
+ serv_got_im(oim->session->account->gc, passport, decode_msg, 0, stamp);
+
+ /*Now get the oim message ID from the oim_list.
+ * and append to read list to prepare for deleting the Offline Message when sign out
+ */
+ if(oim->oim_list != NULL){
+ msg_id = oim->oim_list->data;
+ msn_oim_post_delete_msg(oim,msg_id);
+ oim->oim_list = g_list_remove(oim->oim_list, oim->oim_list->data);
+ g_free(msg_id);
+ }
+
+ g_free(passport);
+}
+
+/* Parse the XML data,
+ * prepare to report the OIM to user
+ */
+static void
+msn_oim_get_process(MsnOim *oim, const char *oim_msg)
+{
+ xmlnode *oim_node,*bodyNode,*responseNode,*msgNode;
+ char *msg_str;
+
+ oim_node = xmlnode_from_str(oim_msg, strlen(oim_msg));
+ bodyNode = xmlnode_get_child(oim_node,"Body");
+ responseNode = xmlnode_get_child(bodyNode,"GetMessageResponse");
+ msgNode = xmlnode_get_child(responseNode,"GetMessageResult");
+ msg_str = xmlnode_get_data(msgNode);
+ purple_debug_info("OIM","msg:{%s}\n",msg_str);
+ msn_oim_report_to_user(oim,msg_str);
+
+ g_free(msg_str);
+ xmlnode_free(oim_node);
+}
+
+static void
+msn_oim_get_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+ MsnOim * oim = soapconn->session->oim;
+
+ if (soapconn->body == NULL)
+ return;
+
+ purple_debug_info("MSNP14","OIM get read buffer:{%s}\n",soapconn->body);
+
+ /*we need to process the read message!*/
+ msn_oim_get_process(oim,soapconn->body);
+ msn_soap_free_read_buf(soapconn);
+
+ /*get next single Offline Message*/
+ msn_soap_post(soapconn,NULL,msn_oim_retrieve_connect_init);
+}
+
+static void
+msn_oim_get_written_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn = data;
+
+ soapconn->read_cb = msn_oim_get_read_cb;
+// msn_soap_read_cb(data,source,cond);
+}
+
+/* parse the oim XML data
+ * and post it to the soap server to get the Offline Message
+ * */
+void
+msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg)
+{
+ xmlnode *node, *mNode,*ENode,*INode,*rtNode,*nNode;
+ char *passport,*msgid,*nickname, *unread, *rTime = NULL;
+ MsnSession *session = oim->session;
+
+ purple_debug_info("MSNP14:OIM", "%s", xmlmsg);
+
+ node = xmlnode_from_str(xmlmsg, strlen(xmlmsg));
+ if (strcmp(node->name, "MD") != 0) {
+ xmlnode_free(node);
+ return;
+ }
+
+ ENode = xmlnode_get_child(node, "E");
+ INode = xmlnode_get_child(ENode, "IU");
+ unread = xmlnode_get_data(INode);
+
+ if (unread != NULL && purple_account_get_check_mail(session->account))
+ {
+ int count = atoi(unread);
+
+ if (count > 0)
+ {
+ const char *passport;
+ const char *url;
+
+ passport = msn_user_get_passport(session->user);
+ url = session->passport_info.file;
+
+ purple_notify_emails(session->account->gc, atoi(unread), FALSE, NULL, NULL,
+ &passport, &url, NULL, NULL);
+ }
+ }
+
+ for(mNode = xmlnode_get_child(node, "M"); mNode;
+ mNode = xmlnode_get_next_twin(mNode)){
+ /*email Node*/
+ ENode = xmlnode_get_child(mNode,"E");
+ passport = xmlnode_get_data(ENode);
+ /*Index */
+ INode = xmlnode_get_child(mNode,"I");
+ msgid = xmlnode_get_data(INode);
+ /*Nickname*/
+ nNode = xmlnode_get_child(mNode,"N");
+ nickname = xmlnode_get_data(nNode);
+ /*receive time*/
+ rtNode = xmlnode_get_child(mNode,"RT");
+ if(rtNode != NULL) {
+ rTime = xmlnode_get_data(rtNode);
+ rtNode = NULL;
+ }
+/* purple_debug_info("MSNP14","E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime); */
+
+ oim->oim_list = g_list_append(oim->oim_list,strdup(msgid));
+ msn_oim_post_single_get_msg(oim,msgid);
+ g_free(passport);
+ g_free(msgid);
+ g_free(rTime);
+ rTime = NULL;
+ g_free(nickname);
+ }
+ g_free(unread);
+ xmlnode_free(node);
+}
+
+/*Post to get the Offline Instant Message*/
+static void
+msn_oim_post_single_get_msg(MsnOim *oim,const char *msgid)
+{
+ MsnSoapReq *soap_request;
+ const char *soap_body,*t,*p;
+
+ purple_debug_info("MSNP14","Get single OIM Message\n");
+ t = oim->session->passport_info.t;
+ p = oim->session->passport_info.p;
+
+ soap_body = g_strdup_printf(MSN_OIM_GET_TEMPLATE,
+ t,
+ p,
+ msgid
+ );
+ soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST,
+ MSN_OIM_RETRIEVE_URL,
+ MSN_OIM_GET_SOAP_ACTION,
+ soap_body,
+ NULL,
+ msn_oim_get_read_cb,
+ msn_oim_get_written_cb);
+ msn_soap_post(oim->retrieveconn,soap_request,msn_oim_retrieve_connect_init);
+}
+
+/*msn oim retrieve server connect init */
+static void
+msn_oim_retrieve_connect_init(MsnSoapConn *soapconn)
+{
+ purple_debug_info("MSNP14","msn_oim_connect...\n");
+ msn_soap_init(soapconn,MSN_OIM_RETRIEVE_HOST,1,
+ msn_oim_get_connect_cb,
+ msn_oim_get_error_cb);
+}
+
+/*Msn OIM Send Server Connect Init Function*/
+static void
+msn_oim_send_connect_init(MsnSoapConn *sendconn)
+{
+ purple_debug_info("MSNP14","msn oim send connect init...\n");
+ msn_soap_init(sendconn,MSN_OIM_SEND_HOST,1,
+ msn_oim_send_connect_cb,
+ msn_oim_send_error_cb);
+}
+
+/*endof oim.c*/
diff --git a/libpurple/protocols/msn/oim.h b/libpurple/protocols/msn/oim.h
new file mode 100644
index 0000000000..2e4a3b96b8
--- /dev/null
+++ b/libpurple/protocols/msn/oim.h
@@ -0,0 +1,148 @@
+/**
+ * @file oim.h Header file for oim.c
+ * Author
+ * 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
+ */
+#ifndef _MSN_OIM_H_
+#define _MSN_OIM_H_
+
+/*OIM Retrieve SOAP Template*/
+#define MSN_OIM_RETRIEVE_HOST "rsi.hotmail.com"
+#define MSN_OIM_RETRIEVE_URL "/rsi/rsi.asmx"
+#define MSN_OIM_GET_SOAP_ACTION "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage"
+
+#define MSN_OIM_GET_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+ "<soap:Header>"\
+ "<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
+ "<t>%s</t>"\
+ "<p>%s</p>"\
+ "</PassportCookie>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<GetMessage xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
+ "<messageId>%s</messageId>"\
+ "<alsoMarkAsRead>false</alsoMarkAsRead>"\
+ "</GetMessage>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+/*OIM Delete SOAP Template*/
+#define MSN_OIM_DEL_SOAP_ACTION "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages"
+
+#define MSN_OIM_DEL_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+ "<soap:Header>"\
+ "<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
+ "<t>%s</t>"\
+ " <p>%s</p>"\
+ "</PassportCookie>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<DeleteMessages xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\
+ "<messageIds>"\
+ "<messageId>%s</messageId>"\
+ "</messageIds>"\
+ "</DeleteMessages>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+/*OIM Send SOAP Template*/
+#define MSN_OIM_MSG_TEMPLATE "MIME-Version: 1.0\n"\
+ "Content-Type: text/plain; charset=UTF-8\n"\
+ "Content-Transfer-Encoding: base64\n"\
+ "X-OIM-Message-Type: OfflineMessage\n"\
+ "X-OIM-Run-Id: {%s}\n"\
+ "X-OIM-Sequence-Num: %d\n\n"\
+ "%s"
+
+#define MSN_OIM_SEND_HOST "ows.messenger.msn.com"
+#define MSN_OIM_SEND_URL "/OimWS/oim.asmx"
+#define MSN_OIM_SEND_SOAP_ACTION "http://messenger.msn.com/ws/2004/09/oim/Store"
+#define MSN_OIM_SEND_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\
+"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\
+ "<soap:Header>"\
+ "<From memberName=\"%s\" friendlyName=\"%s\" xml:lang=\"en-US\" proxy=\"MSNMSGR\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\" msnpVer=\"MSNP14\" buildVer=\"8.0.0792\"/>"\
+ "<To memberName=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>"\
+ "<Ticket passport=\"%s\" appid=\"%s\" lockkey=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>"\
+ "<Sequence xmlns=\"http://schemas.xmlsoap.org/ws/2003/03/rm\">"\
+ "<Identifier xmlns=\"http://schemas.xmlsoap.org/ws/2002/07/utility\">http://messenger.msn.com</Identifier>"\
+ "<MessageNumber>%d</MessageNumber>"\
+ "</Sequence>"\
+ "</soap:Header>"\
+ "<soap:Body>"\
+ "<MessageType xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">text</MessageType>"\
+ "<Content xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\">%s</Content>"\
+ "</soap:Body>"\
+"</soap:Envelope>"
+
+typedef struct _MsnOimSendReq MsnOimSendReq;
+
+struct _MsnOimSendReq
+{
+ char *from_member;
+ char *friendname;
+ char *to_member;
+ char *oim_msg;
+ gint send_seq;
+};
+
+typedef struct _MsnOim MsnOim;
+
+struct _MsnOim
+{
+ MsnSession *session;
+
+ MsnSoapConn *retrieveconn;
+ GList * oim_list;
+
+ MsnSoapConn *sendconn;
+ char *challenge;
+ char *run_id;
+ gint send_seq;
+ GQueue *send_queue;
+};
+
+/****************************************************
+ * function prototype
+ * **************************************************/
+MsnOim * msn_oim_new(MsnSession *session);
+void msn_oim_destroy(MsnOim *oim);
+void msn_oim_connect(MsnOim *oim);
+
+void msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg);
+
+/*Send OIM Message*/
+void msn_oim_prep_send_msg_info(MsnOim *oim, const char *membername,
+ const char *friendname, const char *tomember,
+ const char * msg);
+
+void msn_oim_send_msg(MsnOim *oim);
+
+/*get the OIM message*/
+void msn_oim_get_msg(MsnOim *oim);
+
+/*report the oim message to the conversation*/
+void msn_oim_report_user(MsnOim *oim,const char *passport,char *msg);
+
+#endif/* _MSN_OIM_H_*/
+/*endof oim.h*/
diff --git a/libpurple/protocols/msn/servconn.c b/libpurple/protocols/msn/servconn.c
index 737c48cae3..2967ba5788 100644
--- a/libpurple/protocols/msn/servconn.c
+++ b/libpurple/protocols/msn/servconn.c
@@ -166,7 +166,7 @@ msn_servconn_got_error(MsnServConn *servconn, MsnServConnError error)
**************************************************************************/
static void
-connect_cb(gpointer data, gint source, const gchar *error_message)
+connect_cb(gpointer data, gint source, const char *error_message)
{
MsnServConn *servconn;
@@ -243,7 +243,9 @@ msn_servconn_connect(MsnServConn *servconn, const char *host, int port)
return TRUE;
}
else
+ {
return FALSE;
+ }
}
void
@@ -388,14 +390,21 @@ read_cb(gpointer data, gint source, PurpleInputCondition cond)
len = read(servconn->fd, buf, sizeof(buf) - 1);
- if (len < 0 && errno == EAGAIN)
- return;
- else if (len <= 0)
- {
- purple_debug_error("msn", "servconn read error, len: %d error: %s\n", len, strerror(errno));
- msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ);
+ if (len <= 0) {
+ switch (errno) {
- return;
+ case 0:
+
+ case EBADF:
+ case EAGAIN: return;
+
+ default: purple_debug_error("msn", "servconn read error,"
+ "len: %d, errno: %d, error: %s\n",
+ len, errno, strerror(errno));
+ msn_servconn_got_error(servconn,
+ MSN_SERVCONN_ERROR_READ);
+ return;
+ }
}
buf[len] = '\0';
@@ -444,6 +453,7 @@ read_cb(gpointer data, gint source, PurpleInputCondition cond)
else
{
msn_cmdproc_process_cmd_text(servconn->cmdproc, cur);
+ servconn->payload_len = servconn->cmdproc->last_cmd->payload_len;
}
} while (servconn->connected && !servconn->wasted && servconn->rx_len > 0);
diff --git a/libpurple/protocols/msn/session.c b/libpurple/protocols/msn/session.c
index 88e9baf1e0..8249a947c9 100644
--- a/libpurple/protocols/msn/session.c
+++ b/libpurple/protocols/msn/session.c
@@ -43,7 +43,9 @@ msn_session_new(PurpleAccount *account)
session->user = msn_user_new(session->userlist,
purple_account_get_username(account), NULL);
- session->protocol_ver = 9;
+ /*if you want to chat with Yahoo Messenger*/
+ //session->protocol_ver = WLM_YAHOO_PROT_VER;
+ session->protocol_ver = WLM_PROT_VER;
session->conv_seq = 1;
return session;
@@ -70,6 +72,8 @@ msn_session_destroy(MsnSession *session)
msn_userlist_destroy(session->userlist);
+ g_free(session->passport_info.t);
+ g_free(session->passport_info.p);
g_free(session->passport_info.kv);
g_free(session->passport_info.sid);
g_free(session->passport_info.mspauth);
@@ -87,6 +91,11 @@ msn_session_destroy(MsnSession *session)
if (session->nexus != NULL)
msn_nexus_destroy(session->nexus);
+ if (session->contact != NULL)
+ msn_contact_destroy(session->contact);
+ if (session->oim != NULL)
+ msn_oim_destroy(session->oim);
+
if (session->user != NULL)
msn_user_destroy(session->user);
@@ -154,6 +163,37 @@ msn_session_find_swboard(MsnSession *session, const char *username)
return NULL;
}
+static PurpleConversation *
+msn_session_get_conv(MsnSession *session,const char *passport)
+{
+ PurpleAccount *account;
+ PurpleConversation * conv;
+
+ g_return_val_if_fail(session != NULL, NULL);
+ account = session->account;
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
+ passport, account);
+ if(conv == NULL){
+ conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, passport);
+ }
+ return conv;
+}
+
+/* put Message to User Conversation
+ *
+ * passport - the one want to talk to you
+ */
+void
+msn_session_report_user(MsnSession *session,const char *passport,char *msg,PurpleMessageFlags flags)
+{
+ PurpleConversation * conv;
+
+ if ((conv = msn_session_get_conv(session,passport)) != NULL){
+ purple_conversation_write(conv, NULL, msg, flags, time(NULL));
+ }
+}
+
MsnSwitchBoard *
msn_session_find_swboard_with_conv(MsnSession *session, PurpleConversation *conv)
{
@@ -229,13 +269,14 @@ msn_session_sync_users(MsnSession *session)
/* The core used to use msn_add_buddy to add all buddies before
* being logged in. This no longer happens, so we manually iterate
- * over the whole buddy list to identify sync issues. */
-
- for (gnode = purple_blist_get_root(); gnode; gnode = gnode->next) {
+ * over the whole buddy list to identify sync issues.
+ */
+ for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next) {
PurpleGroup *group = (PurpleGroup *)gnode;
- const char *group_name = group->name;
+ const char *group_name;
if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
continue;
+ group_name = group->name;
for(cnode = gnode->child; cnode; cnode = cnode->next) {
if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
continue;
@@ -252,21 +293,17 @@ msn_session_sync_users(MsnSession *session)
if ((remote_user != NULL) && (remote_user->list_op & MSN_LIST_FL_OP))
{
- int group_id;
GList *l;
- group_id = msn_userlist_find_group_id(remote_user->userlist,
- group_name);
-
for (l = remote_user->group_ids; l != NULL; l = l->next)
{
- if (group_id == GPOINTER_TO_INT(l->data))
+ const char *name = msn_userlist_find_group_name(remote_user->userlist, l->data);
+ if (name && !g_strcasecmp(group_name, name))
{
found = TRUE;
break;
}
}
-
}
if (!found)
@@ -419,3 +456,4 @@ msn_session_finish_login(MsnSession *session)
msn_cmdproc_send(session->notification->cmdproc, "URL", "%s", "INBOX");
}
}
+
diff --git a/libpurple/protocols/msn/session.h b/libpurple/protocols/msn/session.h
index 6f7a2d4dc6..d2f37035a8 100644
--- a/libpurple/protocols/msn/session.h
+++ b/libpurple/protocols/msn/session.h
@@ -38,6 +38,8 @@ typedef struct _MsnSession MsnSession;
#include "cmdproc.h"
#include "nexus.h"
#include "httpconn.h"
+#include "contact.h"
+#include "oim.h"
#include "userlist.h"
#include "sync.h"
@@ -94,6 +96,8 @@ struct _MsnSession
MsnNotification *notification;
MsnNexus *nexus;
+ MsnContact *contact;
+ MsnOim *oim;
MsnSync *sync;
MsnUserList *userlist;
@@ -105,8 +109,15 @@ struct _MsnSession
int conv_seq; /**< The current conversation sequence number. */
+ /*psm info*/
+ char *psm;
+
struct
{
+ /*t and p, get via USR TWN*/
+ char *t;
+ char *p;
+
char *kv;
char *sid;
char *mspauth;
@@ -114,7 +125,6 @@ struct _MsnSession
char *file;
char *client_ip;
int client_port;
-
} passport_info;
};
@@ -224,4 +234,8 @@ void msn_session_set_login_step(MsnSession *session, MsnLoginStep step);
*/
void msn_session_finish_login(MsnSession *session);
+/*post message to User*/
+void msn_session_report_user(MsnSession *session,const char *passport,
+ char *msg,PurpleMessageFlags flags);
+
#endif /* _MSN_SESSION_H_ */
diff --git a/libpurple/protocols/msn/slp.c b/libpurple/protocols/msn/slp.c
index 37472abc18..f66f1c0085 100644
--- a/libpurple/protocols/msn/slp.c
+++ b/libpurple/protocols/msn/slp.c
@@ -33,6 +33,8 @@
/* ms to delay between sending buddy icon requests to the server. */
#define BUDDY_ICON_DELAY 20000
+/*debug SLP*/
+#define MSN_DEBUG_UD
static void send_ok(MsnSlpCall *slpcall, const char *branch,
const char *type, const char *content);
@@ -777,11 +779,11 @@ got_emoticon(MsnSlpCall *slpcall,
if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) {
/* FIXME: it would be better if we wrote the data as we received it
- instead of all at once, calling write multiple times and
- close once at the very end
- */
+ instead of all at once, calling write multiple times and
+ close once at the very end
+ */
purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
- purple_conv_custom_smiley_close(conv, slpcall->data_info);
+ purple_conv_custom_smiley_close(conv, slpcall->data_info );
}
#ifdef MSN_DEBUG_UD
purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
diff --git a/libpurple/protocols/msn/slpcall.c b/libpurple/protocols/msn/slpcall.c
index fc05340d2f..bab9af3622 100644
--- a/libpurple/protocols/msn/slpcall.c
+++ b/libpurple/protocols/msn/slpcall.c
@@ -22,6 +22,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "msn.h"
+#include "msnutils.h"
#include "slpcall.h"
#include "slpsession.h"
@@ -30,24 +31,6 @@
/* #define MSN_DEBUG_SLPCALL */
/**************************************************************************
- * Util
- **************************************************************************/
-
-static char *
-rand_guid()
-{
- return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
- rand() % 0xAAFF + 0x1111,
- rand() % 0xAAFF + 0x1111,
- rand() % 0xAAFF + 0x1111,
- rand() % 0xAAFF + 0x1111,
- rand() % 0xAAFF + 0x1111,
- rand() % 0xAAFF + 0x1111,
- rand() % 0xAAFF + 0x1111,
- rand() % 0xAAFF + 0x1111);
-}
-
-/**************************************************************************
* Main
**************************************************************************/
diff --git a/libpurple/protocols/msn/slplink.c b/libpurple/protocols/msn/slplink.c
index 0e4152effb..c74cc348c5 100644
--- a/libpurple/protocols/msn/slplink.c
+++ b/libpurple/protocols/msn/slplink.c
@@ -112,8 +112,10 @@ msn_slplink_destroy(MsnSlpLink *slplink)
if (slplink->remote_user != NULL)
g_free(slplink->remote_user);
+#if 0
if (slplink->directconn != NULL)
msn_directconn_destroy(slplink->directconn);
+#endif
while (slplink->slp_calls != NULL)
msn_slp_call_destroy(slplink->slp_calls->data);
@@ -244,11 +246,13 @@ msn_slplink_find_slp_call_with_session_id(MsnSlpLink *slplink, long id)
void
msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg)
{
+#if 0
if (slplink->directconn != NULL)
{
msn_directconn_send_msg(slplink->directconn, msg);
}
else
+#endif
{
if (slplink->swboard == NULL)
{
@@ -634,9 +638,10 @@ msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
MsnDirectConn *directconn;
directconn = slplink->directconn;
-
+#if 0
if (!directconn->acked)
msn_directconn_send_handshake(directconn);
+#endif
}
else if (slpmsg->flags == 0x0 || slpmsg->flags == 0x20 ||
slpmsg->flags == 0x1000030)
diff --git a/libpurple/protocols/msn/soap.c b/libpurple/protocols/msn/soap.c
new file mode 100644
index 0000000000..d013462db7
--- /dev/null
+++ b/libpurple/protocols/msn/soap.c
@@ -0,0 +1,732 @@
+/**
+ * @file soap.c
+ * SOAP connection related process
+ * Author
+ * 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 "soap.h"
+
+
+/*local function prototype*/
+void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step);
+
+/*setup the soap process step*/
+void
+msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step)
+{
+ soapconn->step = step;
+}
+
+//msn_soap_new(MsnSession *session,gpointer data,int sslconn)
+/*new a soap connection*/
+MsnSoapConn *
+msn_soap_new(MsnSession *session,gpointer data,int sslconn)
+{
+ MsnSoapConn *soapconn;
+
+ soapconn = g_new0(MsnSoapConn, 1);
+ soapconn->session = session;
+ soapconn->parent = data;
+ soapconn->ssl_conn = sslconn;
+
+ soapconn->gsc = NULL;
+ soapconn->input_handler = 0;
+ soapconn->output_handler = 0;
+
+ msn_soap_set_process_step(soapconn,MSN_SOAP_UNCONNECTED);
+ soapconn->soap_queue = g_queue_new();
+ return soapconn;
+}
+
+/*ssl soap connect callback*/
+void
+msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc,
+ PurpleInputCondition cond)
+{
+ MsnSoapConn * soapconn;
+ MsnSession *session;
+
+ purple_debug_misc("MSN SOAP","SOAP server connection established!\n");
+
+ soapconn = data;
+ g_return_if_fail(soapconn != NULL);
+
+ session = soapconn->session;
+ g_return_if_fail(session != NULL);
+
+ soapconn->gsc = gsc;
+
+ /*connection callback*/
+ if(soapconn->connect_cb != NULL){
+ soapconn->connect_cb(data,gsc,cond);
+ }
+
+ msn_soap_set_process_step(soapconn,MSN_SOAP_CONNECTED);
+ /*we do the SOAP request here*/
+ msn_soap_post_head_request(soapconn);
+}
+
+/*ssl soap error callback*/
+static void
+msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+{
+ MsnSoapConn * soapconn = data;
+
+ g_return_if_fail(data != NULL);
+ purple_debug_warning("MSN SOAP","Soap connection error!\n");
+ msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED);
+
+ /*error callback*/
+ if(soapconn->error_cb != NULL){
+ soapconn->error_cb(gsc,error,data);
+ }
+}
+
+/*init the soap connection*/
+void
+msn_soap_init(MsnSoapConn *soapconn,char * host,int ssl,
+ PurpleSslInputFunction connect_cb,
+ PurpleSslErrorFunction error_cb)
+{
+ purple_debug_misc("MSN SOAP","Initializing SOAP connection\n");
+ soapconn->login_host = g_strdup(host);
+ soapconn->ssl_conn = ssl;
+ soapconn->connect_cb = connect_cb;
+ soapconn->error_cb = error_cb;
+}
+
+/*connect the soap connection*/
+void
+msn_soap_connect(MsnSoapConn *soapconn)
+{
+ if(soapconn->ssl_conn){
+ purple_ssl_connect(soapconn->session->account, soapconn->login_host,
+ PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, msn_soap_error_cb,
+ soapconn);
+ }else{
+ }
+ msn_soap_set_process_step(soapconn,MSN_SOAP_CONNECTING);
+}
+
+/*close the soap connection*/
+void
+msn_soap_close(MsnSoapConn *soapconn)
+{
+ if(soapconn->ssl_conn){
+ if(soapconn->gsc != NULL){
+ purple_ssl_close(soapconn->gsc);
+ soapconn->gsc = NULL;
+ }
+ }else{
+ }
+ msn_soap_set_process_step(soapconn,MSN_SOAP_UNCONNECTED);
+}
+
+/*clean the unhandled SOAP request*/
+void
+msn_soap_clean_unhandled_request(MsnSoapConn *soapconn)
+{
+ MsnSoapReq *request;
+
+ g_return_if_fail(soapconn != NULL);
+
+ soapconn->body = NULL;
+
+ while ((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){
+ if (soapconn->read_cb) {
+ soapconn->read_cb(soapconn, -1, 0);
+ }
+ msn_soap_request_free(request);
+ }
+}
+
+/*destroy the soap connection*/
+void
+msn_soap_destroy(MsnSoapConn *soapconn)
+{
+ if(soapconn->login_host)
+ g_free(soapconn->login_host);
+
+ if(soapconn->login_path)
+ g_free(soapconn->login_path);
+
+ /*remove the write handler*/
+ if (soapconn->output_handler > 0){
+ purple_input_remove(soapconn->output_handler);
+ soapconn->output_handler = 0;
+ }
+ /*remove the read handler*/
+ if (soapconn->input_handler > 0){
+ purple_input_remove(soapconn->input_handler);
+ soapconn->input_handler = 0;
+ }
+ msn_soap_free_read_buf(soapconn);
+ msn_soap_free_write_buf(soapconn);
+
+ /*close ssl connection*/
+ msn_soap_close(soapconn);
+
+ /*process the unhandled soap request*/
+ msn_soap_clean_unhandled_request(soapconn);
+
+ g_queue_free(soapconn->soap_queue);
+ g_free(soapconn);
+}
+
+/*check the soap is connected?
+ * if connected return 1
+ */
+int
+msn_soap_connected(MsnSoapConn *soapconn)
+{
+ if(soapconn->ssl_conn){
+ return (soapconn->gsc == NULL? 0 : 1);
+ }
+ return(soapconn->fd>0? 1 : 0);
+}
+
+/*read and append the content to the buffer*/
+static gssize
+msn_soap_read(MsnSoapConn *soapconn)
+{
+ gssize len, requested_len;
+ char temp_buf[MSN_SOAP_READ_BUFF_SIZE];
+
+ if ( soapconn->need_to_read == 0 || soapconn->need_to_read > MSN_SOAP_READ_BUFF_SIZE) {
+ requested_len = MSN_SOAP_READ_BUFF_SIZE;
+ }
+ else {
+ requested_len = soapconn->need_to_read;
+ }
+
+ if ( soapconn->ssl_conn ) {
+ len = purple_ssl_read(soapconn->gsc, temp_buf, requested_len);
+ } else {
+ len = read(soapconn->fd, temp_buf, requested_len);
+ }
+
+
+ if ( len <= 0 ) {
+ switch (errno) {
+
+ case 0:
+ case EBADF: /* we are sometimes getting this in Windows */
+ case EAGAIN: return len;
+
+ default : purple_debug_error("MSN SOAP", "Read error!"
+ "read len: %d, error = %s\n",
+ len, strerror(errno));
+ purple_input_remove(soapconn->input_handler);
+ soapconn->input_handler = 0;
+ g_free(soapconn->read_buf);
+ soapconn->read_buf = NULL;
+ soapconn->read_len = 0;
+ /* TODO: error handling */
+ return len;
+ }
+ }
+ else {
+ soapconn->read_buf = g_realloc(soapconn->read_buf,
+ soapconn->read_len + len + 1);
+ if ( soapconn->read_buf != NULL ) {
+ memcpy(soapconn->read_buf + soapconn->read_len, temp_buf, len);
+ soapconn->read_len += len;
+ soapconn->read_buf[soapconn->read_len] = '\0';
+ }
+ else {
+ purple_debug_error("MSN SOAP", "Failure re-allocating %d bytes of memory!\n", soapconn->read_len + len + 1);
+ exit(EXIT_FAILURE);
+ }
+
+ }
+
+#if defined(MSN_SOAP_DEBUG)
+ if (len > 0)
+ purple_debug_info("MSN SOAP","Read %d bytes from SOAP server:\n%s\n", len, soapconn->read_buf + soapconn->read_len - len);
+#endif
+
+ return len;
+}
+
+/*read the whole SOAP server response*/
+void
+msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn *soapconn = data;
+ MsnSession *session;
+ int len;
+ char * body_start,*body_len;
+ char *length_start,*length_end;
+#ifdef MSN_SOAP_DEBUG
+#if !defined(_WIN32)
+ gchar * formattedxml = NULL;
+ gchar * http_headers = NULL;
+ xmlnode * node = NULL;
+#endif
+ purple_debug_misc("MSN SOAP", "msn_soap_read_cb()\n");
+#endif
+ session = soapconn->session;
+ g_return_if_fail(session != NULL);
+
+
+ /*read the request header*/
+ len = msn_soap_read(soapconn);
+
+ if ( len < 0 )
+ return;
+
+ if (soapconn->read_buf == NULL) {
+ return;
+ }
+
+ if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL)
+ || ( strstr(soapconn->read_buf, "HTTP/1.1 301") != NULL ) )
+ {
+ /* Redirect. */
+ char *location, *c;
+
+ purple_debug_info("MSN SOAP", "HTTP Redirect\n");
+ location = strstr(soapconn->read_buf, "Location: ");
+ if (location == NULL)
+ {
+ msn_soap_free_read_buf(soapconn);
+
+ return;
+ }
+ location = strchr(location, ' ') + 1;
+
+ if ((c = strchr(location, '\r')) != NULL)
+ *c = '\0';
+
+ /* Skip the http:// */
+ if ((c = strchr(location, '/')) != NULL)
+ location = c + 2;
+
+ if ((c = strchr(location, '/')) != NULL)
+ {
+ g_free(soapconn->login_path);
+ soapconn->login_path = g_strdup(c);
+
+ *c = '\0';
+ }
+
+ g_free(soapconn->login_host);
+ soapconn->login_host = g_strdup(location);
+
+ purple_ssl_connect(session->account, soapconn->login_host,
+ PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb,
+ msn_soap_error_cb, soapconn);
+ }
+ /* Another case of redirection, active on May, 2007
+ See http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener#Redirect
+ */
+ else if (strstr(soapconn->read_buf,
+ "<faultcode>psf:Redirect</faultcode>") != NULL)
+ {
+ char *location, *c;
+
+ location = strstr(soapconn->read_buf, "<psf:redirectUrl>");
+ /* Omit the tag preceding the URL */
+ location += strlen("<psf:redirectUrl>");
+ location = strstr(location, ":/");
+ if (location == NULL)
+ {
+ msn_soap_free_read_buf(soapconn);
+ return;
+ }
+
+ location += strlen("://"); /* Skip http:// or https:// */
+
+ if ( (c = strstr(location, "</psf:redirectUrl>")) != NULL )
+ *c = '\0';
+
+ if ( (c = strstr(location, "/")) != NULL )
+ {
+ g_free(soapconn->login_path);
+ soapconn->login_path = g_strdup(c);
+ *c = '\0';
+ }
+
+ g_free(soapconn->login_host);
+ soapconn->login_host = g_strdup(location);
+
+ purple_ssl_connect(session->account, soapconn->login_host,
+ PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb,
+ msn_soap_error_cb, soapconn);
+ }
+ else if (strstr(soapconn->read_buf, "HTTP/1.1 401 Unauthorized") != NULL)
+ {
+ const char *error;
+
+ purple_debug_error("MSN SOAP", "Received HTTP error 401 Unauthorized\n");
+ if ((error = strstr(soapconn->read_buf, "WWW-Authenticate")) != NULL)
+ {
+ if ((error = strstr(error, "cbtxt=")) != NULL)
+ {
+ const char *c;
+ char *temp;
+
+ error += strlen("cbtxt=");
+
+ if ((c = strchr(error, '\n')) == NULL)
+ c = error + strlen(error);
+
+ temp = g_strndup(error, c - error);
+ error = purple_url_decode(temp);
+ g_free(temp);
+ }
+ }
+
+ msn_session_set_error(session, MSN_ERROR_AUTH, error);
+ }
+ /* Handle Passport 3.0 authentication failures.
+ * Further info: http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
+ */
+ else if (strstr(soapconn->read_buf,
+ "<faultcode>wsse:FailedAuthentication</faultcode>") != NULL)
+ {
+ char *faultstring;
+
+ faultstring = strstr(soapconn->read_buf, "<faultstring>");
+
+ if (faultstring != NULL)
+ {
+ faultstring += strlen("<faultstring>");
+ *strstr(soapconn->read_buf, "</faultstring>") = '\0';
+ }
+
+ msn_session_set_error(session, MSN_ERROR_AUTH, faultstring);
+ }
+ else if (strstr(soapconn->read_buf, "HTTP/1.1 503 Service Unavailable"))
+ {
+ msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL);
+ }
+ else if ((strstr(soapconn->read_buf, "HTTP/1.1 200 OK"))
+ ||(strstr(soapconn->read_buf, "HTTP/1.1 500")))
+ {
+ /*OK! process the SOAP body*/
+ body_start = (char *)g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n");
+ if (!body_start) {
+ return;
+ }
+ body_start += 4;
+
+ // purple_debug_misc("msn", "Soap Read: {%s}\n", soapconn->read_buf);
+
+ /* we read the content-length*/
+ length_start = strstr(soapconn->read_buf, "Content-Length: ");
+ length_start += strlen("Content-Length: ");
+ length_end = strstr(length_start, "\r\n");
+ body_len = g_strndup(length_start, length_end - length_start);
+
+ /*setup the conn body */
+ soapconn->body = body_start;
+ soapconn->body_len = atoi(body_len);
+ g_free(body_len);
+#ifdef MSN_SOAP_DEBUG
+ purple_debug_misc("MSN SOAP","SOAP bytes read so far: %d, Content-Length: %d\n", soapconn->read_len, soapconn->body_len);
+#endif
+ soapconn->need_to_read = (body_start - soapconn->read_buf + soapconn->body_len) - soapconn->read_len;
+ if ( soapconn->need_to_read > 0 ) {
+ return;
+ }
+
+#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32)
+
+ node = xmlnode_from_str(soapconn->body, soapconn->body_len);
+
+ if (node != NULL) {
+ formattedxml = xmlnode_to_formatted_str(node, NULL);
+ http_headers = g_strndup(soapconn->read_buf, soapconn->body - soapconn->read_buf);
+
+ purple_debug_info("MSN SOAP","Data with XML payload received from the SOAP server:\n%s%s\n", http_headers, formattedxml);
+ g_free(http_headers);
+ g_free(formattedxml);
+ xmlnode_free(node);
+ }
+ else
+ purple_debug_info("MSN SOAP","Data received from the SOAP server:\n%s\n", soapconn->read_buf);
+#endif
+
+ /*remove the read handler*/
+ purple_input_remove(soapconn->input_handler);
+ soapconn->input_handler = 0;
+ /*
+ * close the soap connection,if more soap request came,
+ * Just reconnect to do it,
+ *
+ * To solve the problem described below:
+ * When I post the soap request in one socket one after the other,
+ * The first read is ok, But the second soap read always got 0 bytes,
+ * Weird!
+ * */
+ msn_soap_close(soapconn);
+
+ /*call the read callback*/
+ if ( soapconn->read_cb != NULL ) {
+ soapconn->read_cb(soapconn, source, 0);
+ }
+ }
+ return;
+}
+
+void
+msn_soap_free_read_buf(MsnSoapConn *soapconn)
+{
+ g_return_if_fail(soapconn != NULL);
+
+ if (soapconn->read_buf) {
+ g_free(soapconn->read_buf);
+ }
+ soapconn->read_buf = NULL;
+ soapconn->read_len = 0;
+ soapconn->need_to_read = 0;
+}
+
+void
+msn_soap_free_write_buf(MsnSoapConn *soapconn)
+{
+ g_return_if_fail(soapconn != NULL);
+
+ if (soapconn->write_buf) {
+ g_free(soapconn->write_buf);
+ }
+ soapconn->write_buf = NULL;
+ soapconn->written_len = 0;
+}
+
+/*Soap write process func*/
+static void
+msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+ MsnSoapConn *soapconn = data;
+ int len, total_len;
+
+ g_return_if_fail(soapconn != NULL);
+ if ( soapconn->write_buf == NULL ) {
+ purple_debug_error("MSN SOAP","SOAP buffer is NULL\n");
+ purple_input_remove(soapconn->output_handler);
+ soapconn->output_handler = -1;
+ return;
+ }
+ total_len = strlen(soapconn->write_buf);
+
+ /*
+ * write the content to SSL server,
+ */
+ len = purple_ssl_write(soapconn->gsc,
+ soapconn->write_buf + soapconn->written_len,
+ total_len - soapconn->written_len);
+
+ if (len < 0 && errno == EAGAIN)
+ return;
+ else if (len <= 0){
+ /*SSL write error!*/
+ purple_input_remove(soapconn->output_handler);
+ soapconn->output_handler = -1;
+ /* TODO: notify of the error */
+ purple_debug_error("MSN SOAP","Error writing to SSL connection!\n");
+ return;
+ }
+ soapconn->written_len += len;
+
+ if (soapconn->written_len < total_len)
+ return;
+
+ purple_input_remove(soapconn->output_handler);
+ soapconn->output_handler = -1;
+
+ /*clear the write buff*/
+ msn_soap_free_write_buf(soapconn);
+
+ /* Write finish!
+ * callback for write done
+ */
+ if(soapconn->written_cb != NULL){
+ soapconn->written_cb(soapconn, source, 0);
+ }
+ /*maybe we need to read the input?*/
+ if (soapconn->input_handler == 0) {
+ soapconn->input_handler = purple_input_add(soapconn->gsc->fd,
+ PURPLE_INPUT_READ, msn_soap_read_cb, soapconn);
+ }
+// msn_soap_read_cb(soapconn,source,0);
+}
+
+/*write the buffer to SOAP connection*/
+void
+msn_soap_write(MsnSoapConn * soapconn, char *write_buf, PurpleInputFunction written_cb)
+{
+ soapconn->write_buf = write_buf;
+ soapconn->written_len = 0;
+ soapconn->written_cb = written_cb;
+
+ msn_soap_free_read_buf(soapconn);
+
+ /*clear the read buffer first*/
+ /*start the write*/
+ soapconn->output_handler = purple_input_add(soapconn->gsc->fd, PURPLE_INPUT_WRITE,
+ msn_soap_write_cb, soapconn);
+ msn_soap_write_cb(soapconn, soapconn->gsc->fd, PURPLE_INPUT_WRITE);
+}
+
+/* New a soap request*/
+MsnSoapReq *
+msn_soap_request_new(const char *host,const char *post_url,const char *soap_action,
+ const char *body, const gpointer data_cb,
+ PurpleInputFunction read_cb,PurpleInputFunction written_cb)
+{
+ MsnSoapReq *request;
+
+ request = g_new0(MsnSoapReq, 1);
+ request->id = 0;
+
+ request->login_host = g_strdup(host);
+ request->login_path = g_strdup(post_url);
+ request->soap_action = g_strdup(soap_action);
+ request->body = g_strdup(body);
+ request->data_cb = data_cb;
+ request->read_cb = read_cb;
+ request->written_cb = written_cb;
+
+ return request;
+}
+
+/*free a soap request*/
+void
+msn_soap_request_free(MsnSoapReq *request)
+{
+ g_return_if_fail(request != NULL);
+
+ g_free(request->login_host);
+ g_free(request->login_path);
+ g_free(request->soap_action);
+ g_free(request->body);
+ request->read_cb = NULL;
+ request->written_cb = NULL;
+
+ g_free(request);
+}
+
+/*post the soap request queue's head request*/
+void
+msn_soap_post_head_request(MsnSoapConn *soapconn)
+{
+ purple_debug_info("MSN SOAP", "Posting new request from head of the queue\n");
+
+ g_return_if_fail(soapconn->soap_queue != NULL);
+
+ if(!g_queue_is_empty(soapconn->soap_queue)){
+ MsnSoapReq *request;
+ if((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){
+ msn_soap_post_request(soapconn,request);
+ }
+ } else {
+ purple_debug_info("MSN SOAP", "No requests to process found.\n");
+ msn_soap_set_process_step(soapconn,MSN_SOAP_CONNECTED_IDLE);
+ }
+}
+
+/*post the soap request ,
+ * if not connected, Connected first.
+ */
+void
+msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request,
+ MsnSoapConnectInitFunction msn_soap_init_func)
+{
+ if (request != NULL) {
+ g_queue_push_tail(soapconn->soap_queue, request);
+ }
+ if (!msn_soap_connected(soapconn) && (soapconn->step == MSN_SOAP_UNCONNECTED)
+ &&(!g_queue_is_empty(soapconn->soap_queue))) {
+ /*not connected?and we have something to process connect it first*/
+ purple_debug_misc("MSN SOAP","No connection to SOAP server. Connecting...\n");
+ msn_soap_init_func(soapconn);
+ msn_soap_connect(soapconn);
+ return;
+ }
+ purple_debug_misc("MSN SOAP","Connected to SOAP server\n");
+
+ /*if connected, what we only needed to do is to queue the request,
+ * when SOAP request in the queue processed done, will do this command.
+ * we just waiting...
+ * If we send the request this time,error may occure
+ */
+ if (soapconn->step == MSN_SOAP_CONNECTED_IDLE){
+ msn_soap_post_head_request(soapconn);
+ }
+}
+
+/*Post the soap request action*/
+void
+msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request)
+{
+ char * soap_head = NULL;
+ char * request_str = NULL;
+#ifdef MSN_SOAP_DEBUG
+#if !defined(_WIN32)
+ xmlnode * node;
+#endif
+ purple_debug_misc("MSN SOAP","msn_soap_post_request()\n");
+#endif
+
+ msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING);
+ soap_head = g_strdup_printf(
+ "POST %s HTTP/1.1\r\n"
+ "SOAPAction: %s\r\n"
+ "Content-Type:text/xml; charset=utf-8\r\n"
+ "Cookie: MSPAuth=%s\r\n"
+ "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n"
+ "Accept: */*\r\n"
+ "Host: %s\r\n"
+ "Content-Length: %" G_GSIZE_FORMAT "\r\n"
+ "Connection: Keep-Alive\r\n"
+ "Cache-Control: no-cache\r\n\r\n",
+ request->login_path,
+ request->soap_action,
+ soapconn->session->passport_info.mspauth,
+ request->login_host,
+ strlen(request->body)
+ );
+ request_str = g_strdup_printf("%s%s", soap_head, request->body);
+
+#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32)
+ node = xmlnode_from_str(request->body, -1);
+ if (node != NULL) {
+ char *formattedstr = xmlnode_to_formatted_str(node, NULL);
+ purple_debug_info("MSN SOAP","Posting request to SOAP server:\n%s%s\n",soap_head, formattedstr);
+ g_free(formattedstr);
+ xmlnode_free(node);
+ }
+ else
+ purple_debug_info("MSN SOAP","Failed to parse SOAP request being sent:\n%s\n", request_str);
+#endif
+
+ g_free(soap_head);
+ /*free read buffer*/
+ // msn_soap_free_read_buf(soapconn);
+ /*post it to server*/
+ soapconn->data_cb = request->data_cb;
+ msn_soap_write(soapconn, request_str, request->written_cb);
+}
+
diff --git a/libpurple/protocols/msn/soap.h b/libpurple/protocols/msn/soap.h
new file mode 100644
index 0000000000..373a12809e
--- /dev/null
+++ b/libpurple/protocols/msn/soap.h
@@ -0,0 +1,155 @@
+/**
+ * @file soap.h
+ * header file for SOAP connection related process
+ * Author
+ * 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
+ */
+#ifndef _MSN_SOAP_H_
+#define _MSN_SOAP_H_
+
+#define MSN_SOAP_READ_BUFF_SIZE 8192
+
+/* define this to debug the communications with the SOAP server */
+/* #define MSN_SOAP_DEBUG */
+
+
+typedef enum
+{
+ MSN_SOAP_UNCONNECTED,
+ MSN_SOAP_CONNECTING,
+ MSN_SOAP_CONNECTED,
+ MSN_SOAP_PROCESSING,
+ MSN_SOAP_CONNECTED_IDLE
+}MsnSoapStep;
+
+/*MSN SoapRequest structure*/
+typedef struct _MsnSoapReq MsnSoapReq;
+
+/*MSN Https connection structure*/
+typedef struct _MsnSoapConn MsnSoapConn;
+
+typedef void (*MsnSoapConnectInitFunction)(MsnSoapConn *);
+
+
+struct _MsnSoapReq{
+ /*request sequence*/
+ int id;
+
+ char *login_host;
+ char *login_path;
+ char *soap_action;
+
+ char *body;
+
+ gpointer data_cb;
+ PurpleInputFunction read_cb;
+ PurpleInputFunction written_cb;
+};
+
+struct _MsnSoapConn{
+ MsnSession *session;
+ gpointer parent;
+
+ char *login_host;
+ char *login_path;
+ char *soap_action;
+
+ MsnSoapStep step;
+ /*ssl connection?*/
+ guint ssl_conn;
+ /*normal connection*/
+ guint fd;
+ /*SSL connection*/
+ PurpleSslConnection *gsc;
+ /*ssl connection callback*/
+ PurpleSslInputFunction connect_cb;
+ /*ssl error callback*/
+ PurpleSslErrorFunction error_cb;
+
+ /*read handler*/
+ guint input_handler;
+ /*write handler*/
+ guint output_handler;
+
+ /*Queue of SOAP request to send*/
+ int soap_id;
+ GQueue *soap_queue;
+
+ /*write buffer*/
+ char *write_buf;
+ gsize written_len;
+ PurpleInputFunction written_cb;
+
+ /*read buffer*/
+ char *read_buf;
+ gsize read_len;
+ gsize need_to_read;
+ PurpleInputFunction read_cb;
+
+ gpointer data_cb;
+
+ /*HTTP reply body part*/
+ char *body;
+ int body_len;
+};
+
+
+/*Function Prototype*/
+/*Soap Request Function */
+MsnSoapReq *msn_soap_request_new(const char *host, const char *post_url,
+ const char *soap_action, const char *body,
+ const gpointer data_cb,
+ PurpleInputFunction read_cb,
+ PurpleInputFunction written_cb);
+
+void msn_soap_request_free(MsnSoapReq *request);
+void msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request);
+void msn_soap_post_head_request(MsnSoapConn *soapconn);
+
+/*new a soap conneciton */
+MsnSoapConn *msn_soap_new(MsnSession *session,gpointer data,int sslconn);
+
+/*destroy */
+void msn_soap_destroy(MsnSoapConn *soapconn);
+
+/*init a soap conneciton */
+void msn_soap_init(MsnSoapConn *soapconn,char * host,int ssl,PurpleSslInputFunction connect_cb,PurpleSslErrorFunction error_cb);
+void msn_soap_connect(MsnSoapConn *soapconn);
+void msn_soap_close(MsnSoapConn *soapconn);
+
+/*write to soap*/
+void msn_soap_write(MsnSoapConn * soapconn, char *write_buf, PurpleInputFunction written_cb);
+void msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request,MsnSoapConnectInitFunction msn_soap_init_func);
+
+void msn_soap_free_read_buf(MsnSoapConn *soapconn);
+void msn_soap_free_write_buf(MsnSoapConn *soapconn);
+void msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
+void msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond);
+
+/*clean the unhandled request*/
+void msn_soap_clean_unhandled_request(MsnSoapConn *soapconn);
+
+/*check if the soap connection is connected*/
+int msn_soap_connected(MsnSoapConn *soapconn);
+
+#endif/*_MSN_SOAP_H_*/
+
diff --git a/libpurple/protocols/msn/state.c b/libpurple/protocols/msn/state.c
index a02486868a..c7412d9718 100644
--- a/libpurple/protocols/msn/state.c
+++ b/libpurple/protocols/msn/state.c
@@ -38,6 +38,197 @@ static const char *away_text[] =
N_("Available")
};
+/* Local Function Prototype*/
+static char *msn_build_psm(const char *psmstr,const char *mediastr,
+ const char *guidstr);
+
+/*
+ * WLM media PSM info build prcedure
+ *
+ * Result can like:
+ * <CurrentMedia>\0Music\01\0{0} - {1}\0Song Title\0Song Artist\0Song Album\0\0</CurrentMedia>\
+ * <CurrentMedia>\0Games\01\0Playing {0}\0Game Name\0</CurrentMedia>\
+ * <CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia>"
+ */
+static char *
+msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr)
+{
+ xmlnode *dataNode,*psmNode,*mediaNode,*guidNode;
+ char *result;
+ int length;
+
+ dataNode = xmlnode_new("Data");
+
+ psmNode = xmlnode_new("PSM");
+ if(psmstr != NULL){
+ xmlnode_insert_data(psmNode,psmstr,strlen(psmstr));
+ }
+ xmlnode_insert_child(dataNode,psmNode);
+
+ mediaNode = xmlnode_new("CurrentMedia");
+ if(mediastr != NULL){
+ xmlnode_insert_data(psmNode,mediastr,strlen(mediastr));
+ }
+ xmlnode_insert_child(dataNode,mediaNode);
+
+ guidNode = xmlnode_new("MachineGuid");
+ if(guidstr != NULL){
+ xmlnode_insert_data(guidNode,guidstr,strlen(guidstr));
+ }
+ xmlnode_insert_child(dataNode,guidNode);
+
+ result = xmlnode_to_str(dataNode,&length);
+ xmlnode_free(dataNode);
+ return result;
+}
+
+/* parse CurrentMedia string */
+char *
+msn_parse_currentmedia(const char *cmedia)
+{
+ char **cmedia_array;
+ GString *buffer = NULL;
+ int strings;
+
+ if ((cmedia == NULL) || (*cmedia == '\0')) {
+ purple_debug_info("msn", "No currentmedia string\n");
+ return NULL;
+ }
+
+ purple_debug_info("msn", "Parsing currentmedia string: \"%s\"\n", cmedia);
+
+ cmedia_array = g_strsplit(cmedia, "\\0", 0);
+
+ strings = 0;
+ /* Yes, we want to skip the first element here, as it is empty due to
+ * the cmedia string starting with \0 -- see the examples below.
+ while (cmedia_array[++strings] != NULL);
+
+ /* The cmedia_array[2] field contains a 1 if enabled. */
+ if ((strings > 3) && (!strcmp(cmedia_array[2], "1"))) {
+ char *inptr = cmedia_array[3];
+
+ buffer = g_string_new(NULL);
+
+ while (*inptr != '\0') {
+ if ((*inptr == '{') && ((*(inptr + 1) != '\0') && (*(inptr+2) == '}')) {
+ char *tmpptr;
+ int tmp;
+
+ errno = 0;
+ tmp = strtol(inptr + 1, &tmpptr, 10);
+
+ if (errno == 0 && tmpptr != inptr + 1 &&
+ tmp + 4 < strings) {
+ /* Replace {?} tag with appropriate text only when successful.
+ * Skip otherwise. */
+ buffer = g_string_append(buffer, cmedia_array[tmp + 4]);
+ }
+ inptr += 3; /* Skip to the next char after '}' */
+ } else {
+ buffer = g_string_append_c(buffer, *inptr++);
+ }
+ }
+ purple_debug_info("msn", "Parsed currentmedia string, result: \"%s\"\n",
+ buffer->str);
+ } else {
+ purple_debug_info("msn", "Current media marked disabled, not parsing.\n");
+ }
+
+ g_strfreev(cmedia_array);
+ return buffer ? g_string_free(buffer, FALSE) : NULL;
+}
+
+/* get the CurrentMedia info from the XML string */
+char *
+msn_get_currentmedia(char *xml_str, gsize len)
+{
+ xmlnode *payloadNode, *currentmediaNode;
+ char *currentmedia;
+
+ purple_debug_info("msn","msn get CurrentMedia\n");
+ payloadNode = xmlnode_from_str(xml_str, len);
+ if (!payloadNode){
+ purple_debug_error("msn","PSM XML parse Error!\n");
+ return NULL;
+ }
+ currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia");
+ if (currentmediaNode == NULL){
+ purple_debug_info("msn","No CurrentMedia Node");
+ xmlnode_free(payloadNode);
+ return NULL;
+ }
+ currentmedia = xmlnode_get_data(currentmediaNode);
+
+ xmlnode_free(payloadNode);
+
+ return currentmedia;
+}
+
+/*get the PSM info from the XML string*/
+char *
+msn_get_psm(char *xml_str, gsize len)
+{
+ xmlnode *payloadNode, *psmNode;
+ char *psm;
+
+ purple_debug_info("MSNP14","msn get PSM\n");
+ payloadNode = xmlnode_from_str(xml_str, len);
+ if (!payloadNode){
+ purple_debug_error("MSNP14","PSM XML parse Error!\n");
+ return NULL;
+ }
+ psmNode = xmlnode_get_child(payloadNode, "PSM");
+ if (psmNode == NULL){
+ purple_debug_info("MSNP14","No PSM status Node");
+ xmlnode_free(payloadNode);
+ return NULL;
+ }
+ psm = xmlnode_get_data(psmNode);
+
+ xmlnode_free(payloadNode);
+
+ return psm;
+}
+
+/* set the MSN's PSM info,Currently Read from the status Line
+ * Thanks for Cris Code
+ */
+void
+msn_set_psm(MsnSession *session)
+{
+ PurpleAccount *account = session->account;
+ PurplePresence *presence;
+ PurpleStatus *status;
+ MsnCmdProc *cmdproc;
+ MsnTransaction *trans;
+ char *payload;
+ const char *statusline;
+ gchar *unescapedstatusline;
+
+ g_return_if_fail(session != NULL);
+ g_return_if_fail(session->notification != NULL);
+
+ cmdproc = session->notification->cmdproc;
+ /*prepare PSM info*/
+ if(session->psm){
+ g_free(session->psm);
+ }
+ /*Get the PSM string from Purple's Status Line*/
+ presence = purple_account_get_presence(account);
+ status = purple_presence_get_active_status(presence);
+ statusline = purple_status_get_attr_string(status, "message");
+ unescapedstatusline = purple_unescape_html(statusline);
+ session->psm = msn_build_psm(unescapedstatusline, NULL, NULL);
+ g_free(unescapedstatusline);
+ payload = session->psm;
+
+ purple_debug_misc("MSNP14","Sending UUX command with payload: %s\n",payload);
+ trans = msn_transaction_new(cmdproc, "UUX","%d",strlen(payload));
+ msn_transaction_set_payload(trans, payload, strlen(payload));
+ msn_cmdproc_send_trans(cmdproc, trans);
+}
+
void
msn_change_status(MsnSession *session)
{
@@ -79,6 +270,7 @@ msn_change_status(MsnSession *session)
g_free(msnobj_str);
}
+ msn_set_psm(session);
}
const char *
diff --git a/libpurple/protocols/msn/state.h b/libpurple/protocols/msn/state.h
index 641b22222b..c1da19bc74 100644
--- a/libpurple/protocols/msn/state.h
+++ b/libpurple/protocols/msn/state.h
@@ -59,6 +59,17 @@ const char *msn_away_get_text(MsnAwayType type);
const char *msn_state_get_text(MsnAwayType state);
+void msn_set_psm(MsnSession *session);
+
+/* Parse CurrentMedia string */
+char * msn_parse_currentmedia(const char *cmedia);
+
+/* Get the CurrentMedia info from the XML string */
+char * msn_get_currentmedia(char *xml_str,gsize len);
+
+/*get the PSM info from the XML string*/
+char * msn_get_psm(char *xml_str,gsize len);
+
MsnAwayType msn_state_from_account(PurpleAccount *account);
#endif /* _MSN_STATE_H_ */
diff --git a/libpurple/protocols/msn/switchboard.c b/libpurple/protocols/msn/switchboard.c
index 248203b106..75104f8720 100644
--- a/libpurple/protocols/msn/switchboard.c
+++ b/libpurple/protocols/msn/switchboard.c
@@ -25,7 +25,7 @@
#include "prefs.h"
#include "switchboard.h"
#include "notification.h"
-#include "msn-utils.h"
+#include "msnutils.h"
#include "error.h"
@@ -534,6 +534,7 @@ release_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
payload = msn_message_gen_payload(msg, &payload_len);
#ifdef MSN_DEBUG_SB
+ purple_debug_info("MSNP14","SB length:{%d}",payload_len);
msn_message_show_readable(msg, "SB SEND", FALSE);
#endif
@@ -621,6 +622,7 @@ msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
g_return_if_fail(swboard != NULL);
g_return_if_fail(msg != NULL);
+ purple_debug_info("MSNP14","switchboard send msg..\n");
if (msn_switchboard_can_send(swboard))
release_msg(swboard, msg);
else if (queue)
@@ -727,7 +729,8 @@ msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
msg = msn_message_new_from_cmd(cmdproc->session, cmd);
- msn_message_parse_payload(msg, payload, len);
+ msn_message_parse_payload(msg, payload, len,
+ MSG_LINE_DEM,MSG_BODY_DEM);
#ifdef MSN_DEBUG_SB
msn_message_show_readable(msg, "SB RECV", FALSE);
#endif
@@ -749,6 +752,14 @@ msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
}
static void
+ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ purple_debug_misc("MSNP14","get UBM...\n");
+ cmdproc->servconn->payload_len = atoi(cmd->params[4]);
+ cmdproc->last_cmd->payload_cb = msg_cmd_post;
+}
+
+static void
nak_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnMessage *msg;
@@ -1100,6 +1111,8 @@ static void
cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
{
int reason = MSN_SB_ERROR_UNKNOWN;
+ MsnMessage *msg;
+ MsnSwitchBoard *swboard = trans->data;
if (error == 215)
{
@@ -1112,7 +1125,19 @@ cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
}
purple_debug_warning("msn", "cal_error: command %s gave error %i\n", trans->command, error);
+ purple_debug_warning("msn", "Will Use Offline Message to sendit\n");
+
+// cal_error_helper(trans, reason);
+ /*offline Message send Process*/
+ while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL){
+ purple_debug_warning("MSNP14","offline msg to send:{%s}\n",msg->body);
+ /* The messages could not be sent due to a switchboard error */
+ swboard->error = MSN_SB_ERROR_USER_OFFLINE;
+ msg_error_helper(swboard->cmdproc, msg,
+ MSN_MSG_ERROR_SB);
+ msn_message_unref(msg);
+ }
cal_error_helper(trans, reason);
}
@@ -1156,6 +1181,7 @@ got_swboard(MsnCmdProc *cmdproc, MsnCommand *cmd)
/* The conversation window was closed. */
return;
+ purple_debug_info("MSNP14","Switchboard:auth:{%s} socket:{%s}\n",cmd->params[4],cmd->params[2]);
msn_switchboard_set_auth_key(swboard, cmd->params[4]);
msn_parse_socket(cmd->params[2], &host, &port);
@@ -1268,6 +1294,7 @@ msn_switchboard_init(void)
msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd);
msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "UBM", ubm_cmd);
msn_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd);
msn_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd);
msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd);
diff --git a/libpurple/protocols/msn/sync.c b/libpurple/protocols/msn/sync.c
index 2b24e10156..a8faa57716 100644
--- a/libpurple/protocols/msn/sync.c
+++ b/libpurple/protocols/msn/sync.c
@@ -90,9 +90,9 @@ lsg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session = cmdproc->session;
const char *name;
- int group_id;
+ const char *group_id;
- group_id = atoi(cmd->params[0]);
+ group_id = cmd->params[0];
name = purple_url_decode(cmd->params[1]);
msn_group_new(session->userlist, group_id, name);
@@ -156,10 +156,10 @@ lst_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
for (c = tokens; *c != NULL; c++)
{
- int id;
+ char *id;
- id = atoi(*c);
- group_ids = g_slist_append(group_ids, GINT_TO_POINTER(id));
+ id = *c;
+ group_ids = g_slist_append(group_ids, g_strdup(id));
}
g_strfreev(tokens);
diff --git a/libpurple/protocols/msn/user.c b/libpurple/protocols/msn/user.c
index 4c4b180eb3..5bbca698b0 100644
--- a/libpurple/protocols/msn/user.c
+++ b/libpurple/protocols/msn/user.c
@@ -25,6 +25,7 @@
#include "user.h"
#include "slp.h"
+/*new a user object*/
MsnUser *
msn_user_new(MsnUserList *userlist, const char *passport,
const char *store_name)
@@ -50,6 +51,7 @@ msn_user_new(MsnUserList *userlist, const char *passport,
return user;
}
+/*destroy a user object*/
void
msn_user_destroy(MsnUser *user)
{
@@ -59,7 +61,14 @@ msn_user_destroy(MsnUser *user)
g_hash_table_destroy(user->clientcaps);
if (user->group_ids != NULL)
+ {
+ GList *l;
+ for (l = user->group_ids; l != NULL; l = l->next)
+ {
+ g_free(l->data);
+ }
g_list_free(user->group_ids);
+ }
if (user->msnobj != NULL)
msn_object_destroy(user->msnobj);
@@ -67,6 +76,7 @@ msn_user_destroy(MsnUser *user)
g_free(user->passport);
g_free(user->friendly_name);
g_free(user->store_name);
+ g_free(user->uid);
g_free(user->phone.home);
g_free(user->phone.work);
g_free(user->phone.mobile);
@@ -81,7 +91,18 @@ msn_user_update(MsnUser *user)
account = user->userlist->session->account;
- if (user->status != NULL) {
+ if (user->statusline != NULL && user->currentmedia != NULL) {
+ purple_prpl_got_user_status(account, user->passport, user->status,
+ "message", user->statusline,
+ "currentmedia", user->currentmedia, NULL);
+ } else if (user->currentmedia != NULL) {
+ purple_prpl_got_user_status(account, user->passport, user->status, "currentmedia",
+ user->currentmedia, NULL);
+ } else if (user->statusline != NULL) {
+ //char *status = g_strdup_printf("%s - %s", user->status, user->statusline);
+ purple_prpl_got_user_status(account, user->passport, user->status,
+ "message", user->statusline, NULL);
+ } else if (user->status != NULL) {
if (!strcmp(user->status, "offline") && user->mobile) {
purple_prpl_got_user_status(account, user->passport, "offline", NULL);
purple_prpl_got_user_status(account, user->passport, "mobile", NULL);
@@ -142,12 +163,66 @@ msn_user_set_friendly_name(MsnUser *user, const char *name)
}
void
+msn_user_set_statusline(MsnUser *user, const char *statusline)
+{
+ g_return_if_fail(user != NULL);
+
+ g_free(user->statusline);
+ user->statusline = g_strdup(statusline);
+}
+
+void
+msn_user_set_currentmedia(MsnUser *user, const char *currentmedia)
+{
+ g_return_if_fail(user != NULL);
+
+ g_free(user->currentmedia);
+ user->currentmedia = g_strdup(currentmedia);
+}
+
+void
msn_user_set_store_name(MsnUser *user, const char *name)
{
g_return_if_fail(user != NULL);
- g_free(user->store_name);
- user->store_name = g_strdup(name);
+ if (name != NULL)
+ {
+ g_free(user->store_name);
+ user->store_name = g_strdup(name);
+ }
+}
+
+void
+msn_user_set_uid(MsnUser *user, const char *uid)
+{
+ g_return_if_fail(user != NULL);
+
+ g_free(user->uid);
+ user->uid = g_strdup(uid);
+}
+
+void
+msn_user_set_type(MsnUser *user, MsnUserType type)
+{
+ g_return_if_fail(user != NULL);
+
+ user->type = type;
+}
+
+void
+msn_user_set_op(MsnUser *user, int list_op)
+{
+ g_return_if_fail(user != NULL);
+
+ user->list_op |= list_op;
+}
+
+void
+msn_user_unset_op(MsnUser *user, int list_op)
+{
+ g_return_if_fail(user != NULL);
+
+ user->list_op &= ~list_op;
}
void
@@ -218,54 +293,97 @@ msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img)
}
}
+/*add group id to User object*/
void
-msn_user_add_group_id(MsnUser *user, int id)
+msn_user_add_group_id(MsnUser *user, const char* id)
{
MsnUserList *userlist;
PurpleAccount *account;
PurpleBuddy *b;
PurpleGroup *g;
const char *passport;
+ char *group_id;
const char *group_name;
g_return_if_fail(user != NULL);
- g_return_if_fail(id >= 0);
+ g_return_if_fail(id != NULL);
- user->group_ids = g_list_append(user->group_ids, GINT_TO_POINTER(id));
+ group_id = g_strdup(id);
+ user->group_ids = g_list_append(user->group_ids, group_id);
userlist = user->userlist;
account = userlist->session->account;
passport = msn_user_get_passport(user);
- group_name = msn_userlist_find_group_name(userlist, id);
+ group_name = msn_userlist_find_group_name(userlist, group_id);
+
+ purple_debug_info("User","group id:%s,name:%s,user:%s\n", group_id, group_name, passport);
g = purple_find_group(group_name);
- if ((id == 0) && (g == NULL))
+ if ((id == NULL) && (g == NULL))
{
g = purple_group_new(group_name);
purple_blist_add_group(g, NULL);
}
b = purple_find_buddy_in_group(account, passport, g);
-
if (b == NULL)
{
b = purple_buddy_new(account, passport, NULL);
-
purple_blist_add_buddy(b, NULL, g, NULL);
}
-
b->proto_data = user;
+ /*Update the blist Node info*/
+// purple_blist_node_set_string(&(b->node), "", "");
+}
+
+/*check if the msn user is online*/
+gboolean
+msn_user_is_online(PurpleAccount *account, const char *name)
+{
+ PurpleBuddy *buddy;
+
+ buddy =purple_find_buddy(account,name);
+ return PURPLE_BUDDY_IS_ONLINE(buddy);
+}
+
+gboolean
+msn_user_is_yahoo(PurpleAccount *account, const char *name)
+{
+ MsnSession *session = NULL;
+ MsnUser *user;
+ PurpleConnection *gc;
+
+ gc = purple_account_get_connection(account);
+ if (gc != NULL)
+ session = gc->proto_data;
+
+ if ((session != NULL) && (session->protocol_ver == WLM_PROT_VER))
+ return FALSE;
+
+ if ((session != NULL) && (user = msn_userlist_find_user(session->userlist, name)) != NULL)
+ {
+ return (user->type == MSN_USER_TYPE_YAHOO);
+ }
+ return (strstr(name,"@yahoo.") != NULL);
}
void
-msn_user_remove_group_id(MsnUser *user, int id)
+msn_user_remove_group_id(MsnUser *user, const char *id)
{
+ GList *l;
+
g_return_if_fail(user != NULL);
- g_return_if_fail(id >= 0);
+ g_return_if_fail(id != NULL);
+
+ l = g_list_find_custom(user->group_ids, id, (GCompareFunc)strcmp);
+
+ if (l == NULL)
+ return;
- user->group_ids = g_list_remove(user->group_ids, GINT_TO_POINTER(id));
+ g_free(l->data);
+ user->group_ids = g_list_remove_link(user->group_ids, l);
}
void
diff --git a/libpurple/protocols/msn/user.h b/libpurple/protocols/msn/user.h
index 1c18a63a9a..3746e8c3b3 100644
--- a/libpurple/protocols/msn/user.h
+++ b/libpurple/protocols/msn/user.h
@@ -31,6 +31,17 @@ typedef struct _MsnUser MsnUser;
#include "userlist.h"
+typedef enum
+{
+ MSN_USER_TYPE_UNKNOWN = 0x00,
+ MSN_USER_TYPE_PASSPORT = 0x01,
+ MSN_USER_TYPE_UNKNOWN1 = 0x02,
+ MSN_USER_TYPE_MOBILE = 0x04,
+ MSN_USER_TYPE_UNKNOWN2 = 0x08,
+ MSN_USER_TYPE_UNKNOWN3 = 0x10,
+ MSN_USER_TYPE_YAHOO = 0x20
+} MsnUserType;
+
/**
* A user.
*/
@@ -45,7 +56,12 @@ struct _MsnUser
char *store_name; /**< The name stored in the server. */
char *friendly_name; /**< The friendly name. */
+ char * uid; /*< User Id */
+
const char *status; /**< The state of the user. */
+ char *statusline; /**< The state of the user. */
+ char *currentmedia; /**< The current media of the user. */
+
gboolean idle; /**< The idle state of the user. */
struct
@@ -65,7 +81,12 @@ struct _MsnUser
GHashTable *clientcaps; /**< The client's capabilities. */
- int list_op;
+ MsnUserType type; /**< The user type */
+
+ int list_op; /**< Which lists the user is in */
+
+ guint membership_id[5]; /**< The membershipId sent by the contacts server,
+ indexed by the list it belongs to */
};
/**************************************************************************/
@@ -102,6 +123,22 @@ void msn_user_destroy(MsnUser *user);
*/
void msn_user_update(MsnUser *user);
+ /**
+ * Sets the new statusline of user.
+ *
+ * @param user The user.
+ * @param state The statusline string.
+ */
+void msn_user_set_statusline(MsnUser *user, const char *statusline);
+
+ /**
+ * Sets the current media of user.
+ *
+ * @param user The user.
+ * @param state The statusline string.
+ */
+void msn_user_set_currentmedia(MsnUser *user, const char *currentmedia);
+
/**
* Sets the new state of user.
*
@@ -156,7 +193,7 @@ void msn_user_set_group_ids(MsnUser *user, GList *ids);
* @param user The user.
* @param id The group ID.
*/
-void msn_user_add_group_id(MsnUser *user, int id);
+void msn_user_add_group_id(MsnUser *user, const char * id);
/**
* Removes the group ID from a user.
@@ -164,7 +201,7 @@ void msn_user_add_group_id(MsnUser *user, int id);
* @param user The user.
* @param id The group ID.
*/
-void msn_user_remove_group_id(MsnUser *user, int id);
+void msn_user_remove_group_id(MsnUser *user, const char * id);
/**
* Sets the home phone number for a user.
@@ -182,6 +219,9 @@ void msn_user_set_home_phone(MsnUser *user, const char *number);
*/
void msn_user_set_work_phone(MsnUser *user, const char *number);
+void msn_user_set_uid(MsnUser *user, const char *uid);
+void msn_user_set_type(MsnUser *user, MsnUserType type);
+
/**
* Sets the mobile phone number for a user.
*
@@ -279,6 +319,22 @@ MsnObject *msn_user_get_object(const MsnUser *user);
*/
GHashTable *msn_user_get_client_caps(const MsnUser *user);
+/**
+ * check to see if user is online
+ */
+gboolean
+msn_user_is_online(PurpleAccount *account, const char *name);
+
+/**
+ * check to see if user is Yahoo User
+ */
+gboolean
+msn_user_is_yahoo(PurpleAccount *account ,const char *name);
+
+void msn_user_set_op(MsnUser *user, int list_op);
+void msn_user_unset_op(MsnUser *user, int list_op);
+
/*@}*/
+
#endif /* _MSN_USER_H_ */
diff --git a/libpurple/protocols/msn/userlist.c b/libpurple/protocols/msn/userlist.c
index 4fac5d8aa6..a8ff3b4baa 100644
--- a/libpurple/protocols/msn/userlist.c
+++ b/libpurple/protocols/msn/userlist.c
@@ -43,8 +43,15 @@ msn_accept_add_cb(gpointer data)
MsnPermitAdd *pa = data;
MsnSession *session = pa->gc->proto_data;
MsnUserList *userlist = session->userlist;
+ MsnUser *user = msn_userlist_find_add_user(userlist, pa->who, pa->who);
+
+ purple_debug_misc("MSN Userlist", "Accepted the new buddy: %s\n", pa->who);
+
+ msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL);
- msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_AL, NULL);
+ if (msn_userlist_user_is_in_list(user, MSN_LIST_FL)) {
+ msn_del_contact_from_list(session->contact, NULL, pa->who, MSN_LIST_PL);
+ }
g_free(pa->who);
g_free(pa->friendly);
@@ -55,10 +62,20 @@ static void
msn_cancel_add_cb(gpointer data)
{
MsnPermitAdd *pa = data;
- MsnSession *session = pa->gc->proto_data;
- MsnUserList *userlist = session->userlist;
- msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_BL, NULL);
+ purple_debug_misc("MSN Userlist", "Deniedthe new buddy: %s\n", pa->who);
+
+ if (g_list_find(purple_connections_get_all(), pa->gc) != NULL)
+ {
+ MsnSession *session = pa->gc->proto_data;
+ MsnUserList *userlist = session->userlist;
+ MsnCallbackState *state = msn_callback_state_new();
+
+ msn_callback_state_set_action(state, MSN_DENIED_BUDDY);
+
+ msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_BL);
+ msn_del_contact_from_list(session->contact, state, pa->who, MSN_LIST_PL);
+ }
g_free(pa->who);
g_free(pa->friendly);
@@ -78,47 +95,42 @@ got_new_entry(PurpleConnection *gc, const char *passport, const char *friendly)
purple_account_request_authorization(purple_connection_get_account(gc), passport, NULL, friendly, NULL,
purple_find_buddy(purple_connection_get_account(gc), passport) != NULL,
msn_accept_add_cb, msn_cancel_add_cb, pa);
+
}
/**************************************************************************
* Utility functions
**************************************************************************/
-static gboolean
-user_is_in_group(MsnUser *user, int group_id)
+gboolean
+msn_userlist_user_is_in_group(MsnUser *user, const char * group_id)
{
if (user == NULL)
return FALSE;
- if (group_id < 0)
+ if (group_id == NULL)
return FALSE;
- if (g_list_find(user->group_ids, GINT_TO_POINTER(group_id)))
+ if (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp))
return TRUE;
return FALSE;
}
-static gboolean
-user_is_there(MsnUser *user, int list_id, int group_id)
+gboolean
+msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id)
{
int list_op;
if (user == NULL)
return FALSE;
-
+
list_op = 1 << list_id;
- if (!(user->list_op & list_op))
+ if (user->list_op & list_op)
+ return TRUE;
+ else
return FALSE;
-
- if (list_id == MSN_LIST_FL)
- {
- if (group_id >= 0)
- return user_is_in_group(user, group_id);
- }
-
- return TRUE;
}
static const char*
@@ -149,31 +161,6 @@ get_store_name(MsnUser *user)
return store_name;
}
-static void
-msn_request_add_group(MsnUserList *userlist, const char *who,
- const char *old_group_name, const char *new_group_name)
-{
- MsnCmdProc *cmdproc;
- MsnTransaction *trans;
- MsnMoveBuddy *data;
-
- cmdproc = userlist->session->notification->cmdproc;
- data = g_new0(MsnMoveBuddy, 1);
-
- data->who = g_strdup(who);
-
- if (old_group_name)
- data->old_group_name = g_strdup(old_group_name);
-
- trans = msn_transaction_new(cmdproc, "ADG", "%s %d",
- purple_url_encode(new_group_name),
- 0);
-
- msn_transaction_set_data(trans, data);
-
- msn_cmdproc_send_trans(cmdproc, trans);
-}
-
/**************************************************************************
* Server functions
**************************************************************************/
@@ -193,14 +180,16 @@ msn_get_list_id(const char *list)
return -1;
}
+/* this function msn_got_add_user isn't called anywhere */
void
msn_got_add_user(MsnSession *session, MsnUser *user,
- MsnListId list_id, int group_id)
+ MsnListId list_id, const char * group_id)
{
PurpleAccount *account;
const char *passport;
const char *friendly;
+ purple_debug_info("MSNP14","got add user...\n");
account = session->account;
passport = msn_user_get_passport(user);
@@ -214,7 +203,7 @@ msn_got_add_user(MsnSession *session, MsnUser *user,
serv_got_alias(gc, passport, friendly);
- if (group_id >= 0)
+ if (group_id != NULL)
{
msn_user_add_group_id(user, group_id);
}
@@ -263,7 +252,7 @@ msn_got_add_user(MsnSession *session, MsnUser *user,
* looked at this. Maybe we should use the store
* name instead? --KingAnt
*/
- got_new_entry(gc, passport, friendly);
+// got_new_entry(gc, passport, friendly);
}
}
@@ -273,7 +262,7 @@ msn_got_add_user(MsnSession *session, MsnUser *user,
void
msn_got_rem_user(MsnSession *session, MsnUser *user,
- MsnListId list_id, int group_id)
+ MsnListId list_id, const char * group_id)
{
PurpleAccount *account;
const char *passport;
@@ -285,7 +274,7 @@ msn_got_rem_user(MsnSession *session, MsnUser *user,
if (list_id == MSN_LIST_FL)
{
/* TODO: When is the user totally removed? */
- if (group_id >= 0)
+ if (group_id != NULL)
{
msn_user_remove_group_id(user, group_id);
return;
@@ -333,7 +322,6 @@ msn_got_rem_user(MsnSession *session, MsnUser *user,
{
purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n",
passport);
-
}
}
@@ -351,14 +339,16 @@ msn_got_lst_user(MsnSession *session, MsnUser *user,
passport = msn_user_get_passport(user);
store = msn_user_get_store_name(user);
+
+ msn_user_set_op(user, list_op);
if (list_op & MSN_LIST_FL_OP)
{
GSList *c;
for (c = group_ids; c != NULL; c = g_slist_next(c))
{
- int group_id;
- group_id = GPOINTER_TO_INT(c->data);
+ char *group_id;
+ group_id = c->data;
msn_user_add_group_id(user, group_id);
}
@@ -393,11 +383,14 @@ msn_got_lst_user(MsnSession *session, MsnUser *user,
if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
{
- got_new_entry(gc, passport, store);
+// got_new_entry(gc, passport, store);
}
}
- user->list_op = list_op;
+ if (list_op & MSN_LIST_PL_OP)
+ {
+ got_new_entry(gc, passport, store);
+ }
}
/**************************************************************************
@@ -427,18 +420,18 @@ msn_userlist_destroy(MsnUserList *userlist)
{
GList *l;
+ /*destroy userlist*/
for (l = userlist->users; l != NULL; l = l->next)
{
msn_user_destroy(l->data);
}
-
g_list_free(userlist->users);
+ /*destroy group list*/
for (l = userlist->groups; l != NULL; l = l->next)
{
msn_group_destroy(l->data);
}
-
g_list_free(userlist->groups);
g_queue_free(userlist->buddy_icon_requests);
@@ -449,6 +442,22 @@ msn_userlist_destroy(MsnUserList *userlist)
g_free(userlist);
}
+MsnUser *
+msn_userlist_find_add_user(MsnUserList *userlist,const char *passport,const char *userName)
+{
+ MsnUser *user;
+
+ user = msn_userlist_find_user(userlist, passport);
+ if (user == NULL)
+ {
+ user = msn_user_new(userlist, passport, userName);
+ msn_userlist_add_user(userlist, user);
+ } else {
+ msn_user_set_store_name(user, userName);
+ }
+ return user;
+}
+
void
msn_userlist_add_user(MsnUserList *userlist, MsnUser *user)
{
@@ -472,10 +481,13 @@ msn_userlist_find_user(MsnUserList *userlist, const char *passport)
{
MsnUser *user = (MsnUser *)l->data;
+// purple_debug_info("MsnUserList","user passport:%s,passport:%s\n",user->passport,passport);
g_return_val_if_fail(user->passport != NULL, NULL);
- if (!strcmp(passport, user->passport))
+ if (!g_strcasecmp(passport, user->passport)){
+// purple_debug_info("MsnUserList","return:%p\n",user);
return user;
+ }
}
return NULL;
@@ -494,18 +506,18 @@ msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group)
}
MsnGroup *
-msn_userlist_find_group_with_id(MsnUserList *userlist, int id)
+msn_userlist_find_group_with_id(MsnUserList *userlist, const char * id)
{
GList *l;
g_return_val_if_fail(userlist != NULL, NULL);
- g_return_val_if_fail(id >= 0, NULL);
+ g_return_val_if_fail(id != NULL, NULL);
for (l = userlist->groups; l != NULL; l = l->next)
{
MsnGroup *group = l->data;
- if (group->id == id)
+ if (!g_strcasecmp(group->id,id))
return group;
}
@@ -524,14 +536,14 @@ msn_userlist_find_group_with_name(MsnUserList *userlist, const char *name)
{
MsnGroup *group = l->data;
- if ((group->name != NULL) && !g_ascii_strcasecmp(name, group->name))
+ if ((group->name != NULL) && !g_strcasecmp(name, group->name))
return group;
}
return NULL;
}
-int
+const char *
msn_userlist_find_group_id(MsnUserList *userlist, const char *group_name)
{
MsnGroup *group;
@@ -541,11 +553,11 @@ msn_userlist_find_group_id(MsnUserList *userlist, const char *group_name)
if (group != NULL)
return msn_group_get_id(group);
else
- return -1;
+ return NULL;
}
const char *
-msn_userlist_find_group_name(MsnUserList *userlist, int group_id)
+msn_userlist_find_group_name(MsnUserList *userlist, const char * group_id)
{
MsnGroup *group;
@@ -558,7 +570,7 @@ msn_userlist_find_group_name(MsnUserList *userlist, int group_id)
}
void
-msn_userlist_rename_group_id(MsnUserList *userlist, int group_id,
+msn_userlist_rename_group_id(MsnUserList *userlist, const char * group_id,
const char *new_name)
{
MsnGroup *group;
@@ -570,7 +582,7 @@ msn_userlist_rename_group_id(MsnUserList *userlist, int group_id,
}
void
-msn_userlist_remove_group_id(MsnUserList *userlist, int group_id)
+msn_userlist_remove_group_id(MsnUserList *userlist, const char * group_id)
{
MsnGroup *group;
@@ -584,116 +596,293 @@ msn_userlist_remove_group_id(MsnUserList *userlist, int group_id)
}
void
-msn_userlist_rem_buddy(MsnUserList *userlist,
- const char *who, int list_id, const char *group_name)
+msn_userlist_rem_buddy(MsnUserList *userlist, const char *who)
{
- MsnUser *user;
- int group_id;
- const char *list;
-
+ MsnUser *user = NULL;
+
+ g_return_if_fail(userlist != NULL);
+ g_return_if_fail(userlist->session != NULL);
+ g_return_if_fail(userlist->session->contact != NULL);
+ g_return_if_fail(who != NULL);
+
user = msn_userlist_find_user(userlist, who);
- group_id = -1;
- if (group_name != NULL)
- {
- group_id = msn_userlist_find_group_id(userlist, group_name);
+ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_FL);
- if (group_id < 0)
- {
- /* Whoa, there is no such group. */
- purple_debug_error("msn", "Group doesn't exist: %s\n", group_name);
- return;
- }
+ /* delete the contact from address book via soap action */
+ if (user != NULL) {
+ msn_delete_contact(userlist->session->contact, user->uid);
}
+}
- /* First we're going to check if not there. */
- if (!(user_is_there(user, list_id, group_id)))
- {
+void
+msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
+ MsnListId list_id)
+{
+ MsnUser *user;
+ const gchar *list;
+ MsnListOp list_op = 1 << list_id;
+
+ user = msn_userlist_find_user(userlist, who);
+
+ g_return_if_fail(user != NULL);
+
+ if ( !msn_userlist_user_is_in_list(user, list_id)) {
list = lists[list_id];
- purple_debug_error("msn", "User '%s' is not there: %s\n",
- who, list);
+ purple_debug_info("MSN Userlist", "User %s is not in list %s, not removing.\n", who, list);
return;
}
- /* Then request the rem to the server. */
- list = lists[list_id];
+ msn_user_unset_op(user, list_op);
- msn_notification_rem_buddy(userlist->session->notification, list, who, group_id);
+ msn_notification_rem_buddy_from_list(userlist->session->notification, list_id, who);
}
+/*add buddy*/
void
-msn_userlist_add_buddy(MsnUserList *userlist,
- const char *who, int list_id,
- const char *group_name)
+msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group_name)
{
MsnUser *user;
- int group_id;
- const char *list;
- const char *store_name;
+ MsnCallbackState *state = NULL;
+ const char *group_id = NULL, *new_group_name;
+
+ new_group_name = group_name == NULL ? MSN_INDIVIDUALS_GROUP_NAME : group_name;
+
+
+ g_return_if_fail(userlist != NULL);
+ g_return_if_fail(userlist->session != NULL);
+
+
+ purple_debug_info("MSN Userlist", "Add user: %s to group: %s\n", who, new_group_name);
- group_id = -1;
+ state = msn_callback_state_new();
+ msn_callback_state_set_who(state, who);
+ msn_callback_state_set_new_group_name(state, new_group_name);
if (!purple_email_is_valid(who))
{
/* only notify the user about problems adding to the friends list
* maybe we should do something else for other lists, but it probably
* won't cause too many problems if we just ignore it */
- if (list_id == MSN_LIST_FL)
- {
- char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
- purple_notify_error(NULL, NULL, str,
- _("The screen name specified is invalid."));
- g_free(str);
- }
+
+ char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
+
+ purple_notify_error(NULL, NULL, str,
+ _("The screen name specified is invalid."));
+ g_free(str);
return;
}
- if (group_name != NULL)
+ group_id = msn_userlist_find_group_id(userlist, new_group_name);
+
+ if (group_id == NULL)
{
- group_id = msn_userlist_find_group_id(userlist, group_name);
+ /* Whoa, we must add that group first. */
+ purple_debug_info("MSN Userlist", "Adding user %s to a new group, creating group %s first\n", who, new_group_name);
+
+ msn_callback_state_set_action(state, MSN_ADD_BUDDY);
- if (group_id < 0)
- {
- /* Whoa, we must add that group first. */
- msn_request_add_group(userlist, who, NULL, group_name);
+ msn_add_group(userlist->session, state, new_group_name);
+ return;
+ } else {
+ msn_callback_state_set_guid(state, group_id);
+ }
+
+ /* XXX: adding user here may not be correct (should add them in the
+ * ACK to the ADL command), but for now we need to make sure they exist
+ * early enough that the ILN command doesn't screw us up */
+
+ user = msn_userlist_find_add_user(userlist, who, who);
+
+ if ( msn_userlist_user_is_in_list(user, MSN_LIST_FL) ) {
+
+ purple_debug_info("MSN Userlist", "User %s already exists\n", who);
+
+ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL);
+
+ if (msn_userlist_user_is_in_group(user, group_id)) {
+ purple_debug_info("MSN Userlist", "User %s is already in group %s, returning\n", who, new_group_name);
return;
}
}
+
+ purple_debug_info("MSN Userlist", "Adding user: %s to group id: %s\n", who, group_id);
- user = msn_userlist_find_user(userlist, who);
+ msn_callback_state_set_action(state, MSN_ADD_BUDDY);
+
+ /* Add contact in the Contact server with a SOAP request and if
+ successful, send ADL with MSN_LIST_AL and MSN_LIST_FL and a FQY */
+ msn_add_contact_to_group(userlist->session->contact, state, who, group_id);
+}
+
+void
+msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
+ MsnListId list_id)
+{
+ MsnUser *user = NULL;
+ const gchar *list;
+ MsnListOp list_op = 1 << list_id;
+ g_return_if_fail(userlist != NULL);
+
+ user = msn_userlist_find_add_user(userlist, who, who);
+
/* First we're going to check if it's already there. */
- if (user_is_there(user, list_id, group_id))
+ if (msn_userlist_user_is_in_list(user, list_id))
{
list = lists[list_id];
- purple_debug_error("msn", "User '%s' is already there: %s\n", who, list);
+ purple_debug_info("MSN Userlist", "User '%s' is already in list: %s\n", who, list);
return;
}
+
+ //store_name = (user != NULL) ? get_store_name(user) : who;
+
+ //purple_debug_info("MSN Userlist", "store_name = %s\n", store_name);
+
+ /* XXX: see XXX above, this should really be done when we get the response from
+ the server */
+
+ msn_user_set_op(user, list_op);
+
+ msn_notification_add_buddy_to_list(userlist->session->notification, list_id, who);
+}
+
+gboolean
+msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who,
+ const char *group_name)
+{
+ MsnUser *user;
+ gchar * group_id;
+
+ g_return_val_if_fail(userlist != NULL, FALSE);
+ g_return_val_if_fail(group_name != NULL, FALSE);
+ g_return_val_if_fail(who != NULL, FALSE);
+
+ purple_debug_info("MSN Userlist","Adding buddy with passport %s to group %s\n", who, group_name);
+
+ if ( (group_id = (gchar *)msn_userlist_find_group_id(userlist, group_name)) == NULL) {
+ purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name);
+ return FALSE;
+ }
+
+ if ( (user = msn_userlist_find_user(userlist, who)) == NULL) {
+ purple_debug_error("MSN Userlist", "User %s not found!", who);
+ return FALSE;
+ }
+
+ msn_user_add_group_id(user, group_id);
+
+ return TRUE;
+}
+
+
+gboolean
+msn_userlist_rem_buddy_from_group(MsnUserList *userlist, const char *who,
+ const char *group_name)
+{
+ const gchar * group_id;
+ MsnUser *user;
+
+ g_return_val_if_fail(userlist != NULL, FALSE);
+ g_return_val_if_fail(group_name != NULL, FALSE);
+ g_return_val_if_fail(who != NULL, FALSE);
+
+ purple_debug_info("MSN Userlist","Removing buddy with passport %s from group %s\n", who, group_name);
+
+ if ( (group_id = msn_userlist_find_group_id(userlist, group_name)) == NULL) {
+ purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name);
+ return FALSE;
+ }
- store_name = (user != NULL) ? get_store_name(user) : who;
+ if ( (user = msn_userlist_find_user(userlist, who)) == NULL) {
+ purple_debug_error("MSN Userlist", "User %s not found!", who);
+ return FALSE;
+ }
- /* Then request the add to the server. */
- list = lists[list_id];
+ msn_user_remove_group_id(user, group_id);
- msn_notification_add_buddy(userlist->session->notification, list, who,
- store_name, group_id);
+ return TRUE;
}
void
msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
- const char *old_group_name, const char *new_group_name)
+ const char *old_group_name, const char *new_group_name)
{
- int new_group_id;
+ const char *new_group_id;
+ MsnCallbackState *state;
+
+ g_return_if_fail(userlist != NULL);
+ g_return_if_fail(userlist->session != NULL);
+ g_return_if_fail(userlist->session->contact != NULL);
+
+ state = msn_callback_state_new();
+ msn_callback_state_set_who(state, who);
+ msn_callback_state_set_action(state, MSN_MOVE_BUDDY);
+ msn_callback_state_set_old_group_name(state, old_group_name);
+ msn_callback_state_set_new_group_name(state, new_group_name);
new_group_id = msn_userlist_find_group_id(userlist, new_group_name);
- if (new_group_id < 0)
- {
- msn_request_add_group(userlist, who, old_group_name, new_group_name);
+ if (new_group_id == NULL)
+ {
+ msn_add_group(userlist->session, state, new_group_name);
return;
}
+
+ /* add the contact to the new group, and remove it from the old one in
+ * the callback
+ */
+ msn_add_contact_to_group(userlist->session->contact, state, who, new_group_id);
+}
- msn_userlist_add_buddy(userlist, who, MSN_LIST_FL, new_group_name);
- msn_userlist_rem_buddy(userlist, who, MSN_LIST_FL, old_group_name);
+/*load userlist from the Blist file cache*/
+void
+msn_userlist_load(MsnSession *session)
+{
+ PurpleBlistNode *gnode, *cnode, *bnode;
+ PurpleConnection *gc = purple_account_get_connection(session->account);
+ GSList *l;
+ MsnUser * user;
+
+ g_return_if_fail(gc != NULL);
+
+ for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next)
+ {
+ if (!PURPLE_BLIST_NODE_IS_GROUP(gnode))
+ continue;
+ for (cnode = gnode->child; cnode; cnode = cnode->next)
+ {
+ if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
+ continue;
+ for (bnode = cnode->child; bnode; bnode = bnode->next)
+ {
+ PurpleBuddy *b;
+ if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
+ continue;
+ b = (PurpleBuddy *)bnode;
+ if (b->account == gc->account)
+ {
+ user = msn_userlist_find_add_user(session->userlist,
+ b->name,NULL);
+ b->proto_data = user;
+ msn_user_set_op(user, MSN_LIST_FL_OP);
+ }
+ }
+ }
+ }
+ for (l = session->account->permit; l != NULL; l = l->next)
+ {
+ user = msn_userlist_find_add_user(session->userlist,
+ (char *)l->data,NULL);
+ msn_user_set_op(user, MSN_LIST_AL_OP);
+ }
+ for (l = session->account->deny; l != NULL; l = l->next)
+ {
+ user = msn_userlist_find_add_user(session->userlist,
+ (char *)l->data,NULL);
+ msn_user_set_op(user, MSN_LIST_BL_OP);
+ }
+
}
+
diff --git a/libpurple/protocols/msn/userlist.h b/libpurple/protocols/msn/userlist.h
index 353cb16fea..86692c323f 100644
--- a/libpurple/protocols/msn/userlist.h
+++ b/libpurple/protocols/msn/userlist.h
@@ -35,16 +35,11 @@ typedef enum
MSN_LIST_FL,
MSN_LIST_AL,
MSN_LIST_BL,
- MSN_LIST_RL
+ MSN_LIST_RL,
+ MSN_LIST_PL
} MsnListId;
-typedef struct
-{
- char *who;
- char *old_group_name;
-
-} MsnMoveBuddy;
struct _MsnUserList
{
@@ -64,40 +59,56 @@ struct _MsnUserList
};
+gboolean msn_userlist_user_is_in_group(MsnUser *user, const char * group_id);
+gboolean msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id);
MsnListId msn_get_list_id(const char *list);
void msn_got_add_user(MsnSession *session, MsnUser *user,
- MsnListId list_id, int group_id);
+ MsnListId list_id, const char *group_id);
void msn_got_rem_user(MsnSession *session, MsnUser *user,
- MsnListId list_id, int group_id);
+ MsnListId list_id, const char *group_id);
void msn_got_lst_user(MsnSession *session, MsnUser *user,
int list_op, GSList *group_ids);
MsnUserList *msn_userlist_new(MsnSession *session);
void msn_userlist_destroy(MsnUserList *userlist);
+
void msn_userlist_add_user(MsnUserList *userlist, MsnUser *user);
void msn_userlist_remove_user(MsnUserList *userlist, MsnUser *user);
-MsnUser *msn_userlist_find_user(MsnUserList *userlist,
- const char *passport);
+
+MsnUser * msn_userlist_find_user(MsnUserList *userlist, const char *passport);
+MsnUser * msn_userlist_find_add_user(MsnUserList *userlist,
+ const char *passport, const char *userName);
+
void msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group);
void msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group);
-MsnGroup *msn_userlist_find_group_with_id(MsnUserList *userlist, int id);
-MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist,
- const char *name);
-int msn_userlist_find_group_id(MsnUserList *userlist,
- const char *group_name);
-const char *msn_userlist_find_group_name(MsnUserList *userlist,
- int group_id);
-void msn_userlist_rename_group_id(MsnUserList *userlist, int group_id,
- const char *new_name);
-void msn_userlist_remove_group_id(MsnUserList *userlist, int group_id);
-
-void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who,
- int list_id, const char *group_name);
-void msn_userlist_add_buddy(MsnUserList *userlist, const char *who,
- int list_id, const char *group_name);
+MsnGroup *msn_userlist_find_group_with_id(MsnUserList *userlist, const char *id);
+MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist, const char *name);
+const char * msn_userlist_find_group_id(MsnUserList *userlist,
+ const char *group_name);
+const char *msn_userlist_find_group_name(MsnUserList *userlist, const char *group_id);
+void msn_userlist_rename_group_id(MsnUserList *userlist, const char *group_id,
+ const char *new_name);
+void msn_userlist_remove_group_id(MsnUserList *userlist, const char *group_id);
+
+void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who);
+void msn_userlist_add_buddy(MsnUserList *userlist,
+ const char *who, const char *group_name);
void msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
- const char *old_group_name,
- const char *new_group_name);
+ const char *old_group_name,
+ const char *new_group_name);
+
+gboolean msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who,
+ const char *group_name);
+gboolean msn_userlist_rem_buddy_from_group(MsnUserList *userlist,
+ const char *who,
+ const char *group_name);
+
+void msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
+ MsnListId list_id);
+void msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
+ MsnListId list_id);
+
+void msn_userlist_load(MsnSession *session);
#endif /* _MSN_USERLIST_H_ */