summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJorge Villase?or <masca@cpw.pidgin.im>2010-10-29 17:36:08 +0000
committerJorge Villase?or <masca@cpw.pidgin.im>2010-10-29 17:36:08 +0000
commitd3e179071c8c0cee5cfa5492071b7bc029dcfbc7 (patch)
treea076e398d2f7daa8405a2bbcf68fe86b01cbecef
parentda62f10b64e128e0212546a3817f13e64a635592 (diff)
parent771e0e1b1e52408fa53ba669f27f4f22fea8acb7 (diff)
downloadpidgin-d3e179071c8c0cee5cfa5492071b7bc029dcfbc7.tar.gz
propagate from branch 'im.pidgin.pidgin' (head cd041b5b7ceb5ee924fb9b9986d74411558e9b8d)
to branch 'im.pidgin.soc.2010.msn-tlc' (head 58956c5274b1e969b71b47f11384b845011c7a70)
-rw-r--r--libpurple/protocols/msn/Makefile.am12
-rw-r--r--libpurple/protocols/msn/Makefile.mingw6
-rw-r--r--libpurple/protocols/msn/cmdproc.c155
-rw-r--r--libpurple/protocols/msn/cmdproc.h36
-rw-r--r--libpurple/protocols/msn/command.c3
-rw-r--r--libpurple/protocols/msn/command.h30
-rw-r--r--libpurple/protocols/msn/contact.c8
-rw-r--r--libpurple/protocols/msn/dialog.c146
-rw-r--r--libpurple/protocols/msn/dialog.h30
-rw-r--r--libpurple/protocols/msn/directconn.c146
-rw-r--r--libpurple/protocols/msn/directconn.h13
-rw-r--r--libpurple/protocols/msn/error.c128
-rw-r--r--libpurple/protocols/msn/error.h10
-rw-r--r--libpurple/protocols/msn/group.h2
-rw-r--r--libpurple/protocols/msn/history.h2
-rw-r--r--libpurple/protocols/msn/httpconn.h1
-rw-r--r--libpurple/protocols/msn/msg.c440
-rw-r--r--libpurple/protocols/msn/msg.h74
-rw-r--r--libpurple/protocols/msn/msn.c188
-rw-r--r--libpurple/protocols/msn/msn.h38
-rw-r--r--libpurple/protocols/msn/msnutils.c30
-rw-r--r--libpurple/protocols/msn/msnutils.h25
-rw-r--r--libpurple/protocols/msn/nexus.c6
-rw-r--r--libpurple/protocols/msn/nexus.h2
-rw-r--r--libpurple/protocols/msn/notification.c400
-rw-r--r--libpurple/protocols/msn/notification.h21
-rw-r--r--libpurple/protocols/msn/object.c2
-rw-r--r--libpurple/protocols/msn/object.h2
-rw-r--r--libpurple/protocols/msn/oim.c13
-rw-r--r--libpurple/protocols/msn/p2p.c76
-rw-r--r--libpurple/protocols/msn/p2p.h88
-rw-r--r--libpurple/protocols/msn/sbconn.c151
-rw-r--r--libpurple/protocols/msn/sbconn.h17
-rw-r--r--libpurple/protocols/msn/servconn.c4
-rw-r--r--libpurple/protocols/msn/servconn.h1
-rw-r--r--libpurple/protocols/msn/session.c28
-rw-r--r--libpurple/protocols/msn/session.h9
-rw-r--r--libpurple/protocols/msn/slp.c1428
-rw-r--r--libpurple/protocols/msn/slp.h16
-rw-r--r--libpurple/protocols/msn/slpcall.c885
-rw-r--r--libpurple/protocols/msn/slpcall.h4
-rw-r--r--libpurple/protocols/msn/slplink.c579
-rw-r--r--libpurple/protocols/msn/slplink.h8
-rw-r--r--libpurple/protocols/msn/slpmsg.c230
-rw-r--r--libpurple/protocols/msn/slpmsg.h91
-rw-r--r--libpurple/protocols/msn/slpmsg_part.c210
-rw-r--r--libpurple/protocols/msn/slpmsg_part.h41
-rw-r--r--libpurple/protocols/msn/state.c75
-rw-r--r--libpurple/protocols/msn/state.h10
-rw-r--r--libpurple/protocols/msn/switchboard.c219
-rw-r--r--libpurple/protocols/msn/switchboard.h33
-rw-r--r--libpurple/protocols/msn/sync.c253
-rw-r--r--libpurple/protocols/msn/sync.h57
-rw-r--r--libpurple/protocols/msn/table.h50
-rw-r--r--libpurple/protocols/msn/transaction.c17
-rw-r--r--libpurple/protocols/msn/transaction.h7
-rw-r--r--libpurple/protocols/msn/user.c261
-rw-r--r--libpurple/protocols/msn/user.h119
-rw-r--r--libpurple/protocols/msn/userlist.c77
-rw-r--r--libpurple/protocols/msn/userlist.h14
-rw-r--r--libpurple/protocols/msn/xfer.c138
-rw-r--r--libpurple/protocols/msn/xfer.h12
-rw-r--r--po/POTFILES.in1
63 files changed, 4089 insertions, 3089 deletions
diff --git a/libpurple/protocols/msn/Makefile.am b/libpurple/protocols/msn/Makefile.am
index f911d5b2f3..dcde2f0a80 100644
--- a/libpurple/protocols/msn/Makefile.am
+++ b/libpurple/protocols/msn/Makefile.am
@@ -12,8 +12,6 @@ MSNSOURCES = \
command.h \
contact.c\
contact.h\
- dialog.c \
- dialog.h \
directconn.c \
directconn.h \
error.c \
@@ -36,6 +34,8 @@ MSNSOURCES = \
object.h \
oim.c\
oim.h\
+ p2p.c \
+ p2p.h \
page.c \
page.h \
servconn.c \
@@ -50,14 +50,16 @@ MSNSOURCES = \
slplink.h \
slpmsg.c \
slpmsg.h \
+ slpmsg_part.c \
+ slpmsg_part.h \
soap.c \
soap.h \
state.c \
state.h \
+ sbconn.c \
+ sbconn.h \
switchboard.c \
switchboard.h \
- sync.c \
- sync.h \
table.c \
table.h \
transaction.c \
@@ -66,6 +68,8 @@ MSNSOURCES = \
user.h \
userlist.c \
userlist.h \
+ xfer.c \
+ xfer.h \
msnutils.c \
msnutils.h
diff --git a/libpurple/protocols/msn/Makefile.mingw b/libpurple/protocols/msn/Makefile.mingw
index fe75c1cb68..c27855ad13 100644
--- a/libpurple/protocols/msn/Makefile.mingw
+++ b/libpurple/protocols/msn/Makefile.mingw
@@ -40,7 +40,6 @@ LIB_PATHS += -L$(GTK_TOP)/lib \
C_SRC = cmdproc.c \
command.c \
contact.c\
- dialog.c \
directconn.c \
error.c \
group.c \
@@ -52,6 +51,7 @@ C_SRC = cmdproc.c \
notification.c \
object.c \
oim.c\
+ p2p.c \
page.c \
servconn.c \
session.c \
@@ -59,14 +59,16 @@ C_SRC = cmdproc.c \
slpcall.c \
slplink.c \
slpmsg.c \
+ slpmsg_part.c \
soap.c\
state.c \
+ sbconn.c \
switchboard.c \
- sync.c \
table.c \
transaction.c \
user.c \
userlist.c \
+ xfer.c \
msnutils.c
OBJECTS = $(C_SRC:%.c=%.o)
diff --git a/libpurple/protocols/msn/cmdproc.c b/libpurple/protocols/msn/cmdproc.c
index 0b3646353b..03d5fe1096 100644
--- a/libpurple/protocols/msn/cmdproc.c
+++ b/libpurple/protocols/msn/cmdproc.c
@@ -21,8 +21,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "debug.h"
+
#include "cmdproc.h"
+#include "error.h"
MsnCmdProc *
msn_cmdproc_new(MsnSession *session)
@@ -122,7 +126,8 @@ msn_cmdproc_send_trans(MsnCmdProc *cmdproc, MsnTransaction *trans)
return;
}
- msn_history_add(cmdproc->history, trans);
+ if (trans->saveable)
+ msn_history_add(cmdproc->history, trans);
data = msn_transaction_to_string(trans);
@@ -155,75 +160,6 @@ msn_cmdproc_send_trans(MsnCmdProc *cmdproc, MsnTransaction *trans)
}
void
-msn_cmdproc_send_quick(MsnCmdProc *cmdproc, const char *command,
- const char *format, ...)
-{
- MsnServConn *servconn;
- char *data;
- char *params = NULL;
- va_list arg;
- size_t len;
-
- g_return_if_fail(cmdproc != NULL);
- g_return_if_fail(command != NULL);
-
- servconn = cmdproc->servconn;
-
- if (!servconn->connected)
- return;
-
- if (format != NULL)
- {
- va_start(arg, format);
- params = g_strdup_vprintf(format, arg);
- va_end(arg);
- }
-
- if (params != NULL)
- data = g_strdup_printf("%s %s\r\n", command, params);
- else
- data = g_strdup_printf("%s\r\n", command);
-
- g_free(params);
-
- len = strlen(data);
-
- show_debug_cmd(cmdproc, FALSE, data);
-
- msn_servconn_write(servconn, data, len);
-
- g_free(data);
-}
-
-void
-msn_cmdproc_send(MsnCmdProc *cmdproc, const char *command,
- const char *format, ...)
-{
- MsnTransaction *trans;
- va_list arg;
-
- g_return_if_fail(cmdproc != NULL);
- g_return_if_fail(command != NULL);
-
- if (!cmdproc->servconn->connected)
- return;
-
- trans = g_new0(MsnTransaction, 1);
-
- trans->cmdproc = cmdproc;
- trans->command = g_strdup(command);
-
- if (format != NULL)
- {
- va_start(arg, format);
- trans->params = g_strdup_vprintf(format, arg);
- va_end(arg);
- }
-
- msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-void
msn_cmdproc_process_payload(MsnCmdProc *cmdproc, char *payload,
int payload_len)
{
@@ -243,58 +179,71 @@ void
msn_cmdproc_process_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
MsnMsgTypeCb cb;
- const char *messageId = NULL;
+ const char *message_id = NULL;
/* Multi-part messages */
- if ((messageId = msn_message_get_attr(msg, "Message-ID")) != NULL) {
- const char *chunk_text = msn_message_get_attr(msg, "Chunks");
+ message_id = msn_message_get_header_value(msg, "Message-ID");
+ if (message_id != NULL) {
+ /* This is the first in a series of chunks */
+
+ const char *chunk_text = msn_message_get_header_value(msg, "Chunks");
guint chunk;
if (chunk_text != NULL) {
chunk = strtol(chunk_text, NULL, 10);
- /* 1024 chunks of ~1300 bytes is ~1MB, which seems OK to prevent
+ /* 1024 chunks of ~1300 bytes is ~1MB, which seems OK to prevent
some random client causing pidgin to hog a ton of memory.
Probably should figure out the maximum that the official client
actually supports, though. */
if (chunk > 0 && chunk < 1024) {
msg->total_chunks = chunk;
msg->received_chunks = 1;
- g_hash_table_insert(cmdproc->multiparts, (gpointer)messageId, msn_message_ref(msg));
- purple_debug_info("msn", "Received chunked message, messageId: '%s', total chunks: %d\n",
- messageId, chunk);
+ g_hash_table_insert(cmdproc->multiparts, (gpointer)message_id, msn_message_ref(msg));
+ purple_debug_info("msn", "Received chunked message, message_id: '%s', total chunks: %d\n",
+ message_id, chunk);
} else {
- purple_debug_error("msn", "MessageId '%s' has too many chunks: %d\n", messageId, chunk);
+ purple_debug_error("msn", "MessageId '%s' has too many chunks: %d\n", message_id, chunk);
}
return;
} else {
- chunk_text = msn_message_get_attr(msg, "Chunk");
+ chunk_text = msn_message_get_header_value(msg, "Chunk");
if (chunk_text != NULL) {
- MsnMessage *first = g_hash_table_lookup(cmdproc->multiparts, messageId);
+ /* This is one chunk in a series of chunks */
+
+ MsnMessage *first = g_hash_table_lookup(cmdproc->multiparts, message_id);
chunk = strtol(chunk_text, NULL, 10);
if (first == NULL) {
purple_debug_error("msn",
- "Unable to find first chunk of messageId '%s' to correspond with chunk %d.\n",
- messageId, chunk+1);
- } else if (first->received_chunks == chunk) {
- /* Chunk is from 1 to total-1 (doesn't count first one) */
- purple_debug_info("msn", "Received chunk %d of %d, messageId: '%s'\n",
- chunk+1, first->total_chunks, messageId);
- first->body = g_realloc(first->body, first->body_len + msg->body_len);
- memcpy(first->body + first->body_len, msg->body, msg->body_len);
- first->body_len += msg->body_len;
- first->received_chunks++;
- if (first->received_chunks != first->total_chunks)
- return;
- else
- /* We're done! Send it along... The caller takes care of
- freeing the old one. */
- msg = first;
- } else {
- /* TODO: Can you legitimately receive chunks out of order? */
- g_hash_table_remove(cmdproc->multiparts, messageId);
+ "Unable to find first chunk of message_id '%s' to correspond with chunk %d.\n",
+ message_id, chunk + 1);
+ } else if (first->received_chunks != chunk) {
+ /*
+ * We received an out of order chunk number (i.e. not the
+ * next one in the sequence). Not sure if this can happen
+ * legitimately, but we definitely don't handle it right
+ * now.
+ */
+ g_hash_table_remove(cmdproc->multiparts, message_id);
return;
}
+
+ /* Chunk is from 1 to total-1 (doesn't count first one) */
+ purple_debug_info("msn", "Received chunk %d of %d, message_id: '%s'\n",
+ chunk + 1, first->total_chunks, message_id);
+ first->body = g_realloc(first->body, first->body_len + msg->body_len);
+ memcpy(first->body + first->body_len, msg->body, msg->body_len);
+ first->body_len += msg->body_len;
+ first->received_chunks++;
+ if (first->received_chunks != first->total_chunks)
+ /* We're waiting for more chunks */
+ return;
+
+ /*
+ * We have all the chunks for this message, great! Send
+ * it along... The caller takes care of freeing the old one.
+ */
+ msg = first;
} else {
- purple_debug_error("msn", "Received MessageId '%s' with no chunk number!\n", messageId);
+ purple_debug_error("msn", "Received MessageId '%s' with no chunk number!\n", message_id);
}
}
}
@@ -314,8 +263,8 @@ msn_cmdproc_process_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
purple_debug_warning("msn", "Unhandled content-type '%s'\n",
msn_message_get_content_type(msg));
- if (messageId != NULL)
- g_hash_table_remove(cmdproc->multiparts, messageId);
+ if (message_id != NULL)
+ g_hash_table_remove(cmdproc->multiparts, message_id);
}
void
diff --git a/libpurple/protocols/msn/cmdproc.h b/libpurple/protocols/msn/cmdproc.h
index 4b13087861..68de2451ec 100644
--- a/libpurple/protocols/msn/cmdproc.h
+++ b/libpurple/protocols/msn/cmdproc.h
@@ -27,7 +27,6 @@
typedef struct _MsnCmdProc MsnCmdProc;
#include "command.h"
-#include "error.h"
#include "history.h"
#include "servconn.h"
#include "session.h"
@@ -51,18 +50,45 @@ struct _MsnCmdProc
void *data; /**< Extra data, like the switchboard. */
};
+/**
+ * Creates a MsnCmdProc structure.
+ *
+ * @param session The session to associate with.
+ *
+ * @return A new MsnCmdProc structure.
+ */
MsnCmdProc *msn_cmdproc_new(MsnSession *session);
+
+/**
+ * Destroys an MsnCmdProc.
+ *
+ * @param cmdproc The object structure.
+ */
void msn_cmdproc_destroy(MsnCmdProc *cmdproc);
+/**
+ * Process the queued transactions.
+ *
+ * @param cmdproc The MsnCmdProc.
+ */
void msn_cmdproc_process_queue(MsnCmdProc *cmdproc);
+/**
+ * Sends transaction using this servconn.
+ *
+ * @param cmdproc The MsnCmdProc to be used.
+ * @param trans The MsnTransaction to be sent.
+ */
void msn_cmdproc_send_trans(MsnCmdProc *cmdproc, MsnTransaction *trans);
+
+/**
+ * Add a transaction to the queue to be processed latter.
+ *
+ * @param cmdproc The MsnCmdProc in which the transaction will be queued.
+ * @param trans The MsnTransaction to be queued.
+ */
void msn_cmdproc_queue_trans(MsnCmdProc *cmdproc,
MsnTransaction *trans);
-void msn_cmdproc_send(MsnCmdProc *cmdproc, const char *command,
- const char *format, ...);
-void msn_cmdproc_send_quick(MsnCmdProc *cmdproc, const char *command,
- const char *format, ...);
void msn_cmdproc_process_msg(MsnCmdProc *cmdproc,
MsnMessage *msg);
diff --git a/libpurple/protocols/msn/command.c b/libpurple/protocols/msn/command.c
index 1ef24a9acf..c27669584b 100644
--- a/libpurple/protocols/msn/command.c
+++ b/libpurple/protocols/msn/command.c
@@ -21,7 +21,8 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+#include "internal.h"
+
#include "command.h"
static gboolean
diff --git a/libpurple/protocols/msn/command.h b/libpurple/protocols/msn/command.h
index fdd82f9324..48fc7dccda 100644
--- a/libpurple/protocols/msn/command.h
+++ b/libpurple/protocols/msn/command.h
@@ -54,9 +54,39 @@ struct _MsnCommand
void *payload_cbdata;
};
+/**
+ * Create a command object from the incoming string and ref it.
+ *
+ * @param string The incoming string.
+ *
+ * @return A MsnCommand object.
+ */
MsnCommand *msn_command_from_string(const char *string);
+
+/**
+ * Destroy a MsnCommand object if its ref count is zero, otherwise
+ * just unref it.
+ *
+ * @param cmd The MsnCommand to be destroyed.
+ */
void msn_command_destroy(MsnCommand *cmd);
+
+/**
+ * Increment the ref count.
+ *
+ * @param cmd The MsnCommand to be ref.
+ *
+ * @return The ref command.
+ */
MsnCommand *msn_command_ref(MsnCommand *cmd);
+
+/**
+ * Decrement the ref count. If the count goes to 0, destroy it.
+ *
+ * @param cmd The MsnCommand to be unref.
+ *
+ * @return The ref command.
+ */
MsnCommand *msn_command_unref(MsnCommand *cmd);
#endif /* MSN_COMMAND_H */
diff --git a/libpurple/protocols/msn/contact.c b/libpurple/protocols/msn/contact.c
index fd01527c3b..da25797e4b 100644
--- a/libpurple/protocols/msn/contact.c
+++ b/libpurple/protocols/msn/contact.c
@@ -24,12 +24,16 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#include "msn.h"
+#include "internal.h"
+#include "debug.h"
+
#include "contact.h"
#include "xmlnode.h"
#include "group.h"
+#include "msnutils.h"
#include "soap.h"
#include "nexus.h"
+#include "user.h"
const char *MsnSoapPartnerScenarioText[] =
{
@@ -1172,7 +1176,7 @@ msn_add_contact_to_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL);
msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL);
- if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) {
+ if (msn_user_is_in_list(user, MSN_LIST_PL)) {
msn_del_contact_from_list(state->session, NULL, state->who, MSN_LIST_PL);
return;
}
diff --git a/libpurple/protocols/msn/dialog.c b/libpurple/protocols/msn/dialog.c
deleted file mode 100644
index 48669d0597..0000000000
--- a/libpurple/protocols/msn/dialog.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/**
- * @file dialog.c Dialog functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#include "msn.h"
-#include "dialog.h"
-
-typedef struct
-{
- PurpleConnection *gc;
- char *who;
- char *group;
- gboolean add;
-
-} MsnAddRemData;
-
-/* 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)
-{
- PurpleBuddy *buddy;
- PurpleGroup *group = NULL;
-
- 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)
-{
-#if 0
- /* this *should* be necessary !! */
- msn_complete_sync_issue(data);
-#endif
-
- 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, data->group);
- }
-
- g_free(data->group);
- g_free(data->who);
- g_free(data);
-}
-
-static void
-msn_rem_cb(MsnAddRemData *data)
-{
- msn_complete_sync_issue(data);
-
- 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->who);
- g_free(data);
-}
-
-void
-msn_show_sync_issue(MsnSession *session, const char *passport,
- const char *group_name)
-{
- PurpleConnection *gc;
- PurpleAccount *account;
- MsnAddRemData *data;
- char *msg, *reason;
-
- account = session->account;
- gc = purple_account_get_connection(account);
-
- data = g_new0(MsnAddRemData, 1);
- data->who = g_strdup(passport);
- data->group = g_strdup(group_name);
- data->gc = gc;
-
- msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"),
- purple_account_get_username(account),
- purple_account_get_protocol_name(account));
-
- if (group_name != NULL)
- {
- reason = g_strdup_printf(_("%s on the local list is "
- "inside the group \"%s\" but not on "
- "the server list. "
- "Do you want this buddy to be added?"),
- passport, group_name);
- }
- else
- {
- reason = g_strdup_printf(_("%s is on the local list but "
- "not on the server list. "
- "Do you want this buddy to be added?"),
- passport);
- }
-
- purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), data->who, NULL,
- data, 2,
- _("Yes"), G_CALLBACK(msn_add_cb),
- _("No"), G_CALLBACK(msn_rem_cb));
-
- g_free(reason);
- g_free(msg);
-}
diff --git a/libpurple/protocols/msn/dialog.h b/libpurple/protocols/msn/dialog.h
deleted file mode 100644
index 2fc9b23ada..0000000000
--- a/libpurple/protocols/msn/dialog.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * @file dialog.h Dialog functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-#ifndef MSN_DIALOG_H
-#define MSN_DIALOG_H
-
-void msn_show_sync_issue(MsnSession *session, const char *passport,
- const char *group_name);
-
-#endif /* MSN_DIALOG_H */
diff --git a/libpurple/protocols/msn/directconn.c b/libpurple/protocols/msn/directconn.c
index 6fb7fb72fd..7cfa24c927 100644
--- a/libpurple/protocols/msn/directconn.c
+++ b/libpurple/protocols/msn/directconn.c
@@ -21,30 +21,20 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
+#include "internal.h"
+#include "cipher.h"
+#include "debug.h"
+
#include "msn.h"
#include "directconn.h"
#include "slp.h"
#include "slpmsg.h"
+#include "p2p.h"
-#pragma pack(push,1)
-typedef struct {
- guint32 session_id;
- guint32 seq_id;
- guint64 offset;
- guint64 total_size;
- guint32 length;
- guint32 flags;
- guint32 ack_id;
- guint32 ack_uid;
- guint64 ack_size;
-/* guint8 body[1]; */
-} MsnDcContext;
-#pragma pack(pop)
-
-#define DC_PACKET_HEADER_SIZE sizeof(MsnDcContext)
#define DC_MAX_BODY_SIZE 8*1024
-#define DC_MAX_PACKET_SIZE (DC_PACKET_HEADER_SIZE + DC_MAX_BODY_SIZE)
+#define DC_MAX_PACKET_SIZE (P2P_PACKET_HEADER_SIZE + DC_MAX_BODY_SIZE)
static void
msn_dc_calculate_nonce_hash(MsnDirectConnNonceType type,
@@ -121,8 +111,12 @@ msn_dc_destroy_packet(MsnDirectConnPacket *p)
{
g_free(p->data);
+#if 0
if (p->msg)
msn_message_unref(p->msg);
+#endif
+ if (p->part)
+ msn_slpmsgpart_unref(p->part);
g_free(p);
}
@@ -191,7 +185,7 @@ msn_dc_destroy(MsnDirectConn *dc)
if (slplink) {
slplink->dc = NULL;
if (slplink->swboard == NULL)
- msn_slplink_destroy(slplink);
+ msn_slplink_unref(slplink);
}
g_free(dc->msg_body);
@@ -353,7 +347,7 @@ msn_dc_fallback_to_sb(MsnDirectConn *dc)
if (queue) {
while (!g_queue_is_empty(queue)) {
MsnDirectConnPacket *p = g_queue_pop_head(queue);
- msn_slplink_send_msg(slplink, p->msg);
+ msn_slplink_send_msgpart(slplink, (MsnSlpMessage*)p->part->ack_data);
msn_dc_destroy_packet(p);
}
g_queue_free(queue);
@@ -363,51 +357,6 @@ msn_dc_fallback_to_sb(MsnDirectConn *dc)
}
static void
-msn_dc_parse_binary_header(MsnDirectConn *dc)
-{
- MsnSlpHeader *h;
- MsnDcContext *context;
-
- g_return_if_fail(dc != NULL);
-
- h = &dc->header;
- /* Skip packet size */
- context = (MsnDcContext *)(dc->in_buffer + 4);
-
- h->session_id = GUINT32_FROM_LE(context->session_id);
- h->id = GUINT32_FROM_LE(context->seq_id);
- h->offset = GUINT64_FROM_LE(context->offset);
- h->total_size = GUINT64_FROM_LE(context->total_size);
- h->length = GUINT32_FROM_LE(context->length);
- h->flags = GUINT32_FROM_LE(context->flags);
- h->ack_id = GUINT32_FROM_LE(context->ack_id);
- h->ack_sub_id = GUINT32_FROM_LE(context->ack_uid);
- h->ack_size = GUINT64_FROM_LE(context->ack_size);
-}
-
-static const gchar *
-msn_dc_serialize_binary_header(MsnDirectConn *dc) {
- MsnSlpHeader *h;
- static MsnDcContext bin_header;
-
- g_return_val_if_fail(dc != NULL, NULL);
-
- h = &dc->header;
-
- bin_header.session_id = GUINT32_TO_LE(h->session_id);
- bin_header.seq_id = GUINT32_TO_LE(h->id);
- bin_header.offset = GUINT64_TO_LE(h->offset);
- bin_header.total_size = GUINT64_TO_LE(h->total_size);
- bin_header.length = GUINT32_TO_LE(h->length);
- bin_header.flags = GUINT32_TO_LE(h->flags);
- bin_header.ack_id = GUINT32_TO_LE(h->ack_id);
- bin_header.ack_uid = GUINT32_TO_LE(h->ack_sub_id);
- bin_header.ack_size = GUINT64_TO_LE(h->ack_size);
-
- return (const gchar *)&bin_header;
-}
-
-static void
msn_dc_send_cb(gpointer data, gint fd, PurpleInputCondition cond)
{
MsnDirectConn *dc = data;
@@ -502,10 +451,11 @@ msn_dc_send_handshake_with_nonce(MsnDirectConn *dc, MsnDirectConnPacket *p)
{
const gchar *h;
- h = msn_dc_serialize_binary_header(dc);
- memcpy(p->data, h, DC_PACKET_HEADER_SIZE);
+ h = (gchar*) msn_p2p_header_to_wire(&dc->header);
+
+ memcpy(p->data, h, P2P_PACKET_HEADER_SIZE);
- memcpy(p->data + offsetof(MsnDcContext, ack_id), dc->nonce, 16);
+ memcpy(p->data + offsetof(MsnP2PHeader, ack_id), dc->nonce, 16);
msn_dc_enqueue_packet(dc, p);
}
@@ -515,7 +465,7 @@ msn_dc_send_handshake(MsnDirectConn *dc)
{
MsnDirectConnPacket *p;
- p = msn_dc_new_packet(DC_PACKET_HEADER_SIZE);
+ p = msn_dc_new_packet(P2P_PACKET_HEADER_SIZE);
dc->header.session_id = 0;
dc->header.id = dc->slpcall->slplink->slp_seq_id++;
@@ -532,7 +482,7 @@ msn_dc_send_handshake_reply(MsnDirectConn *dc)
{
MsnDirectConnPacket *p;
- p = msn_dc_new_packet(DC_PACKET_HEADER_SIZE);
+ p = msn_dc_new_packet(P2P_PACKET_HEADER_SIZE);
dc->header.id = dc->slpcall->slplink->slp_seq_id++;
dc->header.length = 0;
@@ -546,10 +496,10 @@ msn_dc_verify_handshake(MsnDirectConn *dc, guint32 packet_length)
guchar nonce[16];
gchar nonce_hash[37];
- if (packet_length != DC_PACKET_HEADER_SIZE)
+ if (packet_length != P2P_PACKET_HEADER_SIZE)
return FALSE;
- memcpy(nonce, dc->in_buffer + 4 + offsetof(MsnDcContext, ack_id), 16);
+ memcpy(nonce, dc->in_buffer + 4 + offsetof(MsnP2PHeader, ack_id), 16);
if (dc->nonce_type == DC_NONCE_PLAIN) {
if (memcmp(dc->nonce, nonce, 16) == 0) {
@@ -589,6 +539,14 @@ msn_dc_verify_handshake(MsnDirectConn *dc, guint32 packet_length)
static void
msn_dc_send_packet_cb(MsnDirectConnPacket *p)
{
+ if (p->part != NULL && p->part->ack_cb != NULL)
+ p->part->ack_cb(p->part, p->part->ack_data);
+}
+
+#if 0
+static void
+msn_dc_send_packet_cb(MsnDirectConnPacket *p)
+{
if (p->msg != NULL && p->msg->ack_cb != NULL)
p->msg->ack_cb(p->msg, p->msg->ack_data);
}
@@ -599,21 +557,42 @@ msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg)
MsnDirectConnPacket *p;
guint32 length;
- length = msg->body_len + DC_PACKET_HEADER_SIZE;
+ length = msg->body_len + P2P_PACKET_HEADER_SIZE;
p = msn_dc_new_packet(length);
- memcpy(p->data, &msg->msnslp_header, DC_PACKET_HEADER_SIZE);
- memcpy(p->data + DC_PACKET_HEADER_SIZE, msg->body, msg->body_len);
+ memcpy(p->data, msg->slpmsg->header, P2P_PACKET_HEADER_SIZE);
+ memcpy(p->data + P2P_PACKET_HEADER_SIZE, msg->body, msg->body_len);
p->sent_cb = msn_dc_send_packet_cb;
p->msg = msn_message_ref(msg);
msn_dc_enqueue_packet(dc, p);
}
+#endif
+
+void
+msn_dc_enqueue_part(MsnDirectConn *dc, MsnSlpMessagePart *part)
+{
+ MsnDirectConnPacket *p;
+ guint32 length;
+
+ length = part->size + P2P_PACKET_HEADER_SIZE;
+ p = msn_dc_new_packet(length);
+
+ memcpy(p->data, part->header, P2P_PACKET_HEADER_SIZE);
+ memcpy(p->data + P2P_PACKET_HEADER_SIZE, part->buffer, part->size);
+
+ p->sent_cb = msn_dc_send_packet_cb;
+ p->part = part;
+
+ msn_dc_enqueue_packet(dc, p);
+}
static int
msn_dc_process_packet(MsnDirectConn *dc, guint32 packet_length)
{
+ MsnSlpMessagePart *part;
+
g_return_val_if_fail(dc != NULL, DC_PROCESS_ERROR);
switch (dc->state) {
@@ -650,12 +629,9 @@ msn_dc_process_packet(MsnDirectConn *dc, guint32 packet_length)
break;
case DC_STATE_ESTABLISHED:
- msn_slplink_process_msg(
- dc->slplink,
- &dc->header,
- dc->in_buffer + 4 + DC_PACKET_HEADER_SIZE,
- dc->header.length
- );
+
+ part = msn_slpmsgpart_new_from_data(dc->in_buffer + 4, dc->header.length);
+ msn_slplink_process_msg(dc->slplink, part);
/*
if (dc->num_calls == 0) {
@@ -727,7 +703,15 @@ msn_dc_recv_cb(gpointer data, gint fd, PurpleInputCondition cond)
return;
if (dc->state != DC_STATE_FOO) {
- msn_dc_parse_binary_header(dc);
+ MsnP2PHeader *context;
+ MsnP2PHeader *h;
+
+ /* Skip packet size */
+ context = (MsnP2PHeader *)(dc->in_buffer + 4);
+
+ h = msn_p2p_header_from_wire(context);
+ memcpy(&dc->header, h, P2P_PACKET_HEADER_SIZE);
+ g_free(h);
}
switch (msn_dc_process_packet(dc, packet_length)) {
diff --git a/libpurple/protocols/msn/directconn.h b/libpurple/protocols/msn/directconn.h
index 2aae22ec69..52e4548fc2 100644
--- a/libpurple/protocols/msn/directconn.h
+++ b/libpurple/protocols/msn/directconn.h
@@ -30,10 +30,13 @@ typedef struct _MsnDirectConn MsnDirectConn;
#include "proxy.h"
#include "circbuffer.h"
-#include "msg.h"
#include "slp.h"
#include "slplink.h"
#include "slpmsg.h"
+#include "slpmsg_part.h"
+#include "p2p.h"
+
+#define MSN_DCCONN_MAX_SIZE 1352
typedef enum
{
@@ -68,7 +71,7 @@ struct _MsnDirectConnPacket {
guchar *data;
void (*sent_cb)(struct _MsnDirectConnPacket*);
- MsnMessage *msg;
+ MsnSlpMessagePart *part;
};
struct _MsnDirectConn
@@ -100,7 +103,7 @@ struct _MsnDirectConn
GQueue *out_queue; /**< The outgoing packet queue */
int msg_pos; /**< The position of next byte to be sent in the actual packet */
- MsnSlpHeader header; /**< SLP header for parsing / serializing */
+ MsnP2PHeader header; /**< SLP header for parsing / serializing */
/** The callback used for sending information to the peer about the opened socket */
void (*send_connection_info_msg_cb)(MsnDirectConn *);
@@ -124,8 +127,12 @@ struct _MsnDirectConn
/*
* Queues an MSN message to be sent via direct connection.
*/
+#if 0
void
msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg);
+#endif
+void
+msn_dc_enqueue_part(MsnDirectConn *dc, MsnSlpMessagePart *part);
/*
* Creates, initializes, and returns a new MsnDirectConn structure.
diff --git a/libpurple/protocols/msn/error.c b/libpurple/protocols/msn/error.c
index fd13bea29f..0ca5299732 100644
--- a/libpurple/protocols/msn/error.c
+++ b/libpurple/protocols/msn/error.c
@@ -21,9 +21,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "debug.h"
+/* Masca: can we get rid of the sync issue dialog? */
+#include "request.h"
+
#include "error.h"
+typedef struct
+{
+ PurpleConnection *gc;
+ char *who;
+ char *group;
+ gboolean add;
+
+} MsnAddRemData;
+
const char *
msn_error_get_text(unsigned int type, gboolean *debug)
{
@@ -264,3 +278,115 @@ msn_error_handle(MsnSession *session, unsigned int type)
g_free(buf);
}
+/* 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)
+{
+ PurpleBuddy *buddy;
+ PurpleGroup *group = NULL;
+
+ 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)
+{
+#if 0
+ /* this *should* be necessary !! */
+ msn_complete_sync_issue(data);
+#endif
+
+ 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, data->group);
+ }
+
+ g_free(data->group);
+ g_free(data->who);
+ g_free(data);
+}
+
+static void
+msn_rem_cb(MsnAddRemData *data)
+{
+ msn_complete_sync_issue(data);
+
+ 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->who);
+ g_free(data);
+}
+
+void
+msn_error_sync_issue(MsnSession *session, const char *passport,
+ const char *group_name)
+{
+ PurpleConnection *gc;
+ PurpleAccount *account;
+ MsnAddRemData *data;
+ char *msg, *reason;
+
+ account = session->account;
+ gc = purple_account_get_connection(account);
+
+ data = g_new0(MsnAddRemData, 1);
+ data->who = g_strdup(passport);
+ data->group = g_strdup(group_name);
+ data->gc = gc;
+
+ msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"),
+ purple_account_get_username(account),
+ purple_account_get_protocol_name(account));
+
+ if (group_name != NULL)
+ {
+ reason = g_strdup_printf(_("%s on the local list is "
+ "inside the group \"%s\" but not on "
+ "the server list. "
+ "Do you want this buddy to be added?"),
+ passport, group_name);
+ }
+ else
+ {
+ reason = g_strdup_printf(_("%s is on the local list but "
+ "not on the server list. "
+ "Do you want this buddy to be added?"),
+ passport);
+ }
+
+ purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE,
+ purple_connection_get_account(gc), data->who, NULL,
+ data, 2,
+ _("Yes"), G_CALLBACK(msn_add_cb),
+ _("No"), G_CALLBACK(msn_rem_cb));
+
+ g_free(reason);
+ g_free(msg);
+}
diff --git a/libpurple/protocols/msn/error.h b/libpurple/protocols/msn/error.h
index 257bfe1414..5194eaaac3 100644
--- a/libpurple/protocols/msn/error.h
+++ b/libpurple/protocols/msn/error.h
@@ -44,4 +44,14 @@ const char *msn_error_get_text(unsigned int type, gboolean *debug);
*/
void msn_error_handle(MsnSession *session, unsigned int type);
+/**
+ * Show the sync issue in a dialog using request api
+ *
+ * @param sesion MsnSession associated to this error.
+ * @param passport The passport associated with the error.
+ * @param group_name The group in the buddy is suppoused to be
+ */
+void msn_error_sync_issue(MsnSession *session, const char *passport,
+ const char *group_name);
+
#endif /* MSN_ERROR_H */
diff --git a/libpurple/protocols/msn/group.h b/libpurple/protocols/msn/group.h
index 14776c9b17..620d24124f 100644
--- a/libpurple/protocols/msn/group.h
+++ b/libpurple/protocols/msn/group.h
@@ -26,7 +26,7 @@
typedef struct _MsnGroup MsnGroup;
-#include <stdio.h>
+#include "internal.h"
#include "session.h"
#include "user.h"
diff --git a/libpurple/protocols/msn/history.h b/libpurple/protocols/msn/history.h
index 8fdec6ec4d..7ceef1fce4 100644
--- a/libpurple/protocols/msn/history.h
+++ b/libpurple/protocols/msn/history.h
@@ -24,6 +24,8 @@
#ifndef MSN_HISTORY_H
#define MSN_HISTORY_H
+#include "internal.h"
+
typedef struct _MsnHistory MsnHistory;
#include "transaction.h"
diff --git a/libpurple/protocols/msn/httpconn.h b/libpurple/protocols/msn/httpconn.h
index a7bfd69e18..381a27b85c 100644
--- a/libpurple/protocols/msn/httpconn.h
+++ b/libpurple/protocols/msn/httpconn.h
@@ -28,6 +28,7 @@ typedef struct _MsnHttpConn MsnHttpConn;
#include "circbuffer.h"
#include "servconn.h"
+#include "session.h"
/**
* An HTTP Connection.
diff --git a/libpurple/protocols/msn/msg.c b/libpurple/protocols/msn/msg.c
index 57c4aba706..58c6475647 100644
--- a/libpurple/protocols/msn/msg.c
+++ b/libpurple/protocols/msn/msg.c
@@ -21,9 +21,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
+#include "internal.h"
+#include "debug.h"
+
#include "msn.h"
#include "msg.h"
#include "msnutils.h"
+#include "slpmsg.h"
+#include "slpmsg_part.h"
MsnMessage *
msn_message_new(MsnMsgType type)
@@ -36,7 +42,7 @@ msn_message_new(MsnMsgType type)
if (purple_debug_is_verbose())
purple_debug_info("msn", "message new (%p)(%d)\n", msg, type);
- msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ msg->header_table = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_free);
msn_message_ref(msg);
@@ -64,8 +70,9 @@ msn_message_destroy(MsnMessage *msg)
g_free(msg->content_type);
g_free(msg->charset);
- g_hash_table_destroy(msg->attr_table);
- g_list_free(msg->attr_list);
+ g_hash_table_destroy(msg->header_table);
+ g_list_free(msg->header_list);
+ msn_slpmsgpart_destroy(msg->part);
g_free(msg);
}
@@ -112,11 +119,11 @@ msn_message_new_plain(const char *message)
msg = msn_message_new(MSN_MSG_TEXT);
msg->retries = 1;
- msn_message_set_attr(msg, "User-Agent", PACKAGE_NAME "/" VERSION);
+ msn_message_set_header(msg, "User-Agent", PACKAGE_NAME "/" VERSION);
msn_message_set_content_type(msg, "text/plain");
msn_message_set_charset(msg, "UTF-8");
msn_message_set_flag(msg, 'A');
- msn_message_set_attr(msg, "X-MMS-IM-Format",
+ msn_message_set_header(msg, "X-MMS-IM-Format",
"FN=Segoe%20UI; EF=; CO=0; CS=1;PF=0");
message_cr = purple_str_add_cr(message);
@@ -133,7 +140,7 @@ msn_message_new_msnslp(void)
msg = msn_message_new(MSN_MSG_SLP);
- msn_message_set_attr(msg, "User-Agent", NULL);
+ msn_message_set_header(msg, "User-Agent", NULL);
msg->msnslp_message = TRUE;
@@ -157,46 +164,6 @@ msn_message_new_nudge(void)
}
void
-msn_message_parse_slp_body(MsnMessage *msg, const char *body, size_t len)
-{
- MsnSlpHeader header;
- const char *tmp;
- int body_len;
-
- tmp = body;
-
- if (len < sizeof(header)) {
- g_return_if_reached();
- }
-
- /* Import the header. */
- memcpy(&header, tmp, sizeof(header));
- tmp += sizeof(header);
-
- msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id);
- msg->msnslp_header.id = GUINT32_FROM_LE(header.id);
- msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset);
- msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size);
- msg->msnslp_header.length = GUINT32_FROM_LE(header.length);
- msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags);
- msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id);
- msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id);
- msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size);
-
- /* Import the body. */
- body_len = len - (tmp - body);
- /* msg->body_len = msg->msnslp_header.length; */
-
- if (body_len > 0) {
- msg->body_len = len - (tmp - body);
- msg->body = g_malloc(msg->body_len + 1);
- memcpy(msg->body, tmp, msg->body_len);
- msg->body[msg->body_len] = '\0';
- tmp += body_len;
- }
-}
-
-void
msn_message_parse_payload(MsnMessage *msg,
const char *payload, size_t payload_len,
const char *line_dem,const char *body_dem)
@@ -211,7 +178,7 @@ msn_message_parse_payload(MsnMessage *msg,
memcpy(tmp_base, payload, payload_len);
tmp_base[payload_len] = '\0';
- /* Parse the attributes. */
+ /* Find the end of the headers */
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
@@ -222,8 +189,8 @@ msn_message_parse_payload(MsnMessage *msg,
}
*end = '\0';
+ /* Split the headers and parse each one */
elems = g_strsplit(tmp, line_dem, 0);
-
for (cur = elems; *cur != NULL; cur++)
{
const char *key, *value;
@@ -240,7 +207,7 @@ msn_message_parse_payload(MsnMessage *msg,
if (!strcmp(key, "boundary")) {
char *end = strchr(value, '\"');
*end = '\0';
- msn_message_set_attr(msg, key, value);
+ msn_message_set_header(msg, key, value);
}
g_strfreev(tokens);
@@ -278,12 +245,11 @@ msn_message_parse_payload(MsnMessage *msg,
}
else
{
- msn_message_set_attr(msg, key, value);
+ msn_message_set_header(msg, key, value);
}
g_strfreev(tokens);
}
-
g_strfreev(elems);
/* Proceed to the end of the "\r\n\r\n" */
@@ -293,70 +259,26 @@ msn_message_parse_payload(MsnMessage *msg,
content_type = msn_message_get_content_type(msg);
if (content_type != NULL &&
- !strcmp(content_type, "application/x-msnmsgrp2p"))
- {
- MsnSlpHeader header;
- MsnSlpFooter footer;
- int body_len;
-
- if (payload_len - (tmp - tmp_base) < sizeof(header)) {
- g_free(tmp_base);
- g_return_if_reached();
- }
-
+ !strcmp(content_type, "application/x-msnmsgrp2p")) {
msg->msnslp_message = TRUE;
+ msg->part = msn_slpmsgpart_new_from_data(tmp, payload_len - (tmp - tmp_base));
+ }
- /* Import the header. */
- memcpy(&header, tmp, sizeof(header));
- tmp += sizeof(header);
-
- msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id);
- msg->msnslp_header.id = GUINT32_FROM_LE(header.id);
- msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset);
- msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size);
- msg->msnslp_header.length = GUINT32_FROM_LE(header.length);
- msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags);
- msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id);
- msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id);
- msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size);
-
- body_len = payload_len - (tmp - tmp_base) - sizeof(footer);
-
- /* Import the body. */
- if (body_len > 0) {
- msg->body_len = body_len;
- g_free(msg->body);
- msg->body = g_malloc(msg->body_len + 1);
- memcpy(msg->body, tmp, msg->body_len);
- msg->body[msg->body_len] = '\0';
- tmp += body_len;
- }
-
- /* Import the footer. */
- if (body_len >= 0) {
- memcpy(&footer, tmp, sizeof(footer));
- tmp += sizeof(footer);
- msg->msnslp_footer.value = GUINT32_FROM_BE(footer.value);
- }
+ if (payload_len - (tmp - tmp_base) > 0) {
+ msg->body_len = payload_len - (tmp - tmp_base);
+ g_free(msg->body);
+ msg->body = g_malloc(msg->body_len + 1);
+ memcpy(msg->body, tmp, msg->body_len);
+ msg->body[msg->body_len] = '\0';
}
- else
- {
- if (payload_len - (tmp - tmp_base) > 0) {
- msg->body_len = payload_len - (tmp - tmp_base);
- g_free(msg->body);
- msg->body = g_malloc(msg->body_len + 1);
- memcpy(msg->body, tmp, msg->body_len);
- msg->body[msg->body_len] = '\0';
- }
-
- if ((!content_type || !strcmp(content_type, "text/plain"))
+
+ if ((!content_type || !strcmp(content_type, "text/plain"))
&& msg->charset == NULL) {
- char *body = g_convert(msg->body, msg->body_len, "UTF-8",
- "ISO-8859-1", NULL, &msg->body_len, NULL);
- g_free(msg->body);
- msg->body = body;
- msg->charset = g_strdup("UTF-8");
- }
+ char *body = g_convert(msg->body, msg->body_len, "UTF-8",
+ "ISO-8859-1", NULL, &msg->body_len, NULL);
+ g_free(msg->body);
+ msg->body = body;
+ msg->charset = g_strdup("UTF-8");
}
g_free(tmp_base);
@@ -381,43 +303,10 @@ msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd)
char *
msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size)
{
- MsnSlpHeader header;
-
- char *tmp, *base;
- const void *body;
- size_t len, body_len;
-
- g_return_val_if_fail(msg != NULL, NULL);
-
- len = MSN_BUF_LEN;
-
- base = tmp = g_malloc(len + 1);
-
- body = msn_message_get_bin_data(msg, &body_len);
-
- header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id);
- header.id = GUINT32_TO_LE(msg->msnslp_header.id);
- header.offset = GUINT64_TO_LE(msg->msnslp_header.offset);
- header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size);
- header.length = GUINT32_TO_LE(msg->msnslp_header.length);
- header.flags = GUINT32_TO_LE(msg->msnslp_header.flags);
- header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id);
- header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id);
- header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size);
-
- memcpy(tmp, &header, 48);
- tmp += 48;
+ char *tmp;
- if (body != NULL)
- {
- memcpy(tmp, body, body_len);
- tmp += body_len;
- }
-
- if (ret_size != NULL)
- *ret_size = tmp - base;
-
- return base;
+ tmp = msn_slpmsgpart_serialize(msg->part, ret_size);
+ return tmp;
}
char *
@@ -454,13 +343,13 @@ msn_message_gen_payload(MsnMessage *msg, size_t *ret_size)
n += strlen(n);
- for (l = msg->attr_list; l != NULL; l = l->next)
+ for (l = msg->header_list; l != NULL; l = l->next)
{
const char *key;
const char *value;
key = l->data;
- value = msn_message_get_attr(msg, key);
+ value = msn_message_get_header_value(msg, key);
g_snprintf(n, end - n, "%s: %s\r\n", key, value);
n += strlen(n);
@@ -472,33 +361,13 @@ msn_message_gen_payload(MsnMessage *msg, size_t *ret_size)
if (msg->msnslp_message)
{
- MsnSlpHeader header;
- MsnSlpFooter footer;
-
- header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id);
- header.id = GUINT32_TO_LE(msg->msnslp_header.id);
- header.offset = GUINT64_TO_LE(msg->msnslp_header.offset);
- header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size);
- header.length = GUINT32_TO_LE(msg->msnslp_header.length);
- header.flags = GUINT32_TO_LE(msg->msnslp_header.flags);
- header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id);
- header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id);
- header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size);
-
- memcpy(n, &header, 48);
- n += 48;
-
- if (body != NULL)
- {
- memcpy(n, body, body_len);
-
- n += body_len;
- }
-
- footer.value = GUINT32_TO_BE(msg->msnslp_footer.value);
+ size_t siz;
+ char *body;
+
+ body = msn_slpmsgpart_serialize(msg->part, &siz);
- memcpy(n, &footer, 4);
- n += 4;
+ memcpy(n, body, siz);
+ n += siz;
}
else
{
@@ -610,15 +479,15 @@ msn_message_get_charset(const MsnMessage *msg)
}
void
-msn_message_set_attr(MsnMessage *msg, const char *attr, const char *value)
+msn_message_set_header(MsnMessage *msg, const char *name, const char *value)
{
const char *temp;
- char *new_attr;
+ char *new_name;
g_return_if_fail(msg != NULL);
- g_return_if_fail(attr != NULL);
+ g_return_if_fail(name != NULL);
- temp = msn_message_get_attr(msg, attr);
+ temp = msn_message_get_header_value(msg, name);
if (value == NULL)
{
@@ -626,37 +495,37 @@ msn_message_set_attr(MsnMessage *msg, const char *attr, const char *value)
{
GList *l;
- for (l = msg->attr_list; l != NULL; l = l->next)
+ for (l = msg->header_list; l != NULL; l = l->next)
{
- if (!g_ascii_strcasecmp(l->data, attr))
+ if (!g_ascii_strcasecmp(l->data, name))
{
- msg->attr_list = g_list_remove(msg->attr_list, l->data);
+ msg->header_list = g_list_remove(msg->header_list, l->data);
break;
}
}
- g_hash_table_remove(msg->attr_table, attr);
+ g_hash_table_remove(msg->header_table, name);
}
return;
}
- new_attr = g_strdup(attr);
+ new_name = g_strdup(name);
- g_hash_table_insert(msg->attr_table, new_attr, g_strdup(value));
+ g_hash_table_insert(msg->header_table, new_name, g_strdup(value));
if (temp == NULL)
- msg->attr_list = g_list_append(msg->attr_list, new_attr);
+ msg->header_list = g_list_append(msg->header_list, new_name);
}
const char *
-msn_message_get_attr(const MsnMessage *msg, const char *attr)
+msn_message_get_header_value(const MsnMessage *msg, const char *name)
{
g_return_val_if_fail(msg != NULL, NULL);
- g_return_val_if_fail(attr != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
- return g_hash_table_lookup(msg->attr_table, attr);
+ return g_hash_table_lookup(msg->header_table, name);
}
GHashTable *
@@ -741,13 +610,13 @@ msn_message_show_readable(MsnMessage *msg, const char *info,
msg->content_type, msg->charset);
}
- for (l = msg->attr_list; l; l = l->next)
+ for (l = msg->header_list; l; l = l->next)
{
char *key;
const char *value;
key = l->data;
- value = msn_message_get_attr(msg, key);
+ value = msn_message_get_header_value(msg, key);
g_string_append_printf(str, "%s: %s\r\n", key, value);
}
@@ -758,15 +627,15 @@ msn_message_show_readable(MsnMessage *msg, const char *info,
if (msg->msnslp_message)
{
- g_string_append_printf(str, "Session ID: %u\r\n", msg->msnslp_header.session_id);
- g_string_append_printf(str, "ID: %u\r\n", msg->msnslp_header.id);
- g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.offset);
- g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.total_size);
- g_string_append_printf(str, "Length: %u\r\n", msg->msnslp_header.length);
- g_string_append_printf(str, "Flags: 0x%x\r\n", msg->msnslp_header.flags);
- g_string_append_printf(str, "ACK ID: %u\r\n", msg->msnslp_header.ack_id);
- g_string_append_printf(str, "SUB ID: %u\r\n", msg->msnslp_header.ack_sub_id);
- g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.ack_size);
+ g_string_append_printf(str, "Session ID: %u\r\n", msg->part->header->session_id);
+ g_string_append_printf(str, "ID: %u\r\n", msg->part->header->id);
+ g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", msg->part->header->offset);
+ g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", msg->part->header->total_size);
+ g_string_append_printf(str, "Length: %u\r\n", msg->part->header->length);
+ g_string_append_printf(str, "Flags: 0x%x\r\n", msg->part->header->flags);
+ g_string_append_printf(str, "ACK ID: %u\r\n", msg->part->header->ack_id);
+ g_string_append_printf(str, "SUB ID: %u\r\n", msg->part->header->ack_sub_id);
+ g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", msg->part->header->ack_size);
if (purple_debug_is_verbose() && body != NULL)
{
@@ -783,17 +652,27 @@ msn_message_show_readable(MsnMessage *msg, const char *info,
else
{
int i;
- for (i = 0; i < msg->body_len; i++)
+ int bin_len;
+
+ if (msg->part->footer->value == P2P_APPID_SESION)
+ bin_len = P2P_PACKET_HEADER_SIZE;
+ else
+ bin_len = body_len;
+
+ for (i = 0; i < bin_len; i++)
{
g_string_append_printf(str, "%.2hhX ", body[i]);
if ((i % 16) == 15)
g_string_append(str, "\r\n");
}
+
+ if (bin_len == P2P_PACKET_HEADER_SIZE)
+ g_string_append_printf(str, "%s ", body + P2P_PACKET_HEADER_SIZE);
g_string_append(str, "\r\n");
}
}
- g_string_append_printf(str, "Footer: %u\r\n", msg->msnslp_footer.value);
+ g_string_append_printf(str, "Footer: 0x%08X\r\n", msg->part->footer->value);
}
else
{
@@ -817,7 +696,6 @@ msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
PurpleConnection *gc;
const char *body;
- char *body_str;
char *body_enc;
char *body_final;
size_t body_len;
@@ -827,9 +705,7 @@ msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
gc = cmdproc->session->account->gc;
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);
+ body_enc = g_markup_escape_text(body, body_len);
passport = msg->remote_user;
@@ -840,13 +716,13 @@ msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
}
#if 0
- if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL)
+ if ((value = msn_message_get_header_value(msg, "User-Agent")) != NULL)
{
purple_debug_misc("msn", "User-Agent = '%s'\n", value);
}
#endif
- if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL)
+ if ((value = msn_message_get_header_value(msg, "X-MMS-IM-Format")) != NULL)
{
char *pre, *post;
@@ -887,8 +763,9 @@ msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
swboard->flag |= MSN_SB_FLAG_IM;
}
}
- else
+ else if (!g_str_equal(passport, purple_account_get_username(gc->account)))
{
+ /* Don't im ourselves ... */
serv_got_im(gc, passport, body_final, 0, time(NULL));
if (swboard->conv == NULL)
{
@@ -914,7 +791,7 @@ msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
gc = cmdproc->session->account->gc;
passport = msg->remote_user;
- if (msn_message_get_attr(msg, "TypingUser") == NULL)
+ if (msn_message_get_header_value(msg, "TypingUser") == NULL)
return;
if (cmdproc->servconn->type == MSN_SERVCONN_SB) {
@@ -1035,6 +912,149 @@ got_voiceclip_cb(MsnSlpCall *slpcall, const guchar *data, gsize size)
}
void
+msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+ MsnSession *session;
+ MsnSlpLink *slplink;
+ const char *data;
+ gsize len;
+
+ session = cmdproc->servconn->session;
+ slplink = msn_session_get_slplink(session, msg->remote_user);
+
+ if (slplink->swboard == NULL)
+ {
+ /*
+ * We will need swboard in order to change its flags. If its
+ * NULL, something has probably gone wrong earlier on. I
+ * didn't want to do this, but MSN 7 is somehow causing us
+ * to crash here, I couldn't reproduce it to debug more,
+ * and people are reporting bugs. Hopefully this doesn't
+ * cause more crashes. Stu.
+ */
+ if (cmdproc->data == NULL)
+ g_warning("msn_p2p_msg cmdproc->data was NULL\n");
+ else {
+ slplink->swboard = (MsnSwitchBoard *)cmdproc->data;
+ slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
+ }
+ }
+
+ data = msn_message_get_bin_data(msg, &len);
+
+ if (msg->part) {
+ msn_slplink_process_msg(slplink, msg->part);
+ }
+ else /* This should never happen. */
+ purple_debug_fatal("msn", "P2P message without a Part.\n");
+}
+
+static void
+got_emoticon(MsnSlpCall *slpcall,
+ const guchar *data, gsize size)
+{
+ PurpleConversation *conv;
+ MsnSwitchBoard *swboard;
+
+ swboard = slpcall->slplink->swboard;
+ conv = swboard->conv;
+
+ if (conv) {
+ /* 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
+ */
+ purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
+ purple_conv_custom_smiley_close(conv, slpcall->data_info );
+ }
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
+}
+
+void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
+{
+ MsnSession *session;
+ MsnSlpLink *slplink;
+ MsnSwitchBoard *swboard;
+ MsnObject *obj;
+ char **tokens;
+ char *smile, *body_str;
+ const char *body, *who, *sha1;
+ guint tok;
+ size_t body_len;
+
+ PurpleConversation *conv;
+
+ session = cmdproc->servconn->session;
+
+ if (!purple_account_get_bool(session->account, "custom_smileys", TRUE))
+ return;
+
+ swboard = cmdproc->data;
+ conv = swboard->conv;
+
+ body = msn_message_get_bin_data(msg, &body_len);
+ if (!body || !body_len)
+ return;
+ body_str = g_strndup(body, body_len);
+
+ /* MSN Messenger 7 may send more than one MSNObject in a single message...
+ * Maybe 10 tokens is a reasonable max value. */
+ tokens = g_strsplit(body_str, "\t", 10);
+
+ g_free(body_str);
+
+ for (tok = 0; tok < 9; tok += 2) {
+ if (tokens[tok] == NULL || tokens[tok + 1] == NULL) {
+ break;
+ }
+
+ smile = tokens[tok];
+ obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1]));
+
+ if (obj == NULL)
+ break;
+
+ who = msn_object_get_creator(obj);
+ sha1 = msn_object_get_sha1(obj);
+
+ slplink = msn_session_get_slplink(session, who);
+ if (slplink->swboard != swboard) {
+ if (slplink->swboard != NULL)
+ /*
+ * Apparently we're using a different switchboard now or
+ * something? I don't know if this is normal, but it
+ * definitely happens. So make sure the old switchboard
+ * doesn't still have a reference to us.
+ */
+ slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
+ slplink->swboard = swboard;
+ slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
+ }
+
+ /* If the conversation doesn't exist then this is a custom smiley
+ * used in the first message in a MSN conversation: we need to create
+ * the conversation now, otherwise the custom smiley won't be shown.
+ * This happens because every GtkIMHtml has its own smiley tree: if
+ * the conversation doesn't exist then we cannot associate the new
+ * smiley with its GtkIMHtml widget. */
+ if (!conv) {
+ conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who);
+ }
+
+ if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) {
+ msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
+ }
+
+ msn_object_destroy(obj);
+ obj = NULL;
+ who = NULL;
+ sha1 = NULL;
+ }
+ g_strfreev(tokens);
+}
+
+void
msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
GHashTable *body;
diff --git a/libpurple/protocols/msn/msg.h b/libpurple/protocols/msn/msg.h
index b75dbf19cc..be55c5aaee 100644
--- a/libpurple/protocols/msn/msg.h
+++ b/libpurple/protocols/msn/msg.h
@@ -58,6 +58,8 @@ typedef enum
#include "session.h"
#include "transaction.h"
#include "user.h"
+#include "slpmsg.h"
+#include "slpmsg_part.h"
typedef void (*MsnMsgCb)(MsnMessage *, void *data);
@@ -67,24 +69,6 @@ typedef void (*MsnMsgCb)(MsnMessage *, void *data);
#define MSG_OIM_BODY_DEM "\n\n"
#define MSG_OIM_LINE_DEM "\n"
-typedef struct
-{
- guint32 session_id;
- guint32 id;
- guint64 offset;
- guint64 total_size;
- guint32 length;
- guint32 flags;
- guint32 ack_id;
- guint32 ack_sub_id;
- guint64 ack_size;
-} MsnSlpHeader;
-
-typedef struct
-{
- guint32 value;
-} MsnSlpFooter;
-
/**
* A message.
*/
@@ -95,6 +79,8 @@ struct _MsnMessage
MsnMsgType type;
gboolean msnslp_message;
+ MsnSlpMessage *slpmsg;
+ MsnSlpMessagePart *part;
char *remote_user;
char flag;
@@ -106,11 +92,8 @@ struct _MsnMessage
guint total_chunks; /**< How many chunks in this multi-part message */
guint received_chunks; /**< How many chunks we've received so far */
- MsnSlpHeader msnslp_header;
- MsnSlpFooter msnslp_footer;
-
- GHashTable *attr_table;
- GList *attr_list;
+ GHashTable *header_table;
+ GList *header_list;
gboolean ack_ref; /**< A flag that states if this message has
been ref'ed for using it in a callback. */
@@ -295,24 +278,24 @@ void msn_message_set_charset(MsnMessage *msg, const char *charset);
const char *msn_message_get_charset(const MsnMessage *msg);
/**
- * Sets an attribute in a message.
+ * Sets a header in a message.
*
- * @param msg The message.
- * @param attr The attribute name.
- * @param value The attribute value.
+ * @param msg The message.
+ * @param header The header name.
+ * @param value The header value.
*/
-void msn_message_set_attr(MsnMessage *msg, const char *attr,
+void msn_message_set_header(MsnMessage *msg, const char *name,
const char *value);
/**
- * Returns an attribute from a message.
+ * Returns the value of a header from a message.
*
- * @param msg The message.
- * @param attr The attribute.
+ * @param msg The message.
+ * @param header The header value.
*
* @return The value, or @c NULL if not found.
*/
-const char *msn_message_get_attr(const MsnMessage *msg, const char *attr);
+const char *msn_message_get_header_value(const MsnMessage *msg, const char *name);
/**
* Parses the body and returns it in the form of a hashtable.
@@ -326,9 +309,6 @@ GHashTable *msn_message_get_hashtable_from_body(const MsnMessage *msg);
void msn_message_show_readable(MsnMessage *msg, const char *info,
gboolean text_body);
-void msn_message_parse_slp_body(MsnMessage *msg, const char *body,
- size_t len);
-
char *msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size);
char *msn_message_to_string(MsnMessage *msg);
@@ -337,8 +317,32 @@ void msn_plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
void msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+/**
+ * Processes peer to peer messages.
+ *
+ * @param cmdproc The command processor.
+ * @param msg The message.
+ */
+void msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+
+/**
+ * Processes emoticon messages.
+ *
+ * @param cmdproc The command processor.
+ * @param msg The message.
+ */
+void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+
void msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+/**
+ * Processes INVITE messages.
+ *
+ * @param cmdproc The command processor.
+ * @param msg The message.
+ */
+void msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
+
void msn_handwritten_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
#endif /* MSN_MSG_H */
diff --git a/libpurple/protocols/msn/msn.c b/libpurple/protocols/msn/msn.c
index 93e1db7337..15b31d8713 100644
--- a/libpurple/protocols/msn/msn.c
+++ b/libpurple/protocols/msn/msn.c
@@ -23,7 +23,11 @@
*/
#define PHOTO_SUPPORT 1
-#include "msn.h"
+#include "internal.h"
+
+#include "debug.h"
+#include "request.h"
+
#include "accountopt.h"
#include "contact.h"
#include "msg.h"
@@ -40,10 +44,10 @@
#include "msnutils.h"
#include "version.h"
+#include "error.h"
#include "msg.h"
#include "switchboard.h"
#include "notification.h"
-#include "sync.h"
#include "slplink.h"
#if PHOTO_SUPPORT
@@ -116,29 +120,6 @@ msn_normalize(const PurpleAccount *account, const char *str)
return buf;
}
-gboolean
-msn_email_is_valid(const char *passport)
-{
- if (purple_email_is_valid(passport)) {
- /* Special characters aren't allowed in domains, so only go to '@' */
- while (*passport != '@') {
- if (*passport == '/')
- return FALSE;
- else if (*passport == '?')
- return FALSE;
- else if (*passport == '=')
- return FALSE;
- /* MSN also doesn't like colons, but that's checked already */
-
- passport++;
- }
-
- return TRUE;
- }
-
- return FALSE;
-}
-
static gboolean
msn_send_attention(PurpleConnection *gc, const char *username, guint type)
{
@@ -267,9 +248,9 @@ msn_set_public_alias(PurpleConnection *pc, const char *alias,
{
MsnCmdProc *cmdproc;
MsnSession *session;
+ MsnTransaction *trans;
PurpleAccount *account;
const char *real_alias;
- MsnTransaction *trans;
struct public_alias_closure *closure;
session = purple_connection_get_protocol_data(pc);
@@ -360,19 +341,21 @@ msn_set_prp(PurpleConnection *gc, const char *type, const char *entry)
{
MsnCmdProc *cmdproc;
MsnSession *session;
+ MsnTransaction *trans;
session = gc->proto_data;
cmdproc = session->notification->cmdproc;
if (entry == NULL || *entry == '\0')
{
- msn_cmdproc_send(cmdproc, "PRP", "%s", type);
+ trans = msn_transaction_new(cmdproc, "PRP", "%s", type);
}
else
{
- msn_cmdproc_send(cmdproc, "PRP", "%s %s", type,
+ trans = msn_transaction_new(cmdproc, "PRP", "%s %s", type,
purple_url_encode(entry));
}
+ msn_cmdproc_send_trans(cmdproc, trans);
}
static void
@@ -478,7 +461,7 @@ msn_show_set_friendly_name(PurplePluginAction *action)
tmp = g_strdup_printf(_("Set friendly name for %s."),
purple_account_get_username(account));
- purple_request_input(gc, _("Set your friendly name."), tmp,
+ purple_request_input(gc, _("Set Friendly Name"), tmp,
_("This is the name that other MSN buddies will "
"see you as."),
purple_connection_get_display_name(gc), FALSE, FALSE, NULL,
@@ -489,6 +472,111 @@ msn_show_set_friendly_name(PurplePluginAction *action)
g_free(tmp);
}
+typedef struct MsnLocationData {
+ PurpleAccount *account;
+ MsnSession *session;
+ PurpleRequestFieldGroup *group;
+} MsnLocationData;
+
+static void
+update_endpoint_cb(MsnLocationData *data, PurpleRequestFields *fields)
+{
+ PurpleAccount *account;
+ MsnSession *session;
+ const char *old_name;
+ const char *name;
+ GList *others;
+
+ session = data->session;
+ account = data->account;
+
+ /* Update the current location's name */
+ old_name = purple_account_get_string(account, "endpoint-name", NULL);
+ name = purple_request_fields_get_string(fields, "endpoint-name");
+ if (!g_str_equal(old_name, name)) {
+ purple_account_set_string(account, "endpoint-name", name);
+ msn_notification_send_uux_private_endpointdata(session);
+ }
+
+ /* Sign out other locations */
+ for (others = purple_request_field_group_get_fields(data->group);
+ others;
+ others = g_list_next(others)) {
+ PurpleRequestField *field = others->data;
+ if (purple_request_field_get_type(field) != PURPLE_REQUEST_FIELD_BOOLEAN)
+ continue;
+ if (purple_request_field_bool_get_value(field)) {
+ const char *id = purple_request_field_get_id(field);
+ char *user;
+ purple_debug_info("msn", "Disconnecting Endpoint %s\n", id);
+
+ user = g_strdup_printf("%s;%s", purple_account_get_username(account), id);
+ msn_notification_send_uun(session, user, MSN_UNIFIED_NOTIFICATION_MPOP, "goawyplzthxbye");
+ g_free(user);
+ }
+ }
+
+ g_free(data);
+}
+
+static void
+msn_show_locations(PurplePluginAction *action)
+{
+ PurpleConnection *pc;
+ PurpleAccount *account;
+ MsnSession *session;
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+ GSList *l;
+ MsnLocationData *data;
+
+ pc = (PurpleConnection *)action->context;
+ account = purple_connection_get_account(pc);
+ session = purple_connection_get_protocol_data(pc);
+
+ fields = purple_request_fields_new();
+
+ group = purple_request_field_group_new(_("This Location"));
+ purple_request_fields_add_group(fields, group);
+ field = purple_request_field_label_new("endpoint-label", _("This is the name that identifies this location"));
+ purple_request_field_group_add_field(group, field);
+ field = purple_request_field_string_new("endpoint-name",
+ _("Name"),
+ purple_account_get_string(account, "endpoint-name", NULL),
+ FALSE);
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_group_add_field(group, field);
+
+ group = purple_request_field_group_new(_("Other Locations"));
+ purple_request_fields_add_group(fields, group);
+ field = purple_request_field_label_new("others-label", _("You can sign out from other locations here"));
+ purple_request_field_group_add_field(group, field);
+
+ for (l = session->user->endpoints; l; l = l->next) {
+ MsnUserEndpoint *ep = l->data;
+
+ if (g_str_equal(ep->id, session->guid))
+ /* Don't add myself to the list */
+ continue;
+
+ field = purple_request_field_bool_new(ep->id, ep->name, FALSE);
+ purple_request_field_group_add_field(group, field);
+ }
+
+ data = g_new0(MsnLocationData, 1);
+ data->account = account;
+ data->session = session;
+ data->group = group;
+
+ purple_request_fields(pc, NULL, NULL, NULL,
+ fields,
+ _("OK"), G_CALLBACK(update_endpoint_cb),
+ _("Cancel"), G_CALLBACK(g_free),
+ account, NULL, NULL,
+ data);
+}
+
static void
msn_show_set_home_phone(PurplePluginAction *action)
{
@@ -656,6 +744,7 @@ msn_send_privacy(PurpleConnection *gc)
PurpleAccount *account;
MsnSession *session;
MsnCmdProc *cmdproc;
+ MsnTransaction *trans;
account = purple_connection_get_account(gc);
session = gc->proto_data;
@@ -663,9 +752,11 @@ msn_send_privacy(PurpleConnection *gc)
if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL ||
account->perm_deny == PURPLE_PRIVACY_DENY_USERS)
- msn_cmdproc_send(cmdproc, "BLP", "%s", "AL");
+ trans = msn_transaction_new(cmdproc, "BLP", "%s", "AL");
else
- msn_cmdproc_send(cmdproc, "BLP", "%s", "BL");
+ trans = msn_transaction_new(cmdproc, "BLP", "%s", "BL");
+
+ msn_cmdproc_send_trans(cmdproc, trans);
}
static void
@@ -709,9 +800,7 @@ initiate_chat_cb(PurpleBlistNode *node, gpointer data)
static void
t_msn_xfer_init(PurpleXfer *xfer)
{
- MsnSlpLink *slplink = xfer->data;
- msn_slplink_request_ft(slplink, xfer);
- msn_slplink_unref(slplink);
+ msn_request_ft(xfer);
}
static void
@@ -1068,6 +1157,11 @@ msn_actions(PurplePlugin *plugin, gpointer context)
m = g_list_append(m, act);
m = g_list_append(m, NULL);
+ act = purple_plugin_action_new(_("View Locations..."),
+ msn_show_locations);
+ m = g_list_append(m, act);
+ m = g_list_append(m, NULL);
+
act = purple_plugin_action_new(_("Set Home Phone Number..."),
msn_show_set_home_phone);
m = g_list_append(m, act);
@@ -1202,6 +1296,13 @@ msn_login(PurpleAccount *account)
username = purple_account_get_string(account, "display-name", NULL);
purple_connection_set_display_name(gc, username);
+ if (purple_account_get_string(account, "endpoint-name", NULL) == NULL) {
+ GHashTable *ui_info = purple_core_get_ui_info();
+ const gchar *ui_name = ui_info ? g_hash_table_lookup(ui_info, "name") : NULL;
+ purple_account_set_string(account, "endpoint-name",
+ ui_name && *ui_name ? ui_name : PACKAGE_NAME);
+ }
+
if (!msn_session_connect(session, host, port, http_method))
purple_connection_error_reason(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
@@ -1412,7 +1513,7 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
msg = msn_message_new_plain(msgtext);
msg->remote_user = g_strdup(who);
- msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
+ msn_message_set_header(msg, "X-MMS-IM-Format", msgformat);
g_free(msgformat);
g_free(msgtext);
@@ -1445,7 +1546,7 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
body_enc = g_markup_escape_text(body_str, -1);
g_free(body_str);
- format = msn_message_get_attr(msg, "X-MMS-IM-Format");
+ format = msn_message_get_header_value(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 : "");
@@ -1520,7 +1621,7 @@ msn_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state)
msg = msn_message_new(MSN_MSG_TYPING);
msn_message_set_content_type(msg, "text/x-msmsgscontrol");
msn_message_set_flag(msg, 'U');
- msn_message_set_attr(msg, "TypingUser",
+ msn_message_set_header(msg, "TypingUser",
purple_account_get_username(account));
msn_message_set_bin_data(msg, "\r\n", 2);
@@ -1576,7 +1677,7 @@ add_pending_buddy(MsnSession *session,
MsnUser *user2 = msn_userlist_find_user(userlist, who);
if (user2 != NULL) {
/* User already in userlist, so just update it. */
- msn_user_destroy(user);
+ msn_user_unref(user);
user = user2;
} else {
msn_userlist_add_user(userlist, user);
@@ -1596,7 +1697,7 @@ add_pending_buddy(MsnSession *session,
/* Remove from local list */
purple_blist_remove_buddy(buddy);
- msn_user_destroy(user);
+ msn_user_unref(user);
}
g_free(group);
}
@@ -1927,7 +2028,7 @@ msn_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFl
}
msg = msn_message_new_plain(msgtext);
- msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat);
+ msn_message_set_header(msg, "X-MMS-IM-Format", msgformat);
smileys = msn_msg_grab_emoticons(msg->body, username);
while (smileys) {
@@ -1967,6 +2068,7 @@ static void
msn_keepalive(PurpleConnection *gc)
{
MsnSession *session;
+ MsnTransaction *trans;
session = gc->proto_data;
@@ -1976,7 +2078,9 @@ msn_keepalive(PurpleConnection *gc)
cmdproc = session->notification->cmdproc;
- msn_cmdproc_send_quick(cmdproc, "PNG", NULL, NULL);
+ trans = msn_transaction_new(cmdproc, "PNG", NULL);
+ msn_transaction_set_saveable(trans, FALSE);
+ msn_cmdproc_send_trans(cmdproc, trans);
}
}
@@ -2712,7 +2816,6 @@ static gboolean msn_load(PurplePlugin *plugin)
{
msn_notification_init();
msn_switchboard_init();
- msn_sync_init();
return TRUE;
}
@@ -2721,7 +2824,6 @@ static gboolean msn_unload(PurplePlugin *plugin)
{
msn_notification_end();
msn_switchboard_end();
- msn_sync_end();
return TRUE;
}
diff --git a/libpurple/protocols/msn/msn.h b/libpurple/protocols/msn/msn.h
index 1e597cf13d..cbd4d37ff5 100644
--- a/libpurple/protocols/msn/msn.h
+++ b/libpurple/protocols/msn/msn.h
@@ -26,16 +26,6 @@
typedef enum
{
- MSN_LIST_FL_OP = 0x01,
- MSN_LIST_AL_OP = 0x02,
- MSN_LIST_BL_OP = 0x04,
- MSN_LIST_RL_OP = 0x08,
- MSN_LIST_PL_OP = 0x10
-} MsnListOp;
-#define MSN_LIST_OP_MASK 0x07
-
-typedef enum
-{
MSN_CLIENT_CAP_WIN_MOBILE = 0x0000001,
MSN_CLIENT_CAP_INK_GIF = 0x0000004,
MSN_CLIENT_CAP_INK_ISF = 0x0000008,
@@ -83,23 +73,7 @@ typedef enum
#include "internal.h"
-#include "account.h"
-#include "accountopt.h"
-#include "blist.h"
-#include "connection.h"
-#include "conversation.h"
-#include "debug.h"
-#include "cipher.h"
-#include "notify.h"
-#include "privacy.h"
-#include "proxy.h"
-#include "prpl.h"
-#include "request.h"
-#include "servconn.h"
-#include "sslconn.h"
-#include "util.h"
-
-#include "ft.h"
+#include "session.h"
#include "msg.h"
@@ -109,9 +83,9 @@ typedef enum
#define MSN_SERVER "messenger.hotmail.com"
#define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com"
#define MSN_PORT 1863
-#define WLM_PROT_VER 15
+#define WLM_PROT_VER 16
-#define WLM_MAX_PROTOCOL 15
+#define WLM_MAX_PROTOCOL 16
#define WLM_MIN_PROTOCOL 15
#define MSN_TYPING_RECV_TIMEOUT 6
@@ -134,16 +108,14 @@ typedef enum
/* Index into attention_types */
#define MSN_NUDGE 0
-#define MSN_CLIENT_ID_VERSION MSN_CLIENT_VER_7_0
+#define MSN_CLIENT_ID_VERSION MSN_CLIENT_VER_9_0
#define MSN_CLIENT_ID_CAPABILITIES (MSN_CLIENT_CAP_PACKET|MSN_CLIENT_CAP_INK_GIF|MSN_CLIENT_CAP_VOICEIM)
+#define MSN_CLIENT_ID_EXT_CAPS (0)
#define MSN_CLIENT_ID \
((MSN_CLIENT_ID_VERSION << 24) | \
(MSN_CLIENT_ID_CAPABILITIES))
-#define MSN_CLIENT_EXT_ID 0
-
-gboolean msn_email_is_valid(const char *passport);
void
msn_set_public_alias(PurpleConnection *gc, const char *alias,
PurpleSetPublicAliasSuccessCallback success_cb,
diff --git a/libpurple/protocols/msn/msnutils.c b/libpurple/protocols/msn/msnutils.c
index f86cf3c647..e8032533a0 100644
--- a/libpurple/protocols/msn/msnutils.c
+++ b/libpurple/protocols/msn/msnutils.c
@@ -21,18 +21,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
+#include "internal.h"
+
#include "msn.h"
#include "msnutils.h"
#include "cipher.h"
-char *rand_guid(void);
-
/**************************************************************************
* Util
**************************************************************************/
char *
-rand_guid()
+rand_guid(void)
{
return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
rand() % 0xAAFF + 0x1111,
@@ -476,6 +477,29 @@ msn_parse_socket(const char *str, char **ret_host, int *ret_port)
*ret_port = port;
}
+gboolean
+msn_email_is_valid(const char *passport)
+{
+ if (purple_email_is_valid(passport)) {
+ /* Special characters aren't allowed in domains, so only go to '@' */
+ while (*passport != '@') {
+ if (*passport == '/')
+ return FALSE;
+ else if (*passport == '?')
+ return FALSE;
+ else if (*passport == '=')
+ return FALSE;
+ /* MSN also doesn't like colons, but that's checked already */
+
+ passport++;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/***************************************************************************
* MSN Challenge Computing Function
***************************************************************************/
diff --git a/libpurple/protocols/msn/msnutils.h b/libpurple/protocols/msn/msnutils.h
index 08c7f44bab..1548be395b 100644
--- a/libpurple/protocols/msn/msnutils.h
+++ b/libpurple/protocols/msn/msnutils.h
@@ -54,7 +54,32 @@ void msn_parse_format(const char *mime, char **pre_ret, char **post_ret);
*/
void msn_import_html(const char *html, char **attributes, char **message);
+/**
+ * Parses a socket string.
+ *
+ * @param str A host:port string.
+ * @param ret_host Return string value of the host.
+ * @param ret_port Return integer value of the port.
+ */
void msn_parse_socket(const char *str, char **ret_host, int *ret_port);
+
+/**
+ * Verify if the email is a vaild passport.
+ *
+ * @param passport The email
+ *
+ * @return True if it is a valid passport, else FALSE
+ */
+gboolean msn_email_is_valid(const char *passport);
+
+/**
+ * Handle MSN Challenge Computation
+ * This algorithm references
+ * http://imfreedom.org/wiki/index.php/MSN:NS/Challenges
+ *
+ * @param input Challenge input.
+ * @param output Callenge output.
+ */
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 b54301582c..b8a25ab86e 100644
--- a/libpurple/protocols/msn/nexus.c
+++ b/libpurple/protocols/msn/nexus.c
@@ -21,7 +21,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "cipher.h"
+#include "debug.h"
+
#include "soap.h"
#include "nexus.h"
#include "notification.h"
diff --git a/libpurple/protocols/msn/nexus.h b/libpurple/protocols/msn/nexus.h
index ab6f7f4ffa..e16bd10aa1 100644
--- a/libpurple/protocols/msn/nexus.h
+++ b/libpurple/protocols/msn/nexus.h
@@ -24,6 +24,8 @@
#ifndef MSN_NEXUS_H
#define MSN_NEXUS_H
+#include "internal.h"
+
typedef struct _MsnNexus MsnNexus;
typedef struct _MsnTicketToken MsnTicketToken;
typedef struct _MsnUsrKey MsnUsrKey;
diff --git a/libpurple/protocols/msn/notification.c b/libpurple/protocols/msn/notification.c
index 2f4e9ce054..98f33767ab 100644
--- a/libpurple/protocols/msn/notification.c
+++ b/libpurple/protocols/msn/notification.c
@@ -21,17 +21,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "cipher.h"
+#include "core.h"
+#include "debug.h"
+
#include "notification.h"
+
#include "contact.h"
-#include "state.h"
#include "error.h"
#include "msnutils.h"
-#include "page.h"
-
+#include "state.h"
#include "userlist.h"
-#include "sync.h"
-#include "slplink.h"
static MsnTable *cbs_table;
@@ -92,6 +94,8 @@ connect_cb(MsnServConn *servconn)
{
MsnCmdProc *cmdproc;
MsnSession *session;
+ MsnTransaction *trans;
+ PurpleAccount *account;
GString *vers;
const char *ver_str;
int i;
@@ -100,6 +104,7 @@ connect_cb(MsnServConn *servconn)
cmdproc = servconn->cmdproc;
session = servconn->session;
+ account = session->account;
vers = g_string_new("");
@@ -115,7 +120,8 @@ connect_cb(MsnServConn *servconn)
/* Skip the initial space */
ver_str = (vers->str + 1);
- msn_cmdproc_send(cmdproc, "VER", "%s", ver_str);
+ trans = msn_transaction_new(cmdproc, "VER", "%s", ver_str);
+ msn_cmdproc_send_trans(cmdproc, trans);
g_string_free(vers, TRUE);
}
@@ -154,30 +160,40 @@ void
msn_got_login_params(MsnSession *session, const char *ticket, const char *response)
{
MsnCmdProc *cmdproc;
+ MsnTransaction *trans;
cmdproc = session->notification->cmdproc;
msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END);
- msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s", ticket, response);
+ if (session->protocol_ver >= 16)
+ trans = msn_transaction_new(cmdproc, "USR", "SSO S %s %s %s", ticket, response, session->guid);
+ else
+ trans = msn_transaction_new(cmdproc, "USR", "SSO S %s %s", ticket, response);
+
+ msn_cmdproc_send_trans(cmdproc, trans);
}
static void
cvr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
PurpleAccount *account;
+ MsnTransaction *trans;
account = cmdproc->session->account;
- msn_cmdproc_send(cmdproc, "USR", "SSO I %s", purple_account_get_username(account));
+ trans = msn_transaction_new(cmdproc, "USR", "SSO I %s", purple_account_get_username(account));
+ msn_cmdproc_send_trans(cmdproc, trans);
}
static void
usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
+ PurpleAccount *account;
session = cmdproc->session;
+ account = session->account;
if (!g_ascii_strcasecmp(cmd->params[1], "OK"))
{
@@ -227,22 +243,25 @@ static void
ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
+ MsnTransaction *trans;
PurpleAccount *account;
gboolean protocol_supported = FALSE;
- char proto_str[8];
+ int proto_ver;
size_t i;
session = cmdproc->session;
account = session->account;
- g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver);
-
+ session->protocol_ver = 0;
for (i = 1; i < cmd->param_count; i++)
{
- if (!strcmp(cmd->params[i], proto_str))
- {
- protocol_supported = TRUE;
- break;
+ if (sscanf(cmd->params[i], "MSNP%d", &proto_ver) == 1) {
+ if (proto_ver >= WLM_MIN_PROTOCOL
+ && proto_ver <= WLM_MAX_PROTOCOL
+ && proto_ver > session->protocol_ver) {
+ protocol_supported = TRUE;
+ session->protocol_ver = proto_ver;
+ }
}
}
@@ -253,15 +272,18 @@ ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
return;
}
+ purple_debug_info("msn", "Negotiated protocol version %d with the server.\n", session->protocol_ver);
+
/*
* Windows Live Messenger 8.5
* Notice :CVR String discriminate!
* reference of http://www.microsoft.com/globaldev/reference/oslocversion.mspx
* to see the Local ID
*/
- msn_cmdproc_send(cmdproc, "CVR",
+ trans = msn_transaction_new(cmdproc, "CVR",
"0x0409 winnt 5.1 i386 MSNMSGR 8.5.1302 BC01 %s",
purple_account_get_username(account));
+ msn_cmdproc_send_trans(cmdproc, trans);
}
/**************************************************************************
@@ -283,12 +305,16 @@ out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
void
msn_notification_close(MsnNotification *notification)
{
+ MsnTransaction *trans;
+
g_return_if_fail(notification != NULL);
if (!notification->in_use)
return;
- msn_cmdproc_send_quick(notification->cmdproc, "OUT", NULL, NULL);
+ trans = msn_transaction_new(notification->cmdproc, "OUT", NULL);
+ msn_transaction_set_saveable(trans, FALSE);
+ msn_cmdproc_send_trans(notification->cmdproc, trans);
msn_notification_disconnect(notification);
}
@@ -996,15 +1022,18 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
PurpleAccount *account;
+ PurpleConnection *gc;
MsnUser *user;
MsnObject *msnobj = NULL;
- unsigned long clientid;
+ unsigned long clientid, extcaps;
+ char *extcap_str;
int networkid = 0;
const char *state, *passport;
char *friendly;
session = cmdproc->session;
account = session->account;
+ gc = purple_account_get_connection(account);
state = cmd->params[1];
passport = cmd->params[2];
@@ -1018,7 +1047,11 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
/* Yahoo! Buddy, looks like */
networkid = atoi(cmd->params[3]);
friendly = g_strdup(purple_url_decode(cmd->params[4]));
- clientid = strtoul(cmd->params[5], NULL, 10);
+ clientid = strtoul(cmd->params[5], &extcap_str, 10);
+ if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+ extcaps = strtoul(extcap_str+1, NULL, 10);
+ else
+ extcaps = 0;
/* cmd->params[7] seems to be a URL to a Yahoo! icon:
https://sec.yimg.com/i/us/nt/b/purpley.1.0.png
@@ -1028,7 +1061,11 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
/* MSNP14+ with Display Picture object */
networkid = atoi(cmd->params[3]);
friendly = g_strdup(purple_url_decode(cmd->params[4]));
- clientid = strtoul(cmd->params[5], NULL, 10);
+ clientid = strtoul(cmd->params[5], &extcap_str, 10);
+ if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+ extcaps = strtoul(extcap_str+1, NULL, 10);
+ else
+ extcaps = 0;
msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6]));
} else if (cmd->param_count == 6) {
/* Yes, this is 5. The friendly name could start with a number,
@@ -1037,17 +1074,29 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
/* MSNP14 without Display Picture object */
networkid = atoi(cmd->params[3]);
friendly = g_strdup(purple_url_decode(cmd->params[4]));
- clientid = strtoul(cmd->params[5], NULL, 10);
+ clientid = strtoul(cmd->params[5], &extcap_str, 10);
+ if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+ extcaps = strtoul(extcap_str+1, NULL, 10);
+ else
+ extcaps = 0;
} else {
/* MSNP8+ with Display Picture object */
friendly = g_strdup(purple_url_decode(cmd->params[3]));
- clientid = strtoul(cmd->params[4], NULL, 10);
+ clientid = strtoul(cmd->params[4], &extcap_str, 10);
+ if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+ extcaps = strtoul(extcap_str+1, NULL, 10);
+ else
+ extcaps = 0;
msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5]));
}
} else if (cmd->param_count == 5) {
/* MSNP8+ without Display Picture object */
friendly = g_strdup(purple_url_decode(cmd->params[3]));
- clientid = strtoul(cmd->params[4], NULL, 10);
+ clientid = strtoul(cmd->params[4], &extcap_str, 10);
+ if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+ extcaps = strtoul(extcap_str+1, NULL, 10);
+ else
+ extcaps = 0;
} else {
purple_debug_warning("msn", "Received ILN with unknown number of parameters.\n");
return;
@@ -1062,6 +1111,7 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->extinfo && user->extinfo->phone_mobile && user->extinfo->phone_mobile[0] == '+');
msn_user_set_clientid(user, clientid);
+ msn_user_set_extcaps(user, extcaps);
msn_user_set_network(user, networkid);
msn_user_set_state(user, state);
@@ -1197,21 +1247,27 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
PurpleAccount *account;
+ PurpleConnection *gc;
MsnUser *user;
MsnObject *msnobj;
- unsigned long clientid;
+ unsigned long clientid, extcaps;
+ char *extcap_str;
int networkid;
const char *state, *passport, *friendly;
session = cmdproc->session;
account = session->account;
+ gc = purple_account_get_connection(account);
state = cmd->params[0];
passport = cmd->params[1];
networkid = atoi(cmd->params[2]);
friendly = purple_url_decode(cmd->params[3]);
- user = msn_userlist_find_user(session->userlist, passport);
+ if (g_str_equal(passport, session->user->passport))
+ user = session->user;
+ else
+ user = msn_userlist_find_user(session->userlist, passport);
if (user == NULL) return;
if (msn_user_set_friendly_name(user, friendly))
@@ -1229,10 +1285,16 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
msn_user_set_object(user, NULL);
}
- clientid = strtoul(cmd->params[4], NULL, 10);
+ clientid = strtoul(cmd->params[4], &extcap_str, 10);
+ if (session->protocol_ver >= 16 && extcap_str && *extcap_str)
+ extcaps = strtoul(extcap_str+1, NULL, 10);
+ else
+ extcaps = 0;
+
user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->extinfo && user->extinfo->phone_mobile && user->extinfo->phone_mobile[0] == '+');
msn_user_set_clientid(user, clientid);
+ msn_user_set_extcaps(user, extcaps);
msn_user_set_network(user, networkid);
msn_user_set_state(user, state);
@@ -1379,13 +1441,11 @@ rng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
MsnSession *session;
MsnSwitchBoard *swboard;
const char *session_id;
- const char *auth_key;
char *host;
int port;
session = cmdproc->session;
session_id = cmd->params[0];
- auth_key = cmd->params[3];
msn_parse_socket(cmd->params[1], &host, &port);
@@ -1395,8 +1455,8 @@ rng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
swboard = msn_switchboard_new(session);
msn_switchboard_set_invited(swboard, TRUE);
- msn_switchboard_set_session_id(swboard, session_id);
- msn_switchboard_set_auth_key(swboard, auth_key);
+ msn_switchboard_set_session_id(swboard, cmd->params[0]);
+ msn_switchboard_set_auth_key(swboard, cmd->params[3]);
swboard->im_user = g_strdup(cmd->params[4]);
/* msn_switchboard_add_user(swboard, cmd->params[4]); */
@@ -1502,6 +1562,77 @@ sbs_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
/*get the payload content*/
}
+static void
+parse_user_endpoints(MsnUser *user, xmlnode *payloadNode)
+{
+ xmlnode *epNode, *capsNode;
+ MsnUserEndpoint data;
+ const char *id;
+ char *caps, *tmp;
+
+ purple_debug_info("msn", "Get EndpointData\n");
+
+ for (epNode = xmlnode_get_child(payloadNode, "EndpointData");
+ epNode;
+ epNode = xmlnode_get_next_twin(epNode)) {
+ id = xmlnode_get_attrib(epNode, "id");
+ capsNode = xmlnode_get_child(epNode, "Capabilities");
+
+ if (capsNode != NULL) {
+ caps = xmlnode_get_data(capsNode);
+
+ data.clientid = strtoul(caps, &tmp, 10);
+ if (tmp && *tmp)
+ data.extcaps = strtoul(tmp + 1, NULL, 10);
+ else
+ data.extcaps = 0;
+
+ g_free(caps);
+
+ } else {
+ data.clientid = 0;
+ data.extcaps = 0;
+ }
+
+ msn_user_set_endpoint_data(user, id, &data);
+ }
+
+ /* Need to shortcut this check, probably... */
+ if (user == user->userlist->session->user) {
+ for (epNode = xmlnode_get_child(payloadNode, "PrivateEndpointData");
+ epNode;
+ epNode = xmlnode_get_next_twin(epNode)) {
+ MsnUserEndpoint *ep;
+ xmlnode *nameNode, *clientNode;
+
+ /* <PrivateEndpointData id='{GUID}'>
+ <EpName>Endpoint Name</EpName>
+ <Idle>true/false</Idle>
+ <ClientType>1</ClientType>
+ <State>NLN</State>
+ </PrivateEndpointData>
+ */
+ id = xmlnode_get_attrib(epNode, "id");
+ ep = msn_user_get_endpoint_data(user, id);
+
+ if (ep != NULL) {
+ nameNode = xmlnode_get_child(epNode, "EpName");
+ if (nameNode != NULL) {
+ g_free(ep->name);
+ ep->name = xmlnode_get_data(nameNode);
+ }
+
+ clientNode = xmlnode_get_child(epNode, "ClientType");
+ if (clientNode != NULL) {
+ tmp = xmlnode_get_data(clientNode);
+ ep->type = strtoul(tmp, NULL, 10);
+ g_free(tmp);
+ }
+ }
+ }
+ }
+}
+
static void parse_currentmedia(MsnUser *user, const char *cmedia)
{
char **cmedia_array;
@@ -1565,16 +1696,22 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
size_t len)
{
MsnSession *session;
+ PurpleAccount *account;
MsnUser *user;
const char *passport;
- char *str;
+ xmlnode *payloadNode;
+ char *psm_str, *str;
session = cmdproc->session;
+ account = session->account;
passport = cmd->params[0];
- user = msn_userlist_find_user(session->userlist, passport);
+ if (g_str_equal(passport, session->user->passport))
+ user = session->user;
+ else
+ user = msn_userlist_find_user(session->userlist, passport);
if (user == NULL) {
- str = g_strndup(payload, len);
+ char *str = g_strndup(payload, len);
purple_debug_info("msn", "unknown user %s, payload is %s\n",
passport, str);
g_free(str);
@@ -1593,13 +1730,28 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
}
if (len != 0) {
- str = msn_get_psm(cmd->payload,len);
- msn_user_set_statusline(user, str);
- g_free(str);
+ payloadNode = xmlnode_from_str(payload, len);
+ if (!payloadNode) {
+ purple_debug_error("msn", "UBX XML parse Error!\n");
- str = msn_get_currentmedia(cmd->payload, len);
+ msn_user_set_statusline(user, NULL);
+
+ msn_user_update(user);
+ return;
+ }
+
+ psm_str = msn_get_psm(payloadNode);
+ msn_user_set_statusline(user, psm_str);
+ g_free(psm_str);
+
+ str = msn_get_currentmedia(payloadNode);
parse_currentmedia(user, str);
g_free(str);
+
+ parse_user_endpoints(user, payloadNode);
+
+ xmlnode_free(payloadNode);
+
} else {
msn_user_set_statusline(user, NULL);
}
@@ -1632,6 +1784,161 @@ uux_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
cmd->payload_len = atoi(cmd->params[1]);
}
+void
+msn_notification_send_uux(MsnSession *session, const char *payload)
+{
+ MsnTransaction *trans;
+ MsnCmdProc *cmdproc;
+ size_t len = strlen(payload);
+
+ cmdproc = session->notification->cmdproc;
+ purple_debug_misc("msn", "Sending UUX command with payload: %s\n", payload);
+ trans = msn_transaction_new(cmdproc, "UUX", "%" G_GSIZE_FORMAT, len);
+ msn_transaction_set_payload(trans, payload, len);
+ msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+void msn_notification_send_uux_endpointdata(MsnSession *session)
+{
+ xmlnode *epDataNode;
+ xmlnode *capNode;
+ char *caps;
+ char *payload;
+ int length;
+
+ epDataNode = xmlnode_new("EndpointData");
+
+ capNode = xmlnode_new_child(epDataNode, "Capabilities");
+ if (session->protocol_ver >= 16)
+ caps = g_strdup_printf("%d:%02d", MSN_CLIENT_ID_CAPABILITIES, MSN_CLIENT_ID_EXT_CAPS);
+ else
+ caps = g_strdup_printf("%d", MSN_CLIENT_ID_CAPABILITIES);
+ xmlnode_insert_data(capNode, caps, -1);
+ g_free(caps);
+
+ payload = xmlnode_to_str(epDataNode, &length);
+
+ msn_notification_send_uux(session, payload);
+
+ xmlnode_free(epDataNode);
+ g_free(payload);
+}
+
+void msn_notification_send_uux_private_endpointdata(MsnSession *session)
+{
+ xmlnode *private;
+ const char *name;
+ xmlnode *epname;
+ xmlnode *idle;
+ GHashTable *ui_info;
+ const gchar *ui_type;
+ xmlnode *client_type;
+ xmlnode *state;
+ char *payload;
+ int length;
+
+ private = xmlnode_new("PrivateEndpointData");
+
+ name = purple_account_get_string(session->account, "endpoint-name", NULL);
+ epname = xmlnode_new_child(private, "EpName");
+ xmlnode_insert_data(epname, name, -1);
+
+ idle = xmlnode_new_child(private, "Idle");
+ xmlnode_insert_data(idle, "false", -1);
+
+ /* ClientType info (from amsn guys):
+ 0: None
+ 1: Computer
+ 2: Website
+ 3: Mobile / none
+ 4: Xbox / phone /mobile
+ 9: MsnGroup
+ 32: Email member, currently Yahoo!
+ */
+ client_type = xmlnode_new_child(private, "ClientType");
+ ui_info = purple_core_get_ui_info();
+ ui_type = ui_info ? g_hash_table_lookup(ui_info, "client_type") : NULL;
+ if (ui_type) {
+ if (strcmp(ui_type, "pc") == 0)
+ xmlnode_insert_data(client_type, "1", -1);
+ else if (strcmp(ui_type, "web") == 0)
+ xmlnode_insert_data(client_type, "2", -1);
+ else if (strcmp(ui_type, "phone") == 0)
+ xmlnode_insert_data(client_type, "3", -1);
+ else if (strcmp(ui_type, "handheld") == 0)
+ xmlnode_insert_data(client_type, "3", -1);
+ else
+ xmlnode_insert_data(client_type, "1", -1);
+ }
+ else
+ xmlnode_insert_data(client_type, "1", -1);
+
+ state = xmlnode_new_child(private, "State");
+ xmlnode_insert_data(state, msn_state_get_text(msn_state_from_account(session->account)), -1);
+
+ payload = xmlnode_to_str(private, &length);
+
+ msn_notification_send_uux(session, payload);
+
+ xmlnode_free(private);
+ g_free(payload);
+}
+
+static void
+ubn_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+ size_t len)
+{
+ /* Do Nothing, right now. */
+ if (payload != NULL)
+ purple_debug_info("msn", "UBN payload:\n%s\n", payload);
+}
+
+static void
+ubn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ purple_debug_misc("msn", "UBN received from %s.\n", cmd->params[0]);
+ cmdproc->last_cmd->payload_cb = ubn_cmd_post;
+ cmd->payload_len = atoi(cmd->params[2]);
+}
+
+static void
+uun_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+ size_t len)
+{
+ /* Do Nothing, right now. */
+ if (payload != NULL)
+ purple_debug_info("msn", "UUN payload:\n%s\n", payload);
+}
+
+static void
+uun_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ if (strcmp(cmd->params[1], "OK") != 0) {
+ purple_debug_misc("msn", "UUN received.\n");
+ cmdproc->last_cmd->payload_cb = uun_cmd_post;
+ cmd->payload_len = atoi(cmd->params[1]);
+ }
+ else
+ purple_debug_misc("msn", "UUN OK received.\n");
+}
+
+void
+msn_notification_send_uun(MsnSession *session, const char *user,
+ MsnUnifiedNotificationType type, const char *payload)
+{
+ MsnTransaction *trans;
+ MsnCmdProc *cmdproc;
+ size_t len = strlen(payload);
+
+ cmdproc = session->notification->cmdproc;
+ purple_debug_misc("msn", "Sending UUN command %d to %s with payload: %s\n",
+ type, user, payload);
+ trans = msn_transaction_new(cmdproc, "UUN", "%s %d %" G_GSIZE_FORMAT,
+ user, type, len);
+ msn_transaction_set_payload(trans, payload, len);
+ msn_cmdproc_send_trans(cmdproc, trans);
+}
+
/**************************************************************************
* Message Types
**************************************************************************/
@@ -1651,39 +1958,39 @@ profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
/* This isn't an official message. */
return;
- if ((value = msn_message_get_attr(msg, "kv")) != NULL)
+ if ((value = msn_message_get_header_value(msg, "kv")) != NULL)
{
g_free(session->passport_info.kv);
session->passport_info.kv = g_strdup(value);
}
- if ((value = msn_message_get_attr(msg, "sid")) != NULL)
+ if ((value = msn_message_get_header_value(msg, "sid")) != NULL)
{
g_free(session->passport_info.sid);
session->passport_info.sid = g_strdup(value);
}
- if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL)
+ if ((value = msn_message_get_header_value(msg, "MSPAuth")) != NULL)
{
g_free(session->passport_info.mspauth);
session->passport_info.mspauth = g_strdup(value);
}
- if ((value = msn_message_get_attr(msg, "ClientIP")) != NULL)
+ if ((value = msn_message_get_header_value(msg, "ClientIP")) != NULL)
{
g_free(session->passport_info.client_ip);
session->passport_info.client_ip = g_strdup(value);
}
- if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL)
+ if ((value = msn_message_get_header_value(msg, "ClientPort")) != NULL)
{
session->passport_info.client_port = ntohs(atoi(value));
}
- if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL)
+ if ((value = msn_message_get_header_value(msg, "LoginTime")) != NULL)
session->passport_info.sl = atol(value);
- if ((value = msn_message_get_attr(msg, "EmailEnabled")) != NULL)
+ if ((value = msn_message_get_header_value(msg, "EmailEnabled")) != NULL)
session->passport_info.email_enabled = (gboolean)atol(value);
#ifdef MSN_PARTIAL_LISTS
@@ -2087,6 +2394,9 @@ msn_notification_init(void)
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, "UBN", ubn_cmd);
+ msn_table_add_cmd(cbs_table, NULL, "UUN", uun_cmd);
+
msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd);
msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd);
diff --git a/libpurple/protocols/msn/notification.h b/libpurple/protocols/msn/notification.h
index 776f10ad20..b74cf4b8ce 100644
--- a/libpurple/protocols/msn/notification.h
+++ b/libpurple/protocols/msn/notification.h
@@ -49,6 +49,7 @@ typedef struct _MsnNotification MsnNotification;
#include "servconn.h"
#include "state.h"
#include "user.h"
+#include "userlist.h"
struct _MsnNotification
{
@@ -66,6 +67,15 @@ struct _MsnNotification
typedef void (*MsnFqyCb)(MsnSession *session, const char *passport, MsnNetwork network, gpointer data);
+/* Type used for msn_notification_send_uun */
+typedef enum {
+ MSN_UNIFIED_NOTIFICATION_SHARED_FOLDERS = 1,
+ MSN_UNIFIED_NOTIFICATION_UNKNOWN1 = 2,
+ MSN_UNIFIED_NOTIFICATION_P2P = 3,
+ MSN_UNIFIED_NOTIFICATION_MPOP = 4
+
+} MsnUnifiedNotificationType;
+
void uum_send_msg(MsnSession *session, MsnMessage *msg);
void msn_notification_end(void);
@@ -87,6 +97,17 @@ gboolean msn_notification_connect(MsnNotification *notification,
void msn_notification_disconnect(MsnNotification *notification);
void msn_notification_dump_contact(MsnSession *session);
+void msn_notification_send_uux(MsnSession *session, const char *payload);
+
+void msn_notification_send_uux_endpointdata(MsnSession *session);
+
+void msn_notification_send_uux_private_endpointdata(MsnSession *session);
+
+void msn_notification_send_uun(MsnSession *session,
+ const char *user,
+ MsnUnifiedNotificationType type,
+ const char *payload);
+
/**
* Closes a notification.
*
diff --git a/libpurple/protocols/msn/object.c b/libpurple/protocols/msn/object.c
index dddbd271ea..ce0ce0ffab 100644
--- a/libpurple/protocols/msn/object.c
+++ b/libpurple/protocols/msn/object.c
@@ -400,7 +400,7 @@ msn_object_get_url1(const MsnObject *obj)
return obj->url1;
}
-static MsnObject *
+MsnObject *
msn_object_find_local(const char *sha1)
{
GList *l;
diff --git a/libpurple/protocols/msn/object.h b/libpurple/protocols/msn/object.h
index d2745118f6..81572dc799 100644
--- a/libpurple/protocols/msn/object.h
+++ b/libpurple/protocols/msn/object.h
@@ -269,6 +269,8 @@ const char *msn_object_get_url(const MsnObject *obj);
*/
const char *msn_object_get_url1(const MsnObject *obj);
+MsnObject * msn_object_find_local(const char *sha1);
+
void msn_object_set_local(MsnObject *obj);
#endif /* MSN_OBJECT_H */
diff --git a/libpurple/protocols/msn/oim.c b/libpurple/protocols/msn/oim.c
index a4ab5d2ce0..f45e226b1a 100644
--- a/libpurple/protocols/msn/oim.c
+++ b/libpurple/protocols/msn/oim.c
@@ -23,7 +23,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "debug.h"
+
#include "soap.h"
#include "oim.h"
#include "msnutils.h"
@@ -618,7 +621,7 @@ msn_oim_report_to_user(MsnOimRecvData *rdata, const char *msg_str)
MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM);
purple_debug_info("msn", "oim body:{%s}\n", message->body);
- boundary = msn_message_get_attr(message, "boundary");
+ boundary = msn_message_get_header_value(message, "boundary");
if (boundary != NULL) {
char *bounds;
@@ -656,7 +659,7 @@ msn_oim_report_to_user(MsnOimRecvData *rdata, const char *msg_str)
decode_msg = (char *)purple_base64_decode(message->body, &body_len);
}
- from = msn_message_get_attr(message, "X-OIM-originatingSource");
+ from = msn_message_get_header_value(message, "X-OIM-originatingSource");
/* Match number to user's mobile number, FROM is a phone number
if the other side pages you using your phone number */
@@ -671,7 +674,7 @@ msn_oim_report_to_user(MsnOimRecvData *rdata, const char *msg_str)
if (passport == NULL) {
char *start, *end;
- from = msn_message_get_attr(message, "From");
+ from = msn_message_get_header_value(message, "From");
tokens = g_strsplit(from, " ", 2);
if (tokens[1] != NULL)
@@ -690,7 +693,7 @@ msn_oim_report_to_user(MsnOimRecvData *rdata, const char *msg_str)
g_strfreev(tokens);
}
- date = msn_message_get_attr(message, "Date");
+ date = msn_message_get_header_value(message, "Date");
stamp = msn_oim_parse_timestamp(date);
purple_debug_info("msn", "oim Date:{%s},passport{%s}\n",
date, passport);
diff --git a/libpurple/protocols/msn/p2p.c b/libpurple/protocols/msn/p2p.c
new file mode 100644
index 0000000000..20caeb5469
--- /dev/null
+++ b/libpurple/protocols/msn/p2p.c
@@ -0,0 +1,76 @@
+#include "internal.h"
+
+#include "p2p.h"
+
+MsnP2PHeader *
+msn_p2p_header_from_wire(MsnP2PHeader *wire)
+{
+ MsnP2PHeader *header;
+
+ header = g_new(MsnP2PHeader, 1);
+
+ header->session_id = GUINT32_FROM_LE(wire->session_id);
+ header->id = GUINT32_FROM_LE(wire->id);
+ header->offset = GUINT64_FROM_LE(wire->offset);
+ header->total_size = GUINT64_FROM_LE(wire->total_size);
+ header->length = GUINT32_FROM_LE(wire->length);
+ header->flags = GUINT32_FROM_LE(wire->flags);
+ header->ack_id = GUINT32_FROM_LE(wire->ack_id);
+ header->ack_sub_id = GUINT32_FROM_LE(wire->ack_sub_id);
+ header->ack_size = GUINT64_FROM_LE(wire->ack_size);
+
+ return header;
+}
+
+MsnP2PHeader *
+msn_p2p_header_to_wire(MsnP2PHeader *header)
+{
+ MsnP2PHeader *wire;
+
+ wire = g_new(MsnP2PHeader, 1);
+
+ wire->session_id = GUINT32_TO_LE(header->session_id);
+ wire->id = GUINT32_TO_LE(header->id);
+ wire->offset = GUINT64_TO_LE(header->offset);
+ wire->total_size = GUINT64_TO_LE(header->total_size);
+ wire->length = GUINT32_TO_LE(header->length);
+ wire->flags = GUINT32_TO_LE(header->flags);
+ wire->ack_id = GUINT32_TO_LE(header->ack_id);
+ wire->ack_sub_id = GUINT32_TO_LE(header->ack_sub_id);
+ wire->ack_size = GUINT64_TO_LE(header->ack_size);
+
+ return wire;
+
+}
+
+MsnP2PFooter *
+msn_p2p_footer_from_wire(MsnP2PFooter *wire)
+{
+ MsnP2PFooter *footer;
+
+ footer = g_new(MsnP2PFooter, 1);
+
+ footer->value = GUINT32_FROM_BE(wire->value);
+
+ return footer;
+}
+
+MsnP2PFooter *
+msn_p2p_footer_to_wire(MsnP2PFooter *footer)
+{
+ MsnP2PFooter *wire;
+
+ wire = g_new(MsnP2PFooter, 1);
+
+ wire->value = GUINT32_TO_BE(footer->value);
+
+ return wire;
+}
+
+gboolean
+msn_p2p_msg_is_data(const MsnP2PHeaderFlag flags)
+{
+ return (flags == P2P_MSN_OBJ_DATA ||
+ flags == (P2P_WML2009_COMP | P2P_MSN_OBJ_DATA) ||
+ flags == P2P_FILE_DATA);
+}
diff --git a/libpurple/protocols/msn/p2p.h b/libpurple/protocols/msn/p2p.h
new file mode 100644
index 0000000000..f7ee313e38
--- /dev/null
+++ b/libpurple/protocols/msn/p2p.h
@@ -0,0 +1,88 @@
+#ifndef MSN_P2P_H
+#define MSN_P2P_H
+
+
+#pragma pack(push,1)
+typedef struct {
+ guint32 session_id;
+ guint32 id;
+ /**
+ * In a MsnSlpMessage:
+ * For outgoing messages this is the number of bytes from buffer that
+ * have already been sent out. For incoming messages this is the
+ * number of bytes that have been written to buffer.
+ */
+ guint64 offset;
+ guint64 total_size;
+ guint32 length;
+ guint32 flags;
+ guint32 ack_id;
+ guint32 ack_sub_id;
+ guint64 ack_size;
+/* guint8 body[1]; */
+} MsnP2PHeader;
+#pragma pack(pop)
+
+#pragma pack(push,1)
+typedef struct {
+ guint8 header_len;
+ guint8 opcode;
+ guint16 message_len;
+ guint32 base_id;
+} MsnP2Pv2Header;
+#pragma pack(pop)
+
+typedef struct
+{
+ guint32 value;
+} MsnP2PFooter;
+
+typedef enum
+{
+ P2P_NO_FLAG = 0x0, /**< No flags specified */
+ P2P_OUT_OF_ORDER = 0x1, /**< Chunk out-of-order */
+ P2P_ACK = 0x2, /**< Acknowledgement */
+ P2P_PENDING_INVITE = 0x4, /**< There is a pending invite */
+ P2P_BINARY_ERROR = 0x8, /**< Error on the binary level */
+ P2P_FILE = 0x10, /**< File */
+ P2P_MSN_OBJ_DATA = 0x20, /**< MsnObject data */
+ P2P_CLOSE = 0x40, /**< Close session */
+ P2P_TLP_ERROR = 0x80, /**< Error at transport layer protocol */
+ P2P_DC_HANDSHAKE = 0x100, /**< Direct Handshake */
+ P2P_WML2009_COMP = 0x1000000, /**< Compatibility with WLM 2009 */
+ P2P_FILE_DATA = 0x1000030 /**< File transfer data */
+} MsnP2PHeaderFlag;
+/* Info From:
+ * http://msnpiki.msnfanatic.com/index.php/MSNC:P2Pv1_Headers#Flags
+ * http://trac.kmess.org/changeset/ba04d0c825769d23370511031c47f6be75fe9b86
+ * #7180
+ */
+
+typedef enum
+{
+ P2P_APPID_SESION = 0x0, /**< Negotiating session */
+ P2P_APPID_OBJ = 0x1, /**< MsnObject (Display or Emoticon) */
+ P2P_APPID_FILE = 0x2, /**< File transfer */
+ P2P_APPID_EMOTE = 0xB, /**< CustomEmoticon */
+ P2P_APPID_DISPLAY = 0xC /**< Display Image */
+} MsnP2PAppId;
+
+#define P2P_PACKET_HEADER_SIZE sizeof(MsnP2PHeader)
+#define P2P_PACKET_FOOTER_SIZE sizeof(MsnP2PFooter)
+
+MsnP2PHeader *
+msn_p2p_header_from_wire(MsnP2PHeader *wire);
+
+MsnP2PHeader *
+msn_p2p_header_to_wire(MsnP2PHeader *header);
+
+MsnP2PFooter *
+msn_p2p_footer_from_wire(MsnP2PFooter *wire);
+
+MsnP2PFooter *
+msn_p2p_footer_to_wire(MsnP2PFooter *footer);
+
+gboolean
+msn_p2p_msg_is_data(const MsnP2PHeaderFlag flags);
+
+#endif /* MSN_P2P_H */
diff --git a/libpurple/protocols/msn/sbconn.c b/libpurple/protocols/msn/sbconn.c
new file mode 100644
index 0000000000..c84284f512
--- /dev/null
+++ b/libpurple/protocols/msn/sbconn.c
@@ -0,0 +1,151 @@
+#include "internal.h"
+#include "debug.h"
+
+#include "msg.h"
+#include "sbconn.h"
+
+void msn_sbconn_send_part(MsnSlpLink *slplink, MsnSlpMessagePart *part)
+{
+ MsnMessage *msg;
+ const char *passport;
+ char *data;
+ size_t size;
+
+ msg = msn_message_new_msnslp();
+
+ passport = purple_normalize(slplink->session->account, slplink->remote_user);
+ msn_message_set_header(msg, "P2P-Dest", passport);
+
+ data = msn_slpmsgpart_serialize(part, &size);
+ msg->part = msn_slpmsgpart_ref(part);
+
+ msn_message_set_bin_data(msg, data, size);
+
+ if (slplink->swboard == NULL)
+ {
+ slplink->swboard = msn_session_get_swboard(slplink->session,
+ slplink->remote_user, MSN_SB_FLAG_FT);
+
+ g_return_if_fail(slplink->swboard != NULL);
+
+ /* If swboard is destroyed we will be too */
+ slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
+ }
+
+ msn_switchboard_send_msg(slplink->swboard, msg, TRUE);
+}
+
+/** Called when a message times out. */
+static void
+msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
+{
+ MsnMessage *msg;
+
+ msg = trans->data;
+
+ msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT);
+}
+
+static void
+release_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
+{
+ MsnCmdProc *cmdproc;
+ MsnTransaction *trans;
+ char *payload;
+ gsize payload_len;
+ char flag;
+
+ g_return_if_fail(swboard != NULL);
+ g_return_if_fail(msg != NULL);
+
+ cmdproc = swboard->cmdproc;
+
+ payload = msn_message_gen_payload(msg, &payload_len);
+
+ if (purple_debug_is_verbose()) {
+ purple_debug_info("msn", "SB length:{%" G_GSIZE_FORMAT "}\n", payload_len);
+ msn_message_show_readable(msg, "SB SEND", FALSE);
+ }
+
+ flag = msn_message_get_flag(msg);
+ trans = msn_transaction_new(cmdproc, "MSG", "%c %" G_GSIZE_FORMAT,
+ flag, payload_len);
+
+ /* Data for callbacks */
+ msn_transaction_set_data(trans, msg);
+
+ if (flag != 'U') {
+ if (msg->type == MSN_MSG_TEXT)
+ {
+ msg->ack_ref = TRUE;
+ msn_message_ref(msg);
+ swboard->ack_list = g_list_append(swboard->ack_list, msg);
+ msn_transaction_set_timeout_cb(trans, msg_timeout);
+ }
+ else if (msg->type == MSN_MSG_SLP)
+ {
+ msg->ack_ref = TRUE;
+ msn_message_ref(msg);
+ swboard->ack_list = g_list_append(swboard->ack_list, msg);
+ msn_transaction_set_timeout_cb(trans, msg_timeout);
+#if 0
+ if (msg->ack_cb != NULL)
+ {
+ msn_transaction_add_cb(trans, "ACK", msg_ack);
+ msn_transaction_add_cb(trans, "NAK", msg_nak);
+ }
+#endif
+ }
+ }
+
+ trans->payload = payload;
+ trans->payload_len = payload_len;
+
+ msg->trans = trans;
+
+ msn_cmdproc_send_trans(cmdproc, trans);
+}
+
+static void
+queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
+{
+ g_return_if_fail(swboard != NULL);
+ g_return_if_fail(msg != NULL);
+
+ purple_debug_info("msn", "Appending message to queue.\n");
+
+ g_queue_push_tail(swboard->msg_queue, msg);
+
+ msn_message_ref(msg);
+}
+
+void
+msn_sbconn_process_queue(MsnSwitchBoard *swboard)
+{
+ MsnMessage *msg;
+
+ g_return_if_fail(swboard != NULL);
+
+ purple_debug_info("msn", "Processing queue\n");
+
+ while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
+ {
+ purple_debug_info("msn", "Sending message\n");
+ release_msg(swboard, msg);
+ msn_message_unref(msg);
+ }
+}
+
+void
+msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
+ gboolean queue)
+{
+ g_return_if_fail(swboard != NULL);
+ g_return_if_fail(msg != NULL);
+
+ purple_debug_info("msn", "switchboard send msg..\n");
+ if (msn_switchboard_can_send(swboard))
+ release_msg(swboard, msg);
+ else if (queue)
+ queue_msg(swboard, msg);
+}
diff --git a/libpurple/protocols/msn/sbconn.h b/libpurple/protocols/msn/sbconn.h
new file mode 100644
index 0000000000..6380641e03
--- /dev/null
+++ b/libpurple/protocols/msn/sbconn.h
@@ -0,0 +1,17 @@
+#ifndef MSN_SBCONN_H
+#define MSN_SBCONN_H
+
+#include "msg.h"
+#include "slplink.h"
+
+#define MSN_SBCONN_MAX_SIZE 1202
+
+void msn_sbconn_send_part(MsnSlpLink *slplink, MsnSlpMessagePart *part);
+
+void msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
+ gboolean queue);
+
+void
+msn_sbconn_process_queue(MsnSwitchBoard *swboard);
+
+#endif /* MSN_SBCONN_H */
diff --git a/libpurple/protocols/msn/servconn.c b/libpurple/protocols/msn/servconn.c
index 28a9226ca6..617216cee2 100644
--- a/libpurple/protocols/msn/servconn.c
+++ b/libpurple/protocols/msn/servconn.c
@@ -21,7 +21,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+#include "internal.h"
+#include "debug.h"
+
#include "servconn.h"
#include "error.h"
diff --git a/libpurple/protocols/msn/servconn.h b/libpurple/protocols/msn/servconn.h
index 0c1434077e..b075fd08f2 100644
--- a/libpurple/protocols/msn/servconn.h
+++ b/libpurple/protocols/msn/servconn.h
@@ -46,6 +46,7 @@ typedef enum
MSN_SERVCONN_SB
} MsnServConnType;
+#include "internal.h"
#include "proxy.h"
#include "cmdproc.h"
diff --git a/libpurple/protocols/msn/session.c b/libpurple/protocols/msn/session.c
index 0b763caa1c..25e1043caf 100644
--- a/libpurple/protocols/msn/session.c
+++ b/libpurple/protocols/msn/session.c
@@ -21,13 +21,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "debug.h"
+
+#include "error.h"
+#include "msnutils.h"
#include "session.h"
#include "notification.h"
#include "oim.h"
-#include "dialog.h"
-
MsnSession *
msn_session_new(PurpleAccount *account)
{
@@ -45,7 +48,9 @@ msn_session_new(PurpleAccount *account)
purple_account_get_username(account), NULL);
session->oim = msn_oim_new(session);
- session->protocol_ver = WLM_PROT_VER;
+ session->protocol_ver = 0;
+
+ session->guid = rand_guid();
return session;
}
@@ -72,14 +77,11 @@ msn_session_destroy(MsnSession *session)
g_hash_table_destroy(session->soap_table);
while (session->slplinks != NULL)
- msn_slplink_destroy(session->slplinks->data);
+ msn_slplink_unref(session->slplinks->data);
while (session->switches != NULL)
msn_switchboard_destroy(session->switches->data);
- if (session->sync != NULL)
- msn_sync_destroy(session->sync);
-
if (session->oim != NULL)
msn_oim_destroy(session->oim);
@@ -87,7 +89,7 @@ msn_session_destroy(MsnSession *session)
msn_nexus_destroy(session->nexus);
if (session->user != NULL)
- msn_user_destroy(session->user);
+ msn_user_unref(session->user);
if (session->notification != NULL)
msn_notification_destroy(session->notification);
@@ -95,6 +97,7 @@ msn_session_destroy(MsnSession *session)
msn_userlist_destroy(session->userlist);
g_free(session->psm);
+ g_free(session->guid);
g_free(session->abch_cachekey);
#if 0
g_free(session->blocked_text);
@@ -329,7 +332,7 @@ msn_session_sync_users(MsnSession *session)
if (!found) {
if ((remote_user == NULL) || !(remote_user->list_op & MSN_LIST_FL_OP)) {
/* The user is not on the server list */
- msn_show_sync_issue(session, buddy_name, group_name);
+ msn_error_sync_issue(session, buddy_name, group_name);
} else {
/* The user is not in that group on the server list */
to_remove = g_list_prepend(to_remove, buddy);
@@ -483,6 +486,11 @@ msn_session_finish_login(MsnSession *session)
msn_session_sync_users(session);
}
+ if (session->protocol_ver >= 16) {
+ /* TODO: Send this when updating status instead? */
+ msn_notification_send_uux_endpointdata(session);
+ msn_notification_send_uux_private_endpointdata(session);
+ }
msn_change_status(session);
}
diff --git a/libpurple/protocols/msn/session.h b/libpurple/protocols/msn/session.h
index 27ad890b16..4277112697 100644
--- a/libpurple/protocols/msn/session.h
+++ b/libpurple/protocols/msn/session.h
@@ -61,15 +61,12 @@ typedef enum
#define MSN_LOGIN_FQY_TIMEOUT 30
-#include "group.h"
-#include "httpconn.h"
+#define MSN_LOGIN_FQY_TIMEOUT 30
+
#include "nexus.h"
#include "notification.h"
#include "oim.h"
-#include "slpcall.h"
-#include "sslconn.h"
#include "switchboard.h"
-#include "sync.h"
#include "user.h"
#include "userlist.h"
@@ -92,7 +89,6 @@ struct _MsnSession
MsnNotification *notification;
MsnNexus *nexus;
MsnOim *oim;
- MsnSync *sync;
MsnUserList *userlist;
char *abch_cachekey;
@@ -122,6 +118,7 @@ struct _MsnSession
GHashTable *soap_table;
guint soap_cleanup_handle;
+ char *guid;
GSList *url_datas; /**< PurpleUtilFetchUrlData to be cancelled on exit */
};
diff --git a/libpurple/protocols/msn/slp.c b/libpurple/protocols/msn/slp.c
index d709884833..6bf14e0176 100644
--- a/libpurple/protocols/msn/slp.c
+++ b/libpurple/protocols/msn/slp.c
@@ -21,7 +21,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "debug.h"
+
#include "slp.h"
#include "slpcall.h"
#include "slpmsg.h"
@@ -29,16 +32,14 @@
#include "object.h"
#include "user.h"
-#include "switchboard.h"
+#include "sbconn.h"
#include "directconn.h"
-
-#include "smiley.h"
+#include "p2p.h"
+#include "xfer.h"
/* seconds to delay between sending buddy icon requests to the server. */
#define BUDDY_ICON_DELAY 20
-static void request_user_display(MsnUser *user);
-
typedef struct {
MsnSession *session;
const char *remote_user;
@@ -46,167 +47,6 @@ typedef struct {
} MsnFetchUserDisplayData;
/**************************************************************************
- * Util
- **************************************************************************/
-
-static char *
-get_token(const char *str, const char *start, const char *end)
-{
- const char *c, *c2;
-
- if ((c = strstr(str, start)) == NULL)
- return NULL;
-
- c += strlen(start);
-
- if (end != NULL)
- {
- if ((c2 = strstr(c, end)) == NULL)
- return NULL;
-
- return g_strndup(c, c2 - c);
- }
- else
- {
- /* This has to be changed */
- return g_strdup(c);
- }
-
-}
-
-/**************************************************************************
- * Xfer
- **************************************************************************/
-
-static void
-msn_xfer_init(PurpleXfer *xfer)
-{
- MsnSlpCall *slpcall;
- /* MsnSlpLink *slplink; */
- char *content;
-
- purple_debug_info("msn", "xfer_init\n");
-
- slpcall = xfer->data;
-
- /* Send Ok */
- content = g_strdup_printf("SessionID: %lu\r\n\r\n",
- slpcall->session_id);
-
- msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
- content);
-
- g_free(content);
- msn_slplink_send_queued_slpmsgs(slpcall->slplink);
-}
-
-void
-msn_xfer_cancel(PurpleXfer *xfer)
-{
- MsnSlpCall *slpcall;
- char *content;
-
- g_return_if_fail(xfer != NULL);
- g_return_if_fail(xfer->data != NULL);
-
- slpcall = xfer->data;
-
- if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
- {
- if (slpcall->started)
- {
- msn_slpcall_close(slpcall);
- }
- else
- {
- content = g_strdup_printf("SessionID: %lu\r\n\r\n",
- slpcall->session_id);
-
- msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
- content);
-
- g_free(content);
- msn_slplink_send_queued_slpmsgs(slpcall->slplink);
-
- if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
- slpcall->wasted = TRUE;
- else
- msn_slpcall_destroy(slpcall);
- }
- }
-}
-
-gssize
-msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer)
-{
- MsnSlpCall *slpcall;
-
- g_return_val_if_fail(xfer != NULL, -1);
- g_return_val_if_fail(data != NULL, -1);
- g_return_val_if_fail(len > 0, -1);
-
- g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND, -1);
-
- slpcall = xfer->data;
- /* Not sure I trust it'll be there */
- g_return_val_if_fail(slpcall != NULL, -1);
-
- g_return_val_if_fail(slpcall->xfer_msg != NULL, -1);
-
- slpcall->u.outgoing.len = len;
- slpcall->u.outgoing.data = data;
- msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg);
- msn_message_unref(slpcall->xfer_msg->msg);
- return MIN(1202, len);
-}
-
-gssize
-msn_xfer_read(guchar **data, PurpleXfer *xfer)
-{
- MsnSlpCall *slpcall;
- gsize len;
-
- g_return_val_if_fail(xfer != NULL, -1);
- g_return_val_if_fail(data != NULL, -1);
-
- g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE, -1);
-
- slpcall = xfer->data;
- /* Not sure I trust it'll be there */
- g_return_val_if_fail(slpcall != NULL, -1);
-
- /* Just pass up the whole GByteArray. We'll make another. */
- *data = slpcall->u.incoming_data->data;
- len = slpcall->u.incoming_data->len;
-
- g_byte_array_free(slpcall->u.incoming_data, FALSE);
- slpcall->u.incoming_data = g_byte_array_new();
-
- return len;
-}
-
-void
-msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session)
-{
- if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) &&
- (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) &&
- (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL))
- {
- purple_xfer_cancel_remote(slpcall->xfer);
- }
-}
-
-void
-msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
- gsize size)
-{
- PurpleXfer *xfer = slpcall->xfer;
-
- purple_xfer_set_completed(xfer, TRUE);
- purple_xfer_end(xfer);
-}
-
-/**************************************************************************
* SLP Control
**************************************************************************/
@@ -250,1037 +90,10 @@ msn_slp_send_decline(MsnSlpCall *slpcall, const char *branch,
msn_slplink_queue_slpmsg(slplink, slpmsg);
}
-/* XXX: this could be improved if we tracked custom smileys
- * per-protocol, per-account, per-session or (ideally) per-conversation
- */
-static PurpleStoredImage *
-find_valid_emoticon(PurpleAccount *account, const char *path)
-{
- GList *smileys;
-
- if (!purple_account_get_bool(account, "custom_smileys", TRUE))
- return NULL;
-
- smileys = purple_smileys_get_all();
-
- for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
- PurpleSmiley *smiley;
- PurpleStoredImage *img;
-
- smiley = smileys->data;
- img = purple_smiley_get_stored_image(smiley);
-
- if (purple_strequal(path, purple_imgstore_get_filename(img))) {
- g_list_free(smileys);
- return img;
- }
-
- purple_imgstore_unref(img);
- }
-
- purple_debug_error("msn", "Received illegal request for file %s\n", path);
- return NULL;
-}
-
-static char *
-parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype)
-{
- char *nonce;
-
- *ntype = DC_NONCE_UNKNOWN;
-
- nonce = get_token(content, "Hashed-Nonce: {", "}\r\n");
- if (nonce) {
- *ntype = DC_NONCE_SHA1;
- } else {
- guint32 n1, n6;
- guint16 n2, n3, n4, n5;
- nonce = get_token(content, "Nonce: {", "}\r\n");
- if (nonce
- && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x",
- &n1, &n2, &n3, &n4, &n5, &n6) == 6) {
- *ntype = DC_NONCE_PLAIN;
- g_free(nonce);
- nonce = g_malloc(16);
- *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1);
- *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2);
- *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3);
- *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4);
- *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5);
- *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6);
- } else {
- /* Invalid nonce, so ignore request */
- g_free(nonce);
- nonce = NULL;
- }
- }
-
- return nonce;
-}
-
-static void
-msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content)
-{
- /* A direct connection negotiation response */
- char *bridge;
- char *nonce;
- char *listening;
- MsnDirectConn *dc = slpcall->slplink->dc;
- MsnDirectConnNonceType ntype;
-
- purple_debug_info("msn", "process_transresp\n");
-
- /* Direct connections are disabled. */
- if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE))
- return;
-
- g_return_if_fail(dc != NULL);
- g_return_if_fail(dc->state == DC_STATE_CLOSED);
-
- bridge = get_token(content, "Bridge: ", "\r\n");
- nonce = parse_dc_nonce(content, &ntype);
- listening = get_token(content, "Listening: ", "\r\n");
- if (listening && bridge && !strcmp(bridge, "TCPv1")) {
- /* Ok, the client supports direct TCP connection */
-
- /* We always need this. */
- if (ntype == DC_NONCE_SHA1) {
- strncpy(dc->remote_nonce, nonce, 36);
- dc->remote_nonce[36] = '\0';
- }
-
- if (!strcasecmp(listening, "false")) {
- if (dc->listen_data != NULL) {
- /*
- * We'll listen for incoming connections but
- * the listening socket isn't ready yet so we cannot
- * send the INVITE packet now. Put the slpcall into waiting mode
- * and let the callback send the invite.
- */
- slpcall->wait_for_socket = TRUE;
-
- } else if (dc->listenfd != -1) {
- /* The listening socket is ready. Send the INVITE here. */
- msn_dc_send_invite(dc);
-
- } else {
- /* We weren't able to create a listener either. Use SB. */
- msn_dc_fallback_to_sb(dc);
- }
-
- } else {
- /*
- * We should connect to the client so parse
- * IP/port from response.
- */
- char *ip, *port_str;
- int port = 0;
-
- if (ntype == DC_NONCE_PLAIN) {
- /* Only needed for listening side. */
- memcpy(dc->nonce, nonce, 16);
- }
-
- /* Cancel any listen attempts because we don't need them. */
- if (dc->listenfd_handle != 0) {
- purple_input_remove(dc->listenfd_handle);
- dc->listenfd_handle = 0;
- }
- if (dc->connect_timeout_handle != 0) {
- purple_timeout_remove(dc->connect_timeout_handle);
- dc->connect_timeout_handle = 0;
- }
- if (dc->listenfd != -1) {
- purple_network_remove_port_mapping(dc->listenfd);
- close(dc->listenfd);
- dc->listenfd = -1;
- }
- if (dc->listen_data != NULL) {
- purple_network_listen_cancel(dc->listen_data);
- dc->listen_data = NULL;
- }
-
- /* Save external IP/port for later use. We'll try local connection first. */
- dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n");
- port_str = get_token(content, "IPv4External-Port: ", "\r\n");
- if (port_str) {
- dc->ext_port = atoi(port_str);
- g_free(port_str);
- }
-
- ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
- port_str = get_token(content, "IPv4Internal-Port: ", "\r\n");
- if (port_str) {
- port = atoi(port_str);
- g_free(port_str);
- }
-
- if (ip && port) {
- /* Try internal address first */
- dc->connect_data = purple_proxy_connect(
- NULL,
- slpcall->slplink->session->account,
- ip,
- port,
- msn_dc_connected_to_peer_cb,
- dc
- );
-
- if (dc->connect_data) {
- /* Add connect timeout handle */
- dc->connect_timeout_handle = purple_timeout_add_seconds(
- DC_OUTGOING_TIMEOUT,
- msn_dc_outgoing_connection_timeout_cb,
- dc
- );
- } else {
- /*
- * Connection failed
- * Try external IP/port (if specified)
- */
- msn_dc_outgoing_connection_timeout_cb(dc);
- }
-
- } else {
- /*
- * Omitted or invalid internal IP address / port
- * Try external IP/port (if specified)
- */
- msn_dc_outgoing_connection_timeout_cb(dc);
- }
-
- g_free(ip);
- }
-
- } else {
- /*
- * Invalid direct connect invitation or
- * TCP connection is not supported
- */
- }
-
- g_free(listening);
- g_free(nonce);
- g_free(bridge);
-
- return;
-}
-
-static void
-got_sessionreq(MsnSlpCall *slpcall, const char *branch,
- const char *euf_guid, const char *context)
-{
- gboolean accepted = FALSE;
-
- if (!strcmp(euf_guid, MSN_OBJ_GUID))
- {
- /* Emoticon or UserDisplay */
- char *content;
- gsize len;
- MsnSlpLink *slplink;
- MsnSlpMessage *slpmsg;
- MsnObject *obj;
- char *msnobj_data;
- PurpleStoredImage *img = NULL;
- int type;
-
- /* Send Ok */
- content = g_strdup_printf("SessionID: %lu\r\n\r\n",
- slpcall->session_id);
-
- msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
- content);
-
- g_free(content);
-
- slplink = slpcall->slplink;
-
- msnobj_data = (char *)purple_base64_decode(context, &len);
- obj = msn_object_new_from_string(msnobj_data);
- type = msn_object_get_type(obj);
- g_free(msnobj_data);
- if (type == MSN_OBJECT_EMOTICON) {
- img = find_valid_emoticon(slplink->session->account, obj->location);
- } else if (type == MSN_OBJECT_USERTILE) {
- img = msn_object_get_image(obj);
- if (img)
- purple_imgstore_ref(img);
- }
- msn_object_destroy(obj);
-
- if (img != NULL) {
- /* DATA PREP */
- slpmsg = msn_slpmsg_new(slplink);
- slpmsg->slpcall = slpcall;
- slpmsg->session_id = slpcall->session_id;
- msn_slpmsg_set_body(slpmsg, NULL, 4);
- slpmsg->info = "SLP DATA PREP";
- msn_slplink_queue_slpmsg(slplink, slpmsg);
-
- /* DATA */
- slpmsg = msn_slpmsg_new(slplink);
- slpmsg->slpcall = slpcall;
- slpmsg->flags = 0x20;
- slpmsg->info = "SLP DATA";
- msn_slpmsg_set_image(slpmsg, img);
- msn_slplink_queue_slpmsg(slplink, slpmsg);
- purple_imgstore_unref(img);
-
- accepted = TRUE;
-
- } else {
- purple_debug_error("msn", "Wrong object.\n");
- }
- }
-
- else if (!strcmp(euf_guid, MSN_FT_GUID))
- {
- /* File Transfer */
- PurpleAccount *account;
- PurpleXfer *xfer;
- MsnFileContext *header;
- gsize bin_len;
- guint32 file_size;
- char *file_name;
-
- account = slpcall->slplink->session->account;
-
- slpcall->end_cb = msn_xfer_end_cb;
- slpcall->branch = g_strdup(branch);
-
- slpcall->pending = TRUE;
-
- xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE,
- slpcall->slplink->remote_user);
-
- header = (MsnFileContext *)purple_base64_decode(context, &bin_len);
- if (header != NULL && bin_len >= sizeof(MsnFileContext) - 1 &&
- (header->version == 2 ||
- (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) {
- file_size = GUINT64_FROM_LE(header->file_size);
-
- file_name = g_convert((const gchar *)&header->file_name,
- MAX_FILE_NAME_LEN * 2,
- "UTF-8", "UTF-16LE",
- NULL, NULL, NULL);
-
- purple_xfer_set_filename(xfer, file_name ? file_name : "");
- g_free(file_name);
- purple_xfer_set_size(xfer, file_size);
- purple_xfer_set_init_fnc(xfer, msn_xfer_init);
- purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
- purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
- purple_xfer_set_read_fnc(xfer, msn_xfer_read);
- purple_xfer_set_write_fnc(xfer, msn_xfer_write);
-
- slpcall->u.incoming_data = g_byte_array_new();
-
- slpcall->xfer = xfer;
- purple_xfer_ref(slpcall->xfer);
-
- xfer->data = slpcall;
-
- if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) {
- purple_xfer_set_thumbnail(xfer, &header->preview,
- bin_len - sizeof(MsnFileContext),
- "image/png");
- }
-
- purple_xfer_request(xfer);
- }
- g_free(header);
-
- accepted = TRUE;
-
- } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) {
- purple_debug_info("msn", "Cam request.\n");
- if (slpcall && slpcall->slplink &&
- slpcall->slplink->session) {
- PurpleConversation *conv;
- gchar *from = slpcall->slplink->remote_user;
- conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, from,
- slpcall->slplink->session->account);
- if (conv) {
- char *buf;
- buf = g_strdup_printf(
- _("%s requests to view your "
- "webcam, but this request is "
- "not yet supported."), from);
- purple_conversation_write(conv, NULL, buf,
- PURPLE_MESSAGE_SYSTEM |
- PURPLE_MESSAGE_NOTIFY,
- time(NULL));
- g_free(buf);
- }
- }
-
- } else if (!strcmp(euf_guid, MSN_CAM_GUID)) {
- purple_debug_info("msn", "Cam invite.\n");
- if (slpcall && slpcall->slplink &&
- slpcall->slplink->session) {
- PurpleConversation *conv;
- gchar *from = slpcall->slplink->remote_user;
- conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, from,
- slpcall->slplink->session->account);
- if (conv) {
- char *buf;
- buf = g_strdup_printf(
- _("%s invited you to view his/her webcam, but "
- "this is not yet supported."), from);
- purple_conversation_write(conv, NULL, buf,
- PURPLE_MESSAGE_SYSTEM |
- PURPLE_MESSAGE_NOTIFY,
- time(NULL));
- g_free(buf);
- }
- }
-
- } else
- purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid);
-
- if (!accepted) {
- char *content = g_strdup_printf("SessionID: %lu\r\n\r\n",
- slpcall->session_id);
- msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content);
- g_free(content);
- }
-}
-
-void
-send_bye(MsnSlpCall *slpcall, const char *type)
-{
- MsnSlpLink *slplink;
- PurpleAccount *account;
- MsnSlpMessage *slpmsg;
- char *header;
-
- slplink = slpcall->slplink;
-
- g_return_if_fail(slplink != NULL);
-
- account = slplink->session->account;
-
- header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0",
- purple_account_get_username(account));
-
- slpmsg = msn_slpmsg_sip_new(slpcall, 0, header,
- "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32",
- type,
- "\r\n");
- g_free(header);
-
- slpmsg->info = "SLP BYE";
- slpmsg->text_body = TRUE;
-
- msn_slplink_queue_slpmsg(slplink, slpmsg);
-}
-
-static void
-got_invite(MsnSlpCall *slpcall,
- const char *branch, const char *type, const char *content)
-{
- MsnSlpLink *slplink;
-
- slplink = slpcall->slplink;
-
- if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
- {
- char *euf_guid, *context;
- char *temp;
-
- euf_guid = get_token(content, "EUF-GUID: {", "}\r\n");
-
- temp = get_token(content, "SessionID: ", "\r\n");
- if (temp != NULL)
- slpcall->session_id = atoi(temp);
- g_free(temp);
-
- temp = get_token(content, "AppID: ", "\r\n");
- if (temp != NULL)
- slpcall->app_id = atoi(temp);
- g_free(temp);
-
- context = get_token(content, "Context: ", "\r\n");
-
- if (context != NULL)
- got_sessionreq(slpcall, branch, euf_guid, context);
-
- g_free(context);
- g_free(euf_guid);
- }
- else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
- {
- /* A direct connection negotiation request */
- char *bridges;
- char *nonce;
- MsnDirectConnNonceType ntype;
-
- purple_debug_info("msn", "got_invite: transreqbody received\n");
-
- /* Direct connections may be disabled. */
- if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) {
- msn_slp_send_ok(slpcall, branch,
- "application/x-msnmsgr-transrespbody",
- "Bridge: TCPv1\r\n"
- "Listening: false\r\n"
- "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
- "\r\n");
- msn_slpcall_session_init(slpcall);
-
- return;
- }
-
- /* Don't do anything if we already have a direct connection */
- if (slplink->dc != NULL)
- return;
-
- bridges = get_token(content, "Bridges: ", "\r\n");
- nonce = parse_dc_nonce(content, &ntype);
- if (bridges && strstr(bridges, "TCPv1") != NULL) {
- /*
- * Ok, the client supports direct TCP connection
- * Try to create a listening port
- */
- MsnDirectConn *dc;
-
- dc = msn_dc_new(slpcall);
- if (ntype == DC_NONCE_PLAIN) {
- /* There is only one nonce for plain auth. */
- dc->nonce_type = ntype;
- memcpy(dc->nonce, nonce, 16);
- } else if (ntype == DC_NONCE_SHA1) {
- /* Each side has a nonce in SHA1 auth. */
- dc->nonce_type = ntype;
- strncpy(dc->remote_nonce, nonce, 36);
- dc->remote_nonce[36] = '\0';
- }
-
- dc->listen_data = purple_network_listen_range(
- 0, 0,
- SOCK_STREAM,
- msn_dc_listen_socket_created_cb,
- dc
- );
-
- if (dc->listen_data == NULL) {
- /* Listen socket creation failed */
-
- purple_debug_info("msn", "got_invite: listening failed\n");
-
- if (dc->nonce_type != DC_NONCE_PLAIN)
- msn_slp_send_ok(slpcall, branch,
- "application/x-msnmsgr-transrespbody",
- "Bridge: TCPv1\r\n"
- "Listening: false\r\n"
- "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
- "\r\n");
- else
- msn_slp_send_ok(slpcall, branch,
- "application/x-msnmsgr-transrespbody",
- "Bridge: TCPv1\r\n"
- "Listening: false\r\n"
- "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
- "\r\n");
-
- } else {
- /*
- * Listen socket created successfully.
- * Don't send anything here because we don't know the parameters
- * of the created socket yet. msn_dc_send_ok will be called from
- * the callback function: dc_listen_socket_created_cb
- */
- purple_debug_info("msn", "got_invite: listening socket created\n");
-
- dc->send_connection_info_msg_cb = msn_dc_send_ok;
- slpcall->wait_for_socket = TRUE;
- }
-
- } else {
- /*
- * Invalid direct connect invitation or
- * TCP connection is not supported.
- */
- }
-
- g_free(nonce);
- g_free(bridges);
- }
- else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
- {
- /* A direct connection negotiation response */
- msn_slp_process_transresp(slpcall, content);
- }
-}
-
-static void
-got_ok(MsnSlpCall *slpcall,
- const char *type, const char *content)
-{
- g_return_if_fail(slpcall != NULL);
- g_return_if_fail(type != NULL);
-
- if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
- {
- char *content;
- char *header;
- char *nonce = NULL;
- MsnSession *session = slpcall->slplink->session;
- MsnSlpMessage *msg;
- MsnDirectConn *dc;
- MsnUser *user;
-
- if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) {
- /* Don't attempt a direct connection if disabled. */
- msn_slpcall_session_init(slpcall);
- return;
- }
-
- if (slpcall->slplink->dc != NULL) {
- /* If we already have an established direct connection
- * then just start the transfer.
- */
- msn_slpcall_session_init(slpcall);
- return;
- }
-
- user = msn_userlist_find_user(session->userlist,
- slpcall->slplink->remote_user);
- if (!user || !(user->clientid & 0xF0000000)) {
- /* Just start a normal SB transfer. */
- msn_slpcall_session_init(slpcall);
- return;
- }
-
- /* Try direct file transfer by sending a second INVITE */
- dc = msn_dc_new(slpcall);
- slpcall->branch = rand_guid();
-
- dc->listen_data = purple_network_listen_range(
- 0, 0,
- SOCK_STREAM,
- msn_dc_listen_socket_created_cb,
- dc
- );
-
- header = g_strdup_printf(
- "INVITE MSNMSGR:%s MSNSLP/1.0",
- slpcall->slplink->remote_user
- );
-
- if (dc->nonce_type == DC_NONCE_SHA1)
- nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash);
-
- if (dc->listen_data == NULL) {
- /* Listen socket creation failed */
- purple_debug_info("msn", "got_ok: listening failed\n");
-
- content = g_strdup_printf(
- "Bridges: TCPv1\r\n"
- "NetID: %u\r\n"
- "Conn-Type: IP-Restrict-NAT\r\n"
- "UPnPNat: false\r\n"
- "ICF: false\r\n"
- "%s"
- "\r\n",
-
- rand() % G_MAXUINT32,
- nonce ? nonce : ""
- );
-
- } else {
- /* Listen socket created successfully. */
- purple_debug_info("msn", "got_ok: listening socket created\n");
-
- content = g_strdup_printf(
- "Bridges: TCPv1\r\n"
- "NetID: 0\r\n"
- "Conn-Type: Direct-Connect\r\n"
- "UPnPNat: false\r\n"
- "ICF: false\r\n"
- "%s"
- "\r\n",
-
- nonce ? nonce : ""
- );
- }
-
- msg = msn_slpmsg_sip_new(
- slpcall,
- 0,
- header,
- slpcall->branch,
- "application/x-msnmsgr-transreqbody",
- content
- );
- msg->info = "DC INVITE";
- msg->text_body = TRUE;
- g_free(nonce);
- g_free(header);
- g_free(content);
-
- msn_slplink_queue_slpmsg(slpcall->slplink, msg);
- }
- else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
- {
- /* Do we get this? */
- purple_debug_info("msn", "OK with transreqbody\n");
- }
- else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
- {
- msn_slp_process_transresp(slpcall, content);
- }
-}
-
-static void
-got_error(MsnSlpCall *slpcall,
- const char *error, const char *type, const char *content)
-{
- /* It's not valid. Kill this off. */
- purple_debug_error("msn", "Received non-OK result: %s\n",
- error ? error : "Unknown");
-
- if (type && (!strcmp(type, "application/x-msnmsgr-transreqbody")
- || !strcmp(type, "application/x-msnmsgr-transrespbody"))) {
- MsnDirectConn *dc = slpcall->slplink->dc;
- if (dc) {
- msn_dc_fallback_to_sb(dc);
- return;
- }
- }
-
- slpcall->wasted = TRUE;
-}
-
-MsnSlpCall *
-msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
-{
- MsnSlpCall *slpcall;
-
- if (body == NULL)
- {
- purple_debug_warning("msn", "received bogus message\n");
- return NULL;
- }
-
- if (!strncmp(body, "INVITE", strlen("INVITE")))
- {
- char *branch;
- char *call_id;
- char *content;
- char *content_type;
-
- /* From: <msnmsgr:buddy@hotmail.com> */
-#if 0
- slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n");
-#endif
-
- branch = get_token(body, ";branch={", "}");
-
- call_id = get_token(body, "Call-ID: {", "}");
-
-#if 0
- long content_len = -1;
-
- temp = get_token(body, "Content-Length: ", "\r\n");
- if (temp != NULL)
- content_len = atoi(temp);
- g_free(temp);
-#endif
- content_type = get_token(body, "Content-Type: ", "\r\n");
-
- content = get_token(body, "\r\n\r\n", NULL);
-
- slpcall = NULL;
- if (branch && call_id)
- {
- slpcall = msn_slplink_find_slp_call(slplink, call_id);
- if (slpcall)
- {
- g_free(slpcall->branch);
- slpcall->branch = g_strdup(branch);
- got_invite(slpcall, branch, content_type, content);
- }
- else if (content_type && content)
- {
- slpcall = msn_slpcall_new(slplink);
- slpcall->id = g_strdup(call_id);
- got_invite(slpcall, branch, content_type, content);
- }
- }
-
- g_free(call_id);
- g_free(branch);
- g_free(content_type);
- g_free(content);
- }
- else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 ")))
- {
- char *content;
- char *content_type;
- /* Make sure this is "OK" */
- const char *status = body + strlen("MSNSLP/1.0 ");
- char *call_id;
-
- call_id = get_token(body, "Call-ID: {", "}");
- slpcall = msn_slplink_find_slp_call(slplink, call_id);
- g_free(call_id);
-
- g_return_val_if_fail(slpcall != NULL, NULL);
-
- content_type = get_token(body, "Content-Type: ", "\r\n");
-
- content = get_token(body, "\r\n\r\n", NULL);
-
- if (strncmp(status, "200 OK", 6))
- {
- char *error = NULL;
- const char *c;
-
- /* Eww */
- if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) ||
- (c = strchr(status, '\0')))
- {
- size_t len = c - status;
- error = g_strndup(status, len);
- }
-
- got_error(slpcall, error, content_type, content);
- g_free(error);
-
- } else {
- /* Everything's just dandy */
- got_ok(slpcall, content_type, content);
- }
-
- g_free(content_type);
- g_free(content);
- }
- else if (!strncmp(body, "BYE", strlen("BYE")))
- {
- char *call_id;
-
- call_id = get_token(body, "Call-ID: {", "}");
- slpcall = msn_slplink_find_slp_call(slplink, call_id);
- g_free(call_id);
-
- if (slpcall != NULL)
- slpcall->wasted = TRUE;
-
- /* msn_slpcall_destroy(slpcall); */
- }
- else
- slpcall = NULL;
-
- return slpcall;
-}
-
/**************************************************************************
* Msg Callbacks
**************************************************************************/
-void
-msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
- MsnSession *session;
- MsnSlpLink *slplink;
- const char *data;
- gsize len;
-
- session = cmdproc->servconn->session;
- slplink = msn_session_get_slplink(session, msg->remote_user);
-
- if (slplink->swboard == NULL)
- {
- /*
- * We will need swboard in order to change its flags. If its
- * NULL, something has probably gone wrong earlier on. I
- * didn't want to do this, but MSN 7 is somehow causing us
- * to crash here, I couldn't reproduce it to debug more,
- * and people are reporting bugs. Hopefully this doesn't
- * cause more crashes. Stu.
- */
- if (cmdproc->data == NULL)
- g_warning("msn_p2p_msg cmdproc->data was NULL\n");
- else {
- slplink->swboard = (MsnSwitchBoard *)cmdproc->data;
- slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
- }
- }
-
- data = msn_message_get_bin_data(msg, &len);
-
- msn_slplink_process_msg(slplink, &msg->msnslp_header, data, len);
-}
-
-static void
-got_emoticon(MsnSlpCall *slpcall,
- const guchar *data, gsize size)
-{
- PurpleConversation *conv;
- MsnSwitchBoard *swboard;
-
- swboard = slpcall->slplink->swboard;
- conv = swboard->conv;
-
- if (conv) {
- /* 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
- */
- purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size);
- purple_conv_custom_smiley_close(conv, slpcall->data_info );
- }
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info);
-}
-
-void
-msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
-{
- MsnSession *session;
- MsnSlpLink *slplink;
- MsnSwitchBoard *swboard;
- MsnObject *obj;
- char **tokens;
- char *smile, *body_str;
- const char *body, *who, *sha1;
- guint tok;
- size_t body_len;
-
- PurpleConversation *conv;
-
- session = cmdproc->servconn->session;
-
- if (!purple_account_get_bool(session->account, "custom_smileys", TRUE))
- return;
-
- swboard = cmdproc->data;
- conv = swboard->conv;
-
- body = msn_message_get_bin_data(msg, &body_len);
- if (!body || !body_len)
- return;
- body_str = g_strndup(body, body_len);
-
- /* MSN Messenger 7 may send more than one MSNObject in a single message...
- * Maybe 10 tokens is a reasonable max value. */
- tokens = g_strsplit(body_str, "\t", 10);
-
- g_free(body_str);
-
- for (tok = 0; tok < 9; tok += 2) {
- if (tokens[tok] == NULL || tokens[tok + 1] == NULL) {
- break;
- }
-
- smile = tokens[tok];
- obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1]));
-
- if (obj == NULL)
- break;
-
- who = msn_object_get_creator(obj);
- sha1 = msn_object_get_sha1(obj);
-
- slplink = msn_session_get_slplink(session, who);
- if (slplink->swboard != swboard) {
- if (slplink->swboard != NULL)
- /*
- * Apparently we're using a different switchboard now or
- * something? I don't know if this is normal, but it
- * definitely happens. So make sure the old switchboard
- * doesn't still have a reference to us.
- */
- slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink);
- slplink->swboard = swboard;
- slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
- }
-
- /* If the conversation doesn't exist then this is a custom smiley
- * used in the first message in a MSN conversation: we need to create
- * the conversation now, otherwise the custom smiley won't be shown.
- * This happens because every GtkIMHtml has its own smiley tree: if
- * the conversation doesn't exist then we cannot associate the new
- * smiley with its GtkIMHtml widget. */
- if (!conv) {
- conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who);
- }
-
- if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) {
- msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj);
- }
-
- msn_object_destroy(obj);
- obj = NULL;
- who = NULL;
- sha1 = NULL;
- }
- g_strfreev(tokens);
-}
-
-static gboolean
-buddy_icon_cached(PurpleConnection *gc, MsnObject *obj)
-{
- PurpleAccount *account;
- PurpleBuddy *buddy;
- const char *old;
- const char *new;
-
- g_return_val_if_fail(obj != NULL, FALSE);
-
- account = purple_connection_get_account(gc);
-
- buddy = purple_find_buddy(account, msn_object_get_creator(obj));
- if (buddy == NULL)
- return FALSE;
-
- old = purple_buddy_icons_get_checksum_for_user(buddy);
- new = msn_object_get_sha1(obj);
-
- if (new == NULL)
- return FALSE;
-
- /* If the old and new checksums are the same, and the file actually exists,
- * then return TRUE */
- if (old != NULL && !strcmp(old, new))
- return TRUE;
-
- return FALSE;
-}
-
-static void
-msn_release_buddy_icon_request(MsnUserList *userlist)
-{
- MsnUser *user;
-
- g_return_if_fail(userlist != NULL);
-
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "Releasing buddy icon request\n");
-
- if (userlist->buddy_icon_window > 0)
- {
- GQueue *queue;
-
- queue = userlist->buddy_icon_requests;
-
- if (g_queue_is_empty(userlist->buddy_icon_requests))
- return;
-
- user = g_queue_pop_head(queue);
-
- userlist->buddy_icon_window--;
- request_user_display(user);
-
- if (purple_debug_is_verbose())
- purple_debug_info("msn",
- "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
- userlist->buddy_icon_window);
- }
-}
-
/*
* Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next
* buddy icon request if there is one.
@@ -1301,61 +114,24 @@ msn_release_buddy_icon_request_timeout(gpointer data)
return FALSE;
}
-void
-msn_queue_buddy_icon_request(MsnUser *user)
-{
- PurpleAccount *account;
- MsnObject *obj;
- GQueue *queue;
-
- g_return_if_fail(user != NULL);
-
- account = user->userlist->session->account;
-
- obj = msn_user_get_object(user);
-
- if (obj == NULL)
- {
- purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL);
- return;
- }
-
- if (!buddy_icon_cached(account->gc, obj))
- {
- MsnUserList *userlist;
-
- userlist = user->userlist;
- queue = userlist->buddy_icon_requests;
-
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
- user->passport, userlist->buddy_icon_window);
-
- g_queue_push_tail(queue, user);
-
- if (userlist->buddy_icon_window > 0)
- msn_release_buddy_icon_request(userlist);
- }
-}
-
static void
got_user_display(MsnSlpCall *slpcall,
const guchar *data, gsize size)
{
- MsnSlpLink *slplink;
+ MsnUserList *userlist;
const char *info;
PurpleAccount *account;
g_return_if_fail(slpcall != NULL);
- slplink = slpcall->slplink;
info = slpcall->data_info;
if (purple_debug_is_verbose())
- purple_debug_info("msn", "Got User Display: %s\n", slplink->remote_user);
+ purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user);
- account = slplink->session->account;
+ userlist = slpcall->slplink->session->userlist;
+ account = slpcall->slplink->session->account;
- purple_buddy_icons_set_for_user(account, slplink->remote_user,
+ purple_buddy_icons_set_for_user(account, slpcall->slplink->remote_user,
g_memdup(data, size), size, info);
}
@@ -1416,7 +192,43 @@ fetched_user_display(PurpleUtilFetchUrlData *url_data, gpointer user_data,
}
static void
-request_user_display(MsnUser *user)
+request_own_user_display(MsnUser *user)
+{
+ PurpleAccount *account;
+ MsnSession *session;
+ MsnObject *my_obj = NULL;
+ gconstpointer data = NULL;
+ const char *info;
+ size_t len = 0;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "Requesting our own user display\n");
+
+ session = user->userlist->session;
+ account = session->account;
+ my_obj = msn_user_get_object(user);
+
+ if (my_obj != NULL) {
+ PurpleStoredImage *img = msn_object_get_image(my_obj);
+ data = purple_imgstore_get_data(img);
+ len = purple_imgstore_get_size(img);
+ info = msn_object_get_sha1(my_obj);
+ }
+
+ purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info);
+
+ /* Free one window slot */
+ session->userlist->buddy_icon_window++;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n",
+ session->userlist->buddy_icon_window);
+
+ msn_release_buddy_icon_request(session->userlist);
+}
+
+void
+msn_request_user_display(MsnUser *user)
{
PurpleAccount *account;
MsnSession *session;
@@ -1452,32 +264,134 @@ request_user_display(MsnUser *user)
}
}
else
- {
- MsnObject *my_obj = NULL;
- gconstpointer data = NULL;
- size_t len = 0;
+ request_own_user_display(user);
+}
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "Requesting our own user display\n");
+static void
+send_file_cb(MsnSlpCall *slpcall)
+{
+ MsnSlpMessage *slpmsg;
+ PurpleXfer *xfer;
- my_obj = msn_user_get_object(session->user);
+ xfer = (PurpleXfer *)slpcall->xfer;
+ if (purple_xfer_get_status(xfer) >= PURPLE_XFER_STATUS_STARTED)
+ return;
- if (my_obj != NULL)
- {
- PurpleStoredImage *img = msn_object_get_image(my_obj);
- data = purple_imgstore_get_data(img);
- len = purple_imgstore_get_size(img);
- }
+ purple_xfer_ref(xfer);
+ purple_xfer_start(xfer, -1, NULL, 0);
+ if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) {
+ purple_xfer_unref(xfer);
+ return;
+ }
+ purple_xfer_unref(xfer);
+
+ slpmsg = msn_slpmsg_file_new(slpcall, purple_xfer_get_size(xfer));
+ msn_slpmsg_set_slplink(slpmsg, slpcall->slplink);
+
+ msn_slplink_send_slpmsg(slpcall->slplink, slpmsg);
+}
+
+static gchar *
+gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
+{
+ gsize size = 0;
+ MsnFileContext *header;
+ gchar *u8 = NULL;
+ gchar *ret;
+ gunichar2 *uni = NULL;
+ glong currentChar = 0;
+ glong len = 0;
+ const char *preview;
+ gsize preview_len;
+
+ size = purple_xfer_get_size(xfer);
+
+ purple_xfer_prepare_thumbnail(xfer, "png");
+
+ if (!file_name) {
+ gchar *basename = g_path_get_basename(file_path);
+ u8 = purple_utf8_try_convert(basename);
+ g_free(basename);
+ file_name = u8;
+ }
+
+ uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL);
- purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info);
+ if (u8) {
+ g_free(u8);
+ file_name = NULL;
+ u8 = NULL;
+ }
+
+ preview = purple_xfer_get_thumbnail(xfer, &preview_len);
+ header = g_malloc(sizeof(MsnFileContext) + preview_len);
- /* Free one window slot */
- session->userlist->buddy_icon_window++;
+ header->length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1);
+ header->version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */
+ header->file_size = GUINT64_TO_LE(size);
+ if (preview)
+ header->type = GUINT32_TO_LE(0);
+ else
+ header->type = GUINT32_TO_LE(1);
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n",
- session->userlist->buddy_icon_window);
+ len = MIN(len, MAX_FILE_NAME_LEN);
+ for (currentChar = 0; currentChar < len; currentChar++) {
+ header->file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]);
+ }
+ memset(&header->file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2);
- msn_release_buddy_icon_request(session->userlist);
+ memset(&header->unknown1, 0, sizeof(header->unknown1));
+ header->unknown2 = GUINT32_TO_LE(0xffffffff);
+ if (preview) {
+ memcpy(&header->preview, preview, preview_len);
}
+ header->preview[preview_len] = '\0';
+
+ g_free(uni);
+ ret = purple_base64_encode((const guchar *)header, sizeof(MsnFileContext) + preview_len);
+ g_free(header);
+ return ret;
}
+
+void
+msn_request_ft(PurpleXfer *xfer)
+{
+ MsnSlpCall *slpcall;
+ MsnSlpLink *slplink;
+ char *context;
+ const char *fn;
+ const char *fp;
+
+ fn = purple_xfer_get_filename(xfer);
+ fp = purple_xfer_get_local_filename(xfer);
+
+ slplink = xfer->data;
+
+ g_return_if_fail(slplink != NULL);
+ g_return_if_fail(fp != NULL);
+
+ slpcall = msn_slpcall_new(slplink);
+ msn_slpcall_init(slpcall, MSN_SLPCALL_DC);
+
+ slpcall->session_init_cb = send_file_cb;
+ slpcall->end_cb = msn_xfer_end_cb;
+ slpcall->cb = msn_xfer_completed_cb;
+ slpcall->xfer = xfer;
+ purple_xfer_ref(slpcall->xfer);
+
+ slpcall->pending = TRUE;
+
+ purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel);
+ purple_xfer_set_read_fnc(xfer, msn_xfer_read);
+ purple_xfer_set_write_fnc(xfer, msn_xfer_write);
+
+ xfer->data = slpcall;
+
+ context = gen_context(xfer, fn, fp);
+
+ msn_slpcall_invite(slpcall, MSN_FT_GUID, P2P_APPID_FILE, context);
+ msn_slplink_unref(slplink);
+
+ g_free(context);
+}
+
diff --git a/libpurple/protocols/msn/slp.h b/libpurple/protocols/msn/slp.h
index 4539adfa7f..69c0b46dc2 100644
--- a/libpurple/protocols/msn/slp.h
+++ b/libpurple/protocols/msn/slp.h
@@ -25,10 +25,12 @@
#define MSN_SLP_H
#include "internal.h"
-
#include "ft.h"
+
#include "session.h"
#include "slpcall.h"
+#include "slplink.h"
+#include "user.h"
#define MAX_FILE_NAME_LEN 260 /* MAX_PATH in Windows */
@@ -49,8 +51,6 @@ typedef struct
} MsnFileContext;
#pragma pack(pop)
-MsnSlpCall * msn_slp_sip_recv(MsnSlpLink *slplink,
- const char *body);
void
msn_slp_send_ok(MsnSlpCall *slpcall, const char *branch,
const char *type, const char *content);
@@ -62,15 +62,9 @@ msn_slp_send_decline(MsnSlpCall *slpcall, const char *branch,
void send_bye(MsnSlpCall *slpcall, const char *type);
-void msn_xfer_completed_cb(MsnSlpCall *slpcall,
- const guchar *body, gsize size);
-
-void msn_xfer_cancel(PurpleXfer *xfer);
-gssize msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer);
-gssize msn_xfer_read(guchar **data, PurpleXfer *xfer);
-void msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session);
+void msn_request_user_display(MsnUser *user);
-void msn_queue_buddy_icon_request(MsnUser *user);
+void msn_request_ft(PurpleXfer *xfer);
#endif /* MSN_SLP_H */
diff --git a/libpurple/protocols/msn/slpcall.c b/libpurple/protocols/msn/slpcall.c
index 569209d286..dbcacc7555 100644
--- a/libpurple/protocols/msn/slpcall.c
+++ b/libpurple/protocols/msn/slpcall.c
@@ -21,11 +21,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "debug.h"
+#include "smiley.h"
+
#include "msnutils.h"
#include "slpcall.h"
#include "slp.h"
+#include "p2p.h"
+#include "xfer.h"
/**************************************************************************
* Main
@@ -140,7 +146,7 @@ msn_slpcall_session_init(MsnSlpCall *slpcall)
void
msn_slpcall_invite(MsnSlpCall *slpcall, const char *euf_guid,
- int app_id, const char *context)
+ MsnP2PAppId app_id, const char *context)
{
MsnSlpLink *slplink;
MsnSlpMessage *slpmsg;
@@ -190,6 +196,861 @@ msn_slpcall_close(MsnSlpCall *slpcall)
msn_slpcall_destroy(slpcall);
}
+/*****************************************************************************
+ * Parse received SLP messages
+ ****************************************************************************/
+
+/**************************************************************************
+ *** Util
+ **************************************************************************/
+
+static char *
+get_token(const char *str, const char *start, const char *end)
+{
+ const char *c, *c2;
+
+ if ((c = strstr(str, start)) == NULL)
+ return NULL;
+
+ c += strlen(start);
+
+ if (end != NULL)
+ {
+ if ((c2 = strstr(c, end)) == NULL)
+ return NULL;
+
+ return g_strndup(c, c2 - c);
+ }
+ else
+ {
+ /* This has to be changed */
+ return g_strdup(c);
+ }
+
+}
+
+/* XXX: this could be improved if we tracked custom smileys
+ * per-protocol, per-account, per-session or (ideally) per-conversation
+ */
+static PurpleStoredImage *
+find_valid_emoticon(PurpleAccount *account, const char *path)
+{
+ GList *smileys;
+
+ if (!purple_account_get_bool(account, "custom_smileys", TRUE))
+ return NULL;
+
+ smileys = purple_smileys_get_all();
+
+ for (; smileys; smileys = g_list_delete_link(smileys, smileys)) {
+ PurpleSmiley *smiley;
+ PurpleStoredImage *img;
+
+ smiley = smileys->data;
+ img = purple_smiley_get_stored_image(smiley);
+
+ if (purple_strequal(path, purple_imgstore_get_filename(img))) {
+ g_list_free(smileys);
+ return img;
+ }
+
+ purple_imgstore_unref(img);
+ }
+
+ purple_debug_error("msn", "Received illegal request for file %s\n", path);
+ return NULL;
+}
+
+static char *
+parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype)
+{
+ char *nonce;
+
+ *ntype = DC_NONCE_UNKNOWN;
+
+ nonce = get_token(content, "Hashed-Nonce: {", "}\r\n");
+ if (nonce) {
+ *ntype = DC_NONCE_SHA1;
+ } else {
+ guint32 n1, n6;
+ guint16 n2, n3, n4, n5;
+ nonce = get_token(content, "Nonce: {", "}\r\n");
+ if (nonce
+ && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x",
+ &n1, &n2, &n3, &n4, &n5, &n6) == 6) {
+ *ntype = DC_NONCE_PLAIN;
+ g_free(nonce);
+ nonce = g_malloc(16);
+ *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1);
+ *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2);
+ *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3);
+ *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4);
+ *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5);
+ *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6);
+ } else {
+ /* Invalid nonce, so ignore request */
+ g_free(nonce);
+ nonce = NULL;
+ }
+ }
+
+ return nonce;
+}
+
+static void
+msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content)
+{
+ /* A direct connection negotiation response */
+ char *bridge;
+ char *nonce;
+ char *listening;
+ MsnDirectConn *dc = slpcall->slplink->dc;
+ MsnDirectConnNonceType ntype;
+
+ purple_debug_info("msn", "process_transresp\n");
+
+ /* Direct connections are disabled. */
+ if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE))
+ return;
+
+ g_return_if_fail(dc != NULL);
+ g_return_if_fail(dc->state == DC_STATE_CLOSED);
+
+ bridge = get_token(content, "Bridge: ", "\r\n");
+ nonce = parse_dc_nonce(content, &ntype);
+ listening = get_token(content, "Listening: ", "\r\n");
+ if (listening && bridge && !strcmp(bridge, "TCPv1")) {
+ /* Ok, the client supports direct TCP connection */
+
+ /* We always need this. */
+ if (ntype == DC_NONCE_SHA1) {
+ strncpy(dc->remote_nonce, nonce, 36);
+ dc->remote_nonce[36] = '\0';
+ }
+
+ if (!strcasecmp(listening, "false")) {
+ if (dc->listen_data != NULL) {
+ /*
+ * We'll listen for incoming connections but
+ * the listening socket isn't ready yet so we cannot
+ * send the INVITE packet now. Put the slpcall into waiting mode
+ * and let the callback send the invite.
+ */
+ slpcall->wait_for_socket = TRUE;
+
+ } else if (dc->listenfd != -1) {
+ /* The listening socket is ready. Send the INVITE here. */
+ msn_dc_send_invite(dc);
+
+ } else {
+ /* We weren't able to create a listener either. Use SB. */
+ msn_dc_fallback_to_sb(dc);
+ }
+
+ } else {
+ /*
+ * We should connect to the client so parse
+ * IP/port from response.
+ */
+ char *ip, *port_str;
+ int port = 0;
+
+ if (ntype == DC_NONCE_PLAIN) {
+ /* Only needed for listening side. */
+ memcpy(dc->nonce, nonce, 16);
+ }
+
+ /* Cancel any listen attempts because we don't need them. */
+ if (dc->listenfd_handle != 0) {
+ purple_input_remove(dc->listenfd_handle);
+ dc->listenfd_handle = 0;
+ }
+ if (dc->connect_timeout_handle != 0) {
+ purple_timeout_remove(dc->connect_timeout_handle);
+ dc->connect_timeout_handle = 0;
+ }
+ if (dc->listenfd != -1) {
+ purple_network_remove_port_mapping(dc->listenfd);
+ close(dc->listenfd);
+ dc->listenfd = -1;
+ }
+ if (dc->listen_data != NULL) {
+ purple_network_listen_cancel(dc->listen_data);
+ dc->listen_data = NULL;
+ }
+
+ /* Save external IP/port for later use. We'll try local connection first. */
+ dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n");
+ port_str = get_token(content, "IPv4External-Port: ", "\r\n");
+ if (port_str) {
+ dc->ext_port = atoi(port_str);
+ g_free(port_str);
+ }
+
+ ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n");
+ port_str = get_token(content, "IPv4Internal-Port: ", "\r\n");
+ if (port_str) {
+ port = atoi(port_str);
+ g_free(port_str);
+ }
+
+ if (ip && port) {
+ /* Try internal address first */
+ dc->connect_data = purple_proxy_connect(
+ NULL,
+ slpcall->slplink->session->account,
+ ip,
+ port,
+ msn_dc_connected_to_peer_cb,
+ dc
+ );
+
+ if (dc->connect_data) {
+ /* Add connect timeout handle */
+ dc->connect_timeout_handle = purple_timeout_add_seconds(
+ DC_OUTGOING_TIMEOUT,
+ msn_dc_outgoing_connection_timeout_cb,
+ dc
+ );
+ } else {
+ /*
+ * Connection failed
+ * Try external IP/port (if specified)
+ */
+ msn_dc_outgoing_connection_timeout_cb(dc);
+ }
+
+ } else {
+ /*
+ * Omitted or invalid internal IP address / port
+ * Try external IP/port (if specified)
+ */
+ msn_dc_outgoing_connection_timeout_cb(dc);
+ }
+
+ g_free(ip);
+ }
+
+ } else {
+ /*
+ * Invalid direct connect invitation or
+ * TCP connection is not supported
+ */
+ }
+
+ g_free(listening);
+ g_free(nonce);
+ g_free(bridge);
+
+ return;
+}
+
+static void
+got_sessionreq(MsnSlpCall *slpcall, const char *branch,
+ const char *euf_guid, const char *context)
+{
+ gboolean accepted = FALSE;
+
+ if (!strcmp(euf_guid, MSN_OBJ_GUID))
+ {
+ /* Emoticon or UserDisplay */
+ char *content;
+ gsize len;
+ MsnSlpLink *slplink;
+ MsnSlpMessage *slpmsg;
+ MsnObject *obj;
+ char *msnobj_data;
+ PurpleStoredImage *img = NULL;
+ int type;
+
+ /* Send Ok */
+ content = g_strdup_printf("SessionID: %lu\r\n\r\n",
+ slpcall->session_id);
+
+ msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
+ content);
+
+ g_free(content);
+
+ slplink = slpcall->slplink;
+
+ msnobj_data = (char *)purple_base64_decode(context, &len);
+ obj = msn_object_new_from_string(msnobj_data);
+ type = msn_object_get_type(obj);
+ g_free(msnobj_data);
+ if (type == MSN_OBJECT_EMOTICON) {
+ img = find_valid_emoticon(slplink->session->account, obj->location);
+ } else if (type == MSN_OBJECT_USERTILE) {
+ img = msn_object_get_image(obj);
+ if (img)
+ purple_imgstore_ref(img);
+ }
+ msn_object_destroy(obj);
+
+ if (img != NULL) {
+ /* DATA PREP */
+ slpmsg = msn_slpmsg_dataprep_new(slpcall);
+ msn_slpmsg_set_slplink(slpmsg, slplink);
+ msn_slplink_queue_slpmsg(slplink, slpmsg);
+
+ /* DATA */
+ slpmsg = msn_slpmsg_obj_new(slpcall, img);
+ msn_slpmsg_set_slplink(slpmsg, slplink);
+ msn_slplink_queue_slpmsg(slplink, slpmsg);
+ purple_imgstore_unref(img);
+
+ accepted = TRUE;
+
+ } else {
+ purple_debug_error("msn", "Wrong object.\n");
+ }
+ }
+
+ else if (!strcmp(euf_guid, MSN_FT_GUID))
+ {
+ /* File Transfer */
+ PurpleAccount *account;
+ PurpleXfer *xfer;
+ MsnFileContext *header;
+ gsize bin_len;
+ guint32 file_size;
+ char *file_name;
+
+ account = slpcall->slplink->session->account;
+
+ slpcall->end_cb = msn_xfer_end_cb;
+ slpcall->branch = g_strdup(branch);
+
+ slpcall->pending = TRUE;
+
+ xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE,
+ slpcall->slplink->remote_user);
+
+ header = (MsnFileContext *)purple_base64_decode(context, &bin_len);
+ if (bin_len >= sizeof(MsnFileContext) - 1 &&
+ (header->version == 2 ||
+ (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) {
+ file_size = GUINT64_FROM_LE(header->file_size);
+
+ file_name = g_convert((const gchar *)&header->file_name,
+ MAX_FILE_NAME_LEN * 2,
+ "UTF-8", "UTF-16LE",
+ NULL, NULL, NULL);
+
+ purple_xfer_set_filename(xfer, file_name ? file_name : "");
+ g_free(file_name);
+ purple_xfer_set_size(xfer, file_size);
+ purple_xfer_set_init_fnc(xfer, msn_xfer_init);
+ purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel);
+ purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel);
+ purple_xfer_set_read_fnc(xfer, msn_xfer_read);
+ purple_xfer_set_write_fnc(xfer, msn_xfer_write);
+
+ slpcall->u.incoming_data = g_byte_array_new();
+
+ slpcall->xfer = xfer;
+ purple_xfer_ref(slpcall->xfer);
+
+ xfer->data = slpcall;
+
+ if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) {
+ purple_xfer_set_thumbnail(xfer, &header->preview,
+ bin_len - sizeof(MsnFileContext),
+ "image/png");
+ }
+
+ purple_xfer_request(xfer);
+ }
+ g_free(header);
+
+ accepted = TRUE;
+
+ } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) {
+ purple_debug_info("msn", "Cam request.\n");
+ if (slpcall && slpcall->slplink &&
+ slpcall->slplink->session) {
+ PurpleConversation *conv;
+ gchar *from = slpcall->slplink->remote_user;
+ conv = purple_find_conversation_with_account(
+ PURPLE_CONV_TYPE_IM, from,
+ slpcall->slplink->session->account);
+ if (conv) {
+ char *buf;
+ buf = g_strdup_printf(
+ _("%s requests to view your "
+ "webcam, but this request is "
+ "not yet supported."), from);
+ purple_conversation_write(conv, NULL, buf,
+ PURPLE_MESSAGE_SYSTEM |
+ PURPLE_MESSAGE_NOTIFY,
+ time(NULL));
+ g_free(buf);
+ }
+ }
+
+ } else if (!strcmp(euf_guid, MSN_CAM_GUID)) {
+ purple_debug_info("msn", "Cam invite.\n");
+ if (slpcall && slpcall->slplink &&
+ slpcall->slplink->session) {
+ PurpleConversation *conv;
+ gchar *from = slpcall->slplink->remote_user;
+ conv = purple_find_conversation_with_account(
+ PURPLE_CONV_TYPE_IM, from,
+ slpcall->slplink->session->account);
+ if (conv) {
+ char *buf;
+ buf = g_strdup_printf(
+ _("%s invited you to view his/her webcam, but "
+ "this is not yet supported."), from);
+ purple_conversation_write(conv, NULL, buf,
+ PURPLE_MESSAGE_SYSTEM |
+ PURPLE_MESSAGE_NOTIFY,
+ time(NULL));
+ g_free(buf);
+ }
+ }
+
+ } else
+ purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid);
+
+ if (!accepted) {
+ char *content = g_strdup_printf("SessionID: %lu\r\n\r\n",
+ slpcall->session_id);
+ msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content);
+ g_free(content);
+ }
+}
+
+void
+send_bye(MsnSlpCall *slpcall, const char *type)
+{
+ MsnSlpLink *slplink;
+ PurpleAccount *account;
+ MsnSlpMessage *slpmsg;
+ char *header;
+
+ slplink = slpcall->slplink;
+
+ g_return_if_fail(slplink != NULL);
+
+ account = slplink->session->account;
+
+ header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0",
+ purple_account_get_username(account));
+
+ slpmsg = msn_slpmsg_sip_new(slpcall, 0, header,
+ "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32",
+ type,
+ "\r\n");
+ g_free(header);
+
+ slpmsg->info = "SLP BYE";
+ slpmsg->text_body = TRUE;
+
+ msn_slplink_queue_slpmsg(slplink, slpmsg);
+}
+
+static void
+got_invite(MsnSlpCall *slpcall,
+ const char *branch, const char *type, const char *content)
+{
+ MsnSlpLink *slplink;
+
+ slplink = slpcall->slplink;
+
+ if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
+ {
+ char *euf_guid, *context;
+ char *temp;
+
+ euf_guid = get_token(content, "EUF-GUID: {", "}\r\n");
+
+ temp = get_token(content, "SessionID: ", "\r\n");
+ if (temp != NULL)
+ slpcall->session_id = atoi(temp);
+ g_free(temp);
+
+ temp = get_token(content, "AppID: ", "\r\n");
+ if (temp != NULL)
+ slpcall->app_id = atoi(temp);
+ g_free(temp);
+
+ context = get_token(content, "Context: ", "\r\n");
+
+ if (context != NULL)
+ got_sessionreq(slpcall, branch, euf_guid, context);
+
+ g_free(context);
+ g_free(euf_guid);
+ }
+ else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
+ {
+ /* A direct connection negotiation request */
+ char *bridges;
+ char *nonce;
+ MsnDirectConnNonceType ntype;
+
+ purple_debug_info("msn", "got_invite: transreqbody received\n");
+
+ /* Direct connections may be disabled. */
+ if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) {
+ msn_slp_send_ok(slpcall, branch,
+ "application/x-msnmsgr-transrespbody",
+ "Bridge: TCPv1\r\n"
+ "Listening: false\r\n"
+ "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
+ "\r\n");
+ msn_slpcall_session_init(slpcall);
+
+ return;
+ }
+
+ /* Don't do anything if we already have a direct connection */
+ if (slplink->dc != NULL)
+ return;
+
+ bridges = get_token(content, "Bridges: ", "\r\n");
+ nonce = parse_dc_nonce(content, &ntype);
+ if (bridges && strstr(bridges, "TCPv1") != NULL) {
+ /*
+ * Ok, the client supports direct TCP connection
+ * Try to create a listening port
+ */
+ MsnDirectConn *dc;
+
+ dc = msn_dc_new(slpcall);
+ if (ntype == DC_NONCE_PLAIN) {
+ /* There is only one nonce for plain auth. */
+ dc->nonce_type = ntype;
+ memcpy(dc->nonce, nonce, 16);
+ } else if (ntype == DC_NONCE_SHA1) {
+ /* Each side has a nonce in SHA1 auth. */
+ dc->nonce_type = ntype;
+ strncpy(dc->remote_nonce, nonce, 36);
+ dc->remote_nonce[36] = '\0';
+ }
+
+ dc->listen_data = purple_network_listen_range(
+ 0, 0,
+ SOCK_STREAM,
+ msn_dc_listen_socket_created_cb,
+ dc
+ );
+
+ if (dc->listen_data == NULL) {
+ /* Listen socket creation failed */
+
+ purple_debug_info("msn", "got_invite: listening failed\n");
+
+ if (dc->nonce_type != DC_NONCE_PLAIN)
+ msn_slp_send_ok(slpcall, branch,
+ "application/x-msnmsgr-transrespbody",
+ "Bridge: TCPv1\r\n"
+ "Listening: false\r\n"
+ "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
+ "\r\n");
+ else
+ msn_slp_send_ok(slpcall, branch,
+ "application/x-msnmsgr-transrespbody",
+ "Bridge: TCPv1\r\n"
+ "Listening: false\r\n"
+ "Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
+ "\r\n");
+
+ } else {
+ /*
+ * Listen socket created successfully.
+ * Don't send anything here because we don't know the parameters
+ * of the created socket yet. msn_dc_send_ok will be called from
+ * the callback function: dc_listen_socket_created_cb
+ */
+ purple_debug_info("msn", "got_invite: listening socket created\n");
+
+ dc->send_connection_info_msg_cb = msn_dc_send_ok;
+ slpcall->wait_for_socket = TRUE;
+ }
+
+ } else {
+ /*
+ * Invalid direct connect invitation or
+ * TCP connection is not supported.
+ */
+ }
+
+ g_free(nonce);
+ g_free(bridges);
+ }
+ else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
+ {
+ /* A direct connection negotiation response */
+ msn_slp_process_transresp(slpcall, content);
+ }
+}
+
+static void
+got_ok(MsnSlpCall *slpcall,
+ const char *type, const char *content)
+{
+ g_return_if_fail(slpcall != NULL);
+ g_return_if_fail(type != NULL);
+
+ if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
+ {
+ char *content;
+ char *header;
+ char *nonce = NULL;
+ MsnSession *session = slpcall->slplink->session;
+ MsnSlpMessage *msg;
+ MsnDirectConn *dc;
+ MsnUser *user;
+
+ if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) {
+ /* Don't attempt a direct connection if disabled. */
+ msn_slpcall_session_init(slpcall);
+ return;
+ }
+
+ if (slpcall->slplink->dc != NULL) {
+ /* If we already have an established direct connection
+ * then just start the transfer.
+ */
+ msn_slpcall_session_init(slpcall);
+ return;
+ }
+
+ user = msn_userlist_find_user(session->userlist,
+ slpcall->slplink->remote_user);
+ if (!user || !(user->clientid & 0xF0000000)) {
+ /* Just start a normal SB transfer. */
+ msn_slpcall_session_init(slpcall);
+ return;
+ }
+
+ /* Try direct file transfer by sending a second INVITE */
+ dc = msn_dc_new(slpcall);
+ slpcall->branch = rand_guid();
+
+ dc->listen_data = purple_network_listen_range(
+ 0, 0,
+ SOCK_STREAM,
+ msn_dc_listen_socket_created_cb,
+ dc
+ );
+
+ header = g_strdup_printf(
+ "INVITE MSNMSGR:%s MSNSLP/1.0",
+ slpcall->slplink->remote_user
+ );
+
+ if (dc->nonce_type == DC_NONCE_SHA1)
+ nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash);
+
+ if (dc->listen_data == NULL) {
+ /* Listen socket creation failed */
+ purple_debug_info("msn", "got_ok: listening failed\n");
+
+ content = g_strdup_printf(
+ "Bridges: TCPv1\r\n"
+ "NetID: %u\r\n"
+ "Conn-Type: IP-Restrict-NAT\r\n"
+ "UPnPNat: false\r\n"
+ "ICF: false\r\n"
+ "%s"
+ "\r\n",
+
+ rand() % G_MAXUINT32,
+ nonce ? nonce : ""
+ );
+
+ } else {
+ /* Listen socket created successfully. */
+ purple_debug_info("msn", "got_ok: listening socket created\n");
+
+ content = g_strdup_printf(
+ "Bridges: TCPv1\r\n"
+ "NetID: 0\r\n"
+ "Conn-Type: Direct-Connect\r\n"
+ "UPnPNat: false\r\n"
+ "ICF: false\r\n"
+ "%s"
+ "\r\n",
+
+ nonce ? nonce : ""
+ );
+ }
+
+ msg = msn_slpmsg_sip_new(
+ slpcall,
+ 0,
+ header,
+ slpcall->branch,
+ "application/x-msnmsgr-transreqbody",
+ content
+ );
+ msg->info = "DC INVITE";
+ msg->text_body = TRUE;
+ g_free(nonce);
+ g_free(header);
+ g_free(content);
+
+ msn_slplink_queue_slpmsg(slpcall->slplink, msg);
+ }
+ else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
+ {
+ /* Do we get this? */
+ purple_debug_info("msn", "OK with transreqbody\n");
+ }
+ else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
+ {
+ msn_slp_process_transresp(slpcall, content);
+ }
+}
+
+static void
+got_error(MsnSlpCall *slpcall,
+ const char *error, const char *type, const char *content)
+{
+ /* It's not valid. Kill this off. */
+ purple_debug_error("msn", "Received non-OK result: %s\n",
+ error ? error : "Unknown");
+
+ if (type && !strcmp(type, "application/x-msnmsgr-transreqbody")) {
+ MsnDirectConn *dc = slpcall->slplink->dc;
+ if (dc) {
+ msn_dc_fallback_to_sb(dc);
+ return;
+ }
+ }
+
+ slpcall->wasted = TRUE;
+}
+
+static MsnSlpCall *
+msn_slp_sip_recv(MsnSlpLink *slplink, const char *body)
+{
+ MsnSlpCall *slpcall;
+
+ if (body == NULL)
+ {
+ purple_debug_warning("msn", "received bogus message\n");
+ return NULL;
+ }
+
+ if (!strncmp(body, "INVITE", strlen("INVITE")))
+ {
+ /* This is an INVITE request */
+ char *branch;
+ char *call_id;
+ char *content;
+ char *content_type;
+
+ /* From: <msnmsgr:buddy@hotmail.com> */
+#if 0
+ slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n");
+#endif
+
+ branch = get_token(body, ";branch={", "}");
+
+ call_id = get_token(body, "Call-ID: {", "}");
+
+#if 0
+ long content_len = -1;
+
+ temp = get_token(body, "Content-Length: ", "\r\n");
+ if (temp != NULL)
+ content_len = atoi(temp);
+ g_free(temp);
+#endif
+ content_type = get_token(body, "Content-Type: ", "\r\n");
+
+ content = get_token(body, "\r\n\r\n", NULL);
+
+ slpcall = NULL;
+ if (branch && call_id)
+ {
+ slpcall = msn_slplink_find_slp_call(slplink, call_id);
+ if (slpcall)
+ {
+ g_free(slpcall->branch);
+ slpcall->branch = g_strdup(branch);
+ got_invite(slpcall, branch, content_type, content);
+ }
+ else if (content_type && content)
+ {
+ slpcall = msn_slpcall_new(slplink);
+ slpcall->id = g_strdup(call_id);
+ got_invite(slpcall, branch, content_type, content);
+ }
+ }
+
+ g_free(call_id);
+ g_free(branch);
+ g_free(content_type);
+ g_free(content);
+ }
+ else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 ")))
+ {
+ /* This is a response */
+ char *content;
+ char *content_type;
+ /* Make sure this is "OK" */
+ const char *status = body + strlen("MSNSLP/1.0 ");
+ char *call_id;
+
+ call_id = get_token(body, "Call-ID: {", "}");
+ slpcall = msn_slplink_find_slp_call(slplink, call_id);
+ g_free(call_id);
+
+ g_return_val_if_fail(slpcall != NULL, NULL);
+
+ content_type = get_token(body, "Content-Type: ", "\r\n");
+
+ content = get_token(body, "\r\n\r\n", NULL);
+
+ if (strncmp(status, "200 OK", 6))
+ {
+ char *error = NULL;
+ const char *c;
+
+ /* Eww */
+ if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) ||
+ (c = strchr(status, '\0')))
+ {
+ size_t len = c - status;
+ error = g_strndup(status, len);
+ }
+
+ got_error(slpcall, error, content_type, content);
+ g_free(error);
+
+ } else {
+ /* Everything's just dandy */
+ got_ok(slpcall, content_type, content);
+ }
+
+ g_free(content_type);
+ g_free(content);
+ }
+ else if (!strncmp(body, "BYE", strlen("BYE")))
+ {
+ /* This is a BYE request */
+ char *call_id;
+
+ call_id = get_token(body, "Call-ID: {", "}");
+ slpcall = msn_slplink_find_slp_call(slplink, call_id);
+ g_free(call_id);
+
+ if (slpcall != NULL)
+ slpcall->wasted = TRUE;
+
+ /* msn_slpcall_destroy(slpcall); */
+ }
+ else
+ slpcall = NULL;
+
+ return slpcall;
+}
+
MsnSlpCall *
msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
{
@@ -199,13 +1060,13 @@ msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
slpcall = NULL;
body = slpmsg->buffer;
- body_len = slpmsg->offset;
+ body_len = slpmsg->header->offset;
- if (slpmsg->flags == 0x0 || slpmsg->flags == 0x1000000)
+ if (slpmsg->header->flags == P2P_NO_FLAG || slpmsg->header->flags == P2P_WML2009_COMP)
{
char *body_str;
- if (slpmsg->session_id == 64)
+ if (slpmsg->header->session_id == 64)
{
/* This is for handwritten messages (Ink) */
GError *error = NULL;
@@ -262,11 +1123,9 @@ msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
}
g_free(body_str);
}
- else if (slpmsg->flags == 0x20 ||
- slpmsg->flags == 0x1000020 ||
- slpmsg->flags == 0x1000030)
+ else if (msn_p2p_msg_is_data(slpmsg->header->flags))
{
- slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id);
+ slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->header->session_id);
if (slpcall != NULL)
{
@@ -282,7 +1141,7 @@ msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
}
}
#if 0
- else if (slpmsg->flags == 0x100)
+ else if (slpmsg->header->flags == 0x100)
{
slpcall = slplink->directconn->initial_call;
@@ -290,13 +1149,13 @@ msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
msn_slpcall_session_init(slpcall);
}
#endif
- else if (slpmsg->flags == 0x2)
+ else if (slpmsg->header->flags == P2P_ACK)
{
/* Acknowledgement of previous message. Don't do anything currently. */
}
else
- purple_debug_warning("msn", "Unprocessed SLP message with flags 0x%08lx\n",
- slpmsg->flags);
+ purple_debug_warning("msn", "Unprocessed SLP message with flags 0x%04x\n",
+ slpmsg->header->flags);
return slpcall;
}
diff --git a/libpurple/protocols/msn/slpcall.h b/libpurple/protocols/msn/slpcall.h
index fb68c79952..04bb88a210 100644
--- a/libpurple/protocols/msn/slpcall.h
+++ b/libpurple/protocols/msn/slpcall.h
@@ -34,8 +34,6 @@ typedef enum
#include "internal.h"
-#include "ft.h"
-
#include "slplink.h"
/* The official client seems to timeout slp calls after 5 minutes */
@@ -94,7 +92,7 @@ void msn_slpcall_init(MsnSlpCall *slpcall, MsnSlpCallType type);
void msn_slpcall_session_init(MsnSlpCall *slpcall);
void msn_slpcall_destroy(MsnSlpCall *slpcall);
void msn_slpcall_invite(MsnSlpCall *slpcall, const char *euf_guid,
- int app_id, const char *context);
+ MsnP2PAppId app_id, const char *context);
void msn_slpcall_close(MsnSlpCall *slpcall);
#endif /* MSN_SLPCALL_H */
diff --git a/libpurple/protocols/msn/slplink.c b/libpurple/protocols/msn/slplink.c
index ef70203bb1..20821bc7b6 100644
--- a/libpurple/protocols/msn/slplink.c
+++ b/libpurple/protocols/msn/slplink.c
@@ -21,30 +21,37 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
+#include "internal.h"
+#include "debug.h"
+
#include "msn.h"
#include "slplink.h"
+#include "slpmsg_part.h"
+#include "sbconn.h"
#include "switchboard.h"
#include "slp.h"
+#include "p2p.h"
#ifdef MSN_DEBUG_SLP_FILES
static int m_sc = 0;
static int m_rc = 0;
static void
-debug_msg_to_file(MsnMessage *msg, gboolean send)
+debug_part_to_file(MsnSlpMessage *msg, gboolean send)
{
char *tmp;
char *dir;
- char *pload;
+ char *data;
int c;
- gsize pload_size;
+ gsize data_size;
dir = send ? "send" : "recv";
c = send ? m_sc++ : m_rc++;
tmp = g_strdup_printf("%s/msntest/%s/%03d", g_get_home_dir(), dir, c);
- pload = msn_message_gen_payload(msg, &pload_size);
- if (!purple_util_write_data_to_file_absolute(tmp, pload, pload_size))
+ data = msn_slpmsg_serialize(msg, &data_size);
+ if (!purple_util_write_data_to_file_absolute(tmp, data, data_size))
{
purple_debug_error("msn", "could not save debug file\n");
}
@@ -81,7 +88,7 @@ msn_slplink_new(MsnSession *session, const char *username)
return msn_slplink_ref(slplink);
}
-void
+static void
msn_slplink_destroy(MsnSlpLink *slplink)
{
MsnSession *session;
@@ -264,78 +271,69 @@ msn_slplink_find_slp_call_with_session_id(MsnSlpLink *slplink, long id)
return NULL;
}
-void
-msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg)
+static void
+msn_slplink_send_part(MsnSlpLink *slplink, MsnSlpMessagePart *part)
{
if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABLISHED)
{
- msn_dc_enqueue_msg(slplink->dc, msg);
+ msn_dc_enqueue_part(slplink->dc, part);
}
else
{
- if (slplink->swboard == NULL)
- {
- slplink->swboard = msn_session_get_swboard(slplink->session,
- slplink->remote_user, MSN_SB_FLAG_FT);
-
- g_return_if_fail(slplink->swboard != NULL);
-
- /* If swboard is destroyed we will be too */
- slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink);
- }
-
- msn_switchboard_send_msg(slplink->swboard, msg, TRUE);
+ msn_sbconn_send_part(slplink, part);
}
}
void
msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
{
- MsnMessage *msg;
+ MsnSlpMessagePart *part;
long long real_size;
size_t len = 0;
/* Maybe we will want to create a new msg for this slpmsg instead of
* reusing the same one all the time. */
- msg = slpmsg->msg;
+ part = msn_slpmsgpart_new(slpmsg->header, slpmsg->footer);
+ part->ack_data = slpmsg;
- real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size;
+ real_size = (slpmsg->header->flags == P2P_ACK) ? 0 : slpmsg->size;
- if (slpmsg->offset < real_size)
+ if (slpmsg->header->offset < real_size)
{
if (slpmsg->slpcall && slpmsg->slpcall->xfer && purple_xfer_get_type(slpmsg->slpcall->xfer) == PURPLE_XFER_SEND &&
purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED)
{
- len = MIN(1202, slpmsg->slpcall->u.outgoing.len);
- msn_message_set_bin_data(msg, slpmsg->slpcall->u.outgoing.data, len);
+ len = MIN(MSN_SBCONN_MAX_SIZE, slpmsg->slpcall->u.outgoing.len);
+ msn_slpmsgpart_set_bin_data(part, slpmsg->slpcall->u.outgoing.data, len);
}
else
{
- len = slpmsg->size - slpmsg->offset;
+ len = slpmsg->size - slpmsg->header->offset;
- if (len > 1202)
- len = 1202;
+ if (len > MSN_SBCONN_MAX_SIZE)
+ len = MSN_SBCONN_MAX_SIZE;
- msn_message_set_bin_data(msg, slpmsg->buffer + slpmsg->offset, len);
+ msn_slpmsgpart_set_bin_data(part, slpmsg->buffer + slpmsg->header->offset, len);
}
- msg->msnslp_header.offset = slpmsg->offset;
- msg->msnslp_header.length = len;
+ slpmsg->header->length = len;
}
+#if 0
+ /* TODO: port this function to SlpMessageParts */
if (purple_debug_is_verbose())
msn_message_show_readable(msg, slpmsg->info, slpmsg->text_body);
+#endif
#ifdef MSN_DEBUG_SLP_FILES
- debug_msg_to_file(msg, TRUE);
+ debug_part_to_file(slpmsg, TRUE);
#endif
- slpmsg->msgs =
- g_list_append(slpmsg->msgs, msn_message_ref(msg));
- msn_slplink_send_msg(slplink, msg);
+ slpmsg->parts = g_list_append(slpmsg->parts, part);
+ msn_slplink_send_part(slplink, part);
+
- if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||
- slpmsg->flags == 0x1000030) &&
+ if (msn_p2p_msg_is_data(slpmsg->header->flags) &&
(slpmsg->slpcall != NULL))
{
slpmsg->slpcall->progress = TRUE;
@@ -343,124 +341,39 @@ msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
if (slpmsg->slpcall->progress_cb != NULL)
{
slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size,
- len, slpmsg->offset);
+ len, slpmsg->header->offset);
}
}
/* slpmsg->offset += len; */
}
-/* We have received the message ack */
-static void
-msg_ack(MsnMessage *msg, void *data)
-{
- MsnSlpMessage *slpmsg;
- long long real_size;
-
- slpmsg = data;
-
- real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size;
-
- slpmsg->offset += msg->msnslp_header.length;
-
- slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
-
- if (slpmsg->offset < real_size)
- {
- if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED)
- {
- slpmsg->slpcall->xfer_msg = slpmsg;
- msn_message_ref(msg);
- purple_xfer_prpl_ready(slpmsg->slpcall->xfer);
- }
- else
- msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
- }
- else
- {
- /* The whole message has been sent */
- if (slpmsg->flags == 0x20 ||
- slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030)
- {
- if (slpmsg->slpcall != NULL)
- {
- if (slpmsg->slpcall->cb)
- slpmsg->slpcall->cb(slpmsg->slpcall,
- NULL, 0);
- }
- }
- }
-
- msn_message_unref(msg);
-}
-
-/* We have received the message nak. */
-static void
-msg_nak(MsnMessage *msg, void *data)
-{
- MsnSlpMessage *slpmsg;
-
- slpmsg = data;
-
- msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
-
- slpmsg->msgs = g_list_remove(slpmsg->msgs, msg);
- msn_message_unref(msg);
-}
-
static void
msn_slplink_release_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
{
- MsnMessage *msg;
- const char *passport;
-
- slpmsg->msg = msg = msn_message_new_msnslp();
+ slpmsg = slpmsg;
+ slpmsg->footer = g_new0(MsnP2PFooter, 1);
- if (slpmsg->flags == 0x0)
+ if (slpmsg->header->flags == P2P_NO_FLAG)
{
- msg->msnslp_header.session_id = slpmsg->session_id;
- msg->msnslp_header.ack_id = rand() % 0xFFFFFF00;
+ slpmsg->header->ack_id = rand() % 0xFFFFFF00;
}
- else if (slpmsg->flags == 0x2)
- {
- msg->msnslp_header.session_id = slpmsg->session_id;
- msg->msnslp_header.ack_id = slpmsg->ack_id;
- msg->msnslp_header.ack_size = slpmsg->ack_size;
- msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id;
- }
- else if (slpmsg->flags == 0x20 ||
- slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030)
+ else if (msn_p2p_msg_is_data(slpmsg->header->flags))
{
MsnSlpCall *slpcall;
slpcall = slpmsg->slpcall;
g_return_if_fail(slpcall != NULL);
- msg->msnslp_header.session_id = slpcall->session_id;
- msg->msnslp_footer.value = slpcall->app_id;
- msg->msnslp_header.ack_id = rand() % 0xFFFFFF00;
- }
- else if (slpmsg->flags == 0x100)
- {
- msg->msnslp_header.ack_id = slpmsg->ack_id;
- msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id;
- msg->msnslp_header.ack_size = slpmsg->ack_size;
+ slpmsg->header->session_id = slpcall->session_id;
+ slpmsg->footer->value = slpcall->app_id;
+ slpmsg->header->ack_id = rand() % 0xFFFFFF00;
}
- msg->msnslp_header.id = slpmsg->id;
- msg->msnslp_header.flags = slpmsg->flags;
+ slpmsg->header->id = slpmsg->id;
- msg->msnslp_header.total_size = slpmsg->size;
-
- passport = purple_normalize(slplink->session->account, slplink->remote_user);
- msn_message_set_attr(msg, "P2P-Dest", passport);
-
- msg->ack_cb = msg_ack;
- msg->nak_cb = msg_nak;
- msg->ack_data = slpmsg;
+ slpmsg->header->total_size = slpmsg->size;
msn_slplink_send_msgpart(slplink, slpmsg);
-
- msn_message_destroy(msg);
}
void
@@ -494,25 +407,18 @@ msn_slplink_send_queued_slpmsgs(MsnSlpLink *slplink)
}
static MsnSlpMessage *
-msn_slplink_create_ack(MsnSlpLink *slplink, MsnSlpHeader *header)
+msn_slplink_create_ack(MsnSlpLink *slplink, MsnP2PHeader *header)
{
MsnSlpMessage *slpmsg;
- slpmsg = msn_slpmsg_new(slplink);
-
- slpmsg->session_id = header->session_id;
- slpmsg->size = header->total_size;
- slpmsg->flags = 0x02;
- slpmsg->ack_id = header->id;
- slpmsg->ack_sub_id = header->ack_id;
- slpmsg->ack_size = header->total_size;
- slpmsg->info = "SLP ACK";
+ slpmsg = msn_slpmsg_ack_new(header);
+ msn_slpmsg_set_slplink(slpmsg, slplink);
return slpmsg;
}
static void
-msn_slplink_send_ack(MsnSlpLink *slplink, MsnSlpHeader *header)
+msn_slplink_send_ack(MsnSlpLink *slplink, MsnP2PHeader *header)
{
MsnSlpMessage *slpmsg = msn_slplink_create_ack(slplink, header);
@@ -520,33 +426,6 @@ msn_slplink_send_ack(MsnSlpLink *slplink, MsnSlpHeader *header)
msn_slpmsg_destroy(slpmsg);
}
-static void
-send_file_cb(MsnSlpCall *slpcall)
-{
- MsnSlpMessage *slpmsg;
- PurpleXfer *xfer;
-
- xfer = (PurpleXfer *)slpcall->xfer;
- if (purple_xfer_get_status(xfer) >= PURPLE_XFER_STATUS_STARTED)
- return;
-
- purple_xfer_ref(xfer);
- purple_xfer_start(xfer, -1, NULL, 0);
- if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) {
- purple_xfer_unref(xfer);
- return;
- }
- purple_xfer_unref(xfer);
-
- slpmsg = msn_slpmsg_new(slpcall->slplink);
- slpmsg->slpcall = slpcall;
- slpmsg->flags = 0x1000030;
- slpmsg->info = "SLP FILE";
- slpmsg->size = purple_xfer_get_size(xfer);
-
- msn_slplink_send_slpmsg(slpcall->slplink, slpmsg);
-}
-
static MsnSlpMessage *
msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id)
{
@@ -556,280 +435,196 @@ msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id)
{
MsnSlpMessage *slpmsg = e->data;
- if ((slpmsg->session_id == session_id) && (slpmsg->id == id))
+ if ((slpmsg->header->session_id == session_id) && (slpmsg->id == id))
return slpmsg;
}
return NULL;
}
-void
-msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpHeader *header, const char *data, gsize len)
+static MsnSlpMessage *
+init_first_msg(MsnSlpLink *slplink, MsnP2PHeader *header)
{
MsnSlpMessage *slpmsg;
- guint64 offset;
- PurpleXfer *xfer = NULL;
- if (header->total_size < header->length)
- {
- purple_debug_error("msn", "This can't be good\n");
- g_return_if_reached();
- }
-
- offset = header->offset;
+ slpmsg = msn_slpmsg_new(slplink);
+ slpmsg->id = header->id;
+ slpmsg->header->session_id = header->session_id;
+ slpmsg->size = header->total_size;
+ slpmsg->header->flags = header->flags;
- if (offset == 0)
+ if (slpmsg->header->session_id)
{
- slpmsg = msn_slpmsg_new(slplink);
- slpmsg->id = header->id;
- slpmsg->session_id = header->session_id;
- slpmsg->size = header->total_size;
- slpmsg->flags = header->flags;
-
- if (slpmsg->session_id)
+ slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->header->session_id);
+ if (slpmsg->slpcall != NULL)
{
- if (slpmsg->slpcall == NULL)
- slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id);
-
- if (slpmsg->slpcall != NULL)
+ if (msn_p2p_msg_is_data(header->flags))
{
- if (slpmsg->flags == 0x20 ||
- slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030)
+ PurpleXfer *xfer = slpmsg->slpcall->xfer;
+ if (xfer != NULL)
{
- xfer = slpmsg->slpcall->xfer;
- if (xfer != NULL)
- {
- slpmsg->ft = TRUE;
- slpmsg->slpcall->xfer_msg = slpmsg;
-
- purple_xfer_ref(xfer);
- purple_xfer_start(xfer, -1, NULL, 0);
-
- if (xfer->data == NULL) {
- purple_xfer_unref(xfer);
- msn_slpmsg_destroy(slpmsg);
- g_return_if_reached();
- } else {
- purple_xfer_unref(xfer);
- }
+ slpmsg->ft = TRUE;
+ slpmsg->slpcall->xfer_msg = slpmsg;
+
+ purple_xfer_ref(xfer);
+ purple_xfer_start(xfer, -1, NULL, 0);
+
+ if (xfer->data == NULL) {
+ purple_xfer_unref(xfer);
+ msn_slpmsg_destroy(slpmsg);
+ g_return_val_if_reached(NULL);
+ } else {
+ purple_xfer_unref(xfer);
}
}
}
}
- if (!slpmsg->ft && slpmsg->size)
- {
- slpmsg->buffer = g_try_malloc(slpmsg->size);
- if (slpmsg->buffer == NULL)
- {
- purple_debug_error("msn", "Failed to allocate buffer for slpmsg\n");
- msn_slpmsg_destroy(slpmsg);
- return;
- }
- }
}
- else
+ if (!slpmsg->ft && slpmsg->size)
{
- slpmsg = msn_slplink_message_find(slplink, header->session_id, header->id);
- if (slpmsg == NULL)
+ slpmsg->buffer = g_try_malloc(slpmsg->size);
+ if (slpmsg->buffer == NULL)
{
- /* Probably the transfer was cancelled */
- purple_debug_error("msn", "Couldn't find slpmsg\n");
- return;
+ purple_debug_error("msn", "Failed to allocate buffer for slpmsg\n");
+ msn_slpmsg_destroy(slpmsg);
+ return NULL;
}
}
- if (slpmsg->ft)
- {
- xfer = slpmsg->slpcall->xfer;
- slpmsg->slpcall->u.incoming_data =
- g_byte_array_append(slpmsg->slpcall->u.incoming_data, (const guchar *)data, len);
- purple_xfer_prpl_ready(xfer);
- }
- else if (slpmsg->size && slpmsg->buffer)
- {
- if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size || slpmsg->offset != offset)
- {
- purple_debug_error("msn",
- "Oversized slpmsg - msgsize=%lld offset=%" G_GUINT64_FORMAT " len=%" G_GSIZE_FORMAT "\n",
- slpmsg->size, offset, len);
- g_return_if_reached();
- } else {
- memcpy(slpmsg->buffer + offset, data, len);
- slpmsg->offset += len;
- }
- }
+ return slpmsg;
+}
- if ((slpmsg->flags == 0x20 ||
- slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) &&
- (slpmsg->slpcall != NULL))
- {
- slpmsg->slpcall->progress = TRUE;
+static void
+process_complete_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg, MsnP2PHeader *header)
+{
+ MsnSlpCall *slpcall;
- if (slpmsg->slpcall->progress_cb != NULL)
- {
- slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size,
- len, offset);
- }
- }
+ slpcall = msn_slp_process_msg(slplink, slpmsg);
-#if 0
- if (slpmsg->buffer == NULL)
+ if (slpcall == NULL) {
+ msn_slpmsg_destroy(slpmsg);
return;
-#endif
-
- if (header->offset + header->length >= header->total_size)
- {
- /* All the pieces of the slpmsg have been received */
- MsnSlpCall *slpcall;
-
- slpcall = msn_slp_process_msg(slplink, slpmsg);
-
- if (slpcall == NULL) {
- msn_slpmsg_destroy(slpmsg);
- return;
- }
+ }
- purple_debug_info("msn", "msn_slplink_process_msg: slpmsg complete\n");
+ purple_debug_info("msn", "msn_slplink_process_msg: slpmsg complete\n");
- if (/* !slpcall->wasted && */ slpmsg->flags == 0x100)
- {
+ if (/* !slpcall->wasted && */ slpmsg->header->flags == P2P_DC_HANDSHAKE)
+ {
#if 0
- MsnDirectConn *directconn;
+ MsnDirectConn *directconn;
- directconn = slplink->directconn;
- if (!directconn->acked)
- msn_directconn_send_handshake(directconn);
+ directconn = slplink->directconn;
+ if (!directconn->acked)
+ msn_directconn_send_handshake(directconn);
#endif
- }
- else if (slpmsg->flags == 0x00 || slpmsg->flags == 0x1000000 ||
- slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||
- slpmsg->flags == 0x1000030)
- {
- /* Release all the messages and send the ACK */
-
- if (slpcall->wait_for_socket) {
- /*
- * Save ack for later because we have to send
- * a 200 OK message to the previous direct connect
- * invitation before ACK but the listening socket isn't
- * created yet.
- */
- purple_debug_info("msn", "msn_slplink_process_msg: save ACK\n");
-
- slpcall->slplink->dc->prev_ack = msn_slplink_create_ack(slplink, header);
- } else if (!slpcall->wasted) {
- purple_debug_info("msn", "msn_slplink_process_msg: send ACK\n");
-
- msn_slplink_send_ack(slplink, header);
- msn_slplink_send_queued_slpmsgs(slplink);
- }
- }
-
- msn_slpmsg_destroy(slpmsg);
-
- if (!slpcall->wait_for_socket && slpcall->wasted)
- msn_slpcall_destroy(slpcall);
- }
-}
-
-static gchar *
-gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path)
-{
- gsize size = 0;
- MsnFileContext *header;
- gchar *u8 = NULL;
- gchar *ret;
- gunichar2 *uni = NULL;
- glong currentChar = 0;
- glong len = 0;
- const char *preview;
- gsize preview_len;
-
- size = purple_xfer_get_size(xfer);
-
- purple_xfer_prepare_thumbnail(xfer, "png");
-
- if (!file_name) {
- gchar *basename = g_path_get_basename(file_path);
- u8 = purple_utf8_try_convert(basename);
- g_free(basename);
- file_name = u8;
}
+ else if (slpmsg->header->flags == P2P_NO_FLAG || slpmsg->header->flags == P2P_WML2009_COMP ||
+ msn_p2p_msg_is_data(slpmsg->header->flags))
+ {
+ /* Release all the messages and send the ACK */
+
+ if (slpcall->wait_for_socket) {
+ /*
+ * Save ack for later because we have to send
+ * a 200 OK message to the previous direct connect
+ * invitation before ACK but the listening socket isn't
+ * created yet.
+ */
+ purple_debug_info("msn", "msn_slplink_process_msg: save ACK\n");
- uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL);
+ slpcall->slplink->dc->prev_ack = msn_slplink_create_ack(slplink, header);
+ } else if (!slpcall->wasted) {
+ purple_debug_info("msn", "msn_slplink_process_msg: send ACK\n");
- if (u8) {
- g_free(u8);
- file_name = NULL;
- u8 = NULL;
+ msn_slplink_send_ack(slplink, header);
+ msn_slplink_send_queued_slpmsgs(slplink);
+ }
}
- preview = purple_xfer_get_thumbnail(xfer, &preview_len);
- header = g_malloc(sizeof(MsnFileContext) + preview_len);
+ msn_slpmsg_destroy(slpmsg);
- header->length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1);
- header->version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */
- header->file_size = GUINT64_TO_LE(size);
- if (preview)
- header->type = GUINT32_TO_LE(0);
- else
- header->type = GUINT32_TO_LE(1);
+ if (!slpcall->wait_for_socket && slpcall->wasted)
+ msn_slpcall_destroy(slpcall);
+}
- len = MIN(len, MAX_FILE_NAME_LEN);
- for (currentChar = 0; currentChar < len; currentChar++) {
- header->file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]);
+static void
+slpmsg_add_part(MsnSlpMessage *slpmsg, MsnSlpMessagePart *part)
+{
+ if (slpmsg->ft) {
+ slpmsg->slpcall->u.incoming_data =
+ g_byte_array_append(slpmsg->slpcall->u.incoming_data, (const guchar *)part->buffer, part->size);
+ purple_xfer_prpl_ready(slpmsg->slpcall->xfer);
}
- memset(&header->file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2);
-
- memset(&header->unknown1, 0, sizeof(header->unknown1));
- header->unknown2 = GUINT32_TO_LE(0xffffffff);
- if (preview) {
- memcpy(&header->preview, preview, preview_len);
+ else if (slpmsg->size && slpmsg->buffer) {
+ if (G_MAXSIZE - part->size < part->header->offset
+ || (part->header->offset + part->size) > slpmsg->size
+ || slpmsg->header->offset != part->header->offset) {
+ purple_debug_error("msn",
+ "Oversized slpmsg - msgsize=%lld offset=%" G_GUINT64_FORMAT " len=%" G_GSIZE_FORMAT "\n",
+ slpmsg->size, part->header->offset, part->size);
+ g_return_if_reached();
+ } else {
+ memcpy(slpmsg->buffer + part->header->offset, part->buffer, part->size);
+ slpmsg->header->offset += part->size;
+ }
}
- header->preview[preview_len] = '\0';
-
- g_free(uni);
- ret = purple_base64_encode((const guchar *)header, sizeof(MsnFileContext) + preview_len);
- g_free(header);
- return ret;
}
void
-msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer)
+msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpMessagePart *part)
{
- MsnSlpCall *slpcall;
- char *context;
- const char *fn;
- const char *fp;
+ MsnSlpMessage *slpmsg;
+ MsnP2PHeader *header;
+ guint64 offset;
- fn = purple_xfer_get_filename(xfer);
- fp = purple_xfer_get_local_filename(xfer);
+ header = part->header;
- g_return_if_fail(slplink != NULL);
- g_return_if_fail(fp != NULL);
+ if (header->total_size < header->length)
+ {
+ /* We seem to have received a bad header */
+ purple_debug_warning("msn", "Total size listed in SLP binary header "
+ "was less than length of this particular message. This "
+ "should not happen. Dropping message.\n");
+ return;
+ }
- slpcall = msn_slpcall_new(slplink);
- msn_slpcall_init(slpcall, MSN_SLPCALL_DC);
+ offset = header->offset;
- slpcall->session_init_cb = send_file_cb;
- slpcall->end_cb = msn_xfer_end_cb;
- slpcall->cb = msn_xfer_completed_cb;
- slpcall->xfer = xfer;
- purple_xfer_ref(slpcall->xfer);
+ if (offset == 0)
+ slpmsg = init_first_msg(slplink, header);
+ else {
+ slpmsg = msn_slplink_message_find(slplink, header->session_id, header->id);
+ if (slpmsg == NULL)
+ {
+ /* Probably the transfer was cancelled */
+ purple_debug_error("msn", "Couldn't find slpmsg\n");
+ return;
+ }
+ }
- slpcall->pending = TRUE;
+ slpmsg_add_part(slpmsg, part);
- purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel);
- purple_xfer_set_read_fnc(xfer, msn_xfer_read);
- purple_xfer_set_write_fnc(xfer, msn_xfer_write);
- xfer->data = slpcall;
+ if (msn_p2p_msg_is_data(slpmsg->header->flags) &&
+ (slpmsg->slpcall != NULL))
+ {
+ slpmsg->slpcall->progress = TRUE;
- context = gen_context(xfer, fn, fp);
+ if (slpmsg->slpcall->progress_cb != NULL)
+ {
+ slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size,
+ part->size, offset);
+ }
+ }
- msn_slpcall_invite(slpcall, MSN_FT_GUID, 2, context);
+#if 0
+ if (slpmsg->buffer == NULL)
+ return;
+#endif
- g_free(context);
+ /* All the pieces of the slpmsg have been received */
+ if (header->offset + header->length >= header->total_size)
+ process_complete_msg(slplink, slpmsg, header);
}
void
@@ -857,7 +652,7 @@ msn_slplink_request_object(MsnSlpLink *slplink,
slpcall->cb = cb;
slpcall->end_cb = end_cb;
- msn_slpcall_invite(slpcall, MSN_OBJ_GUID, 1, msnobj_base64);
+ msn_slpcall_invite(slpcall, MSN_OBJ_GUID, P2P_APPID_OBJ, msnobj_base64);
g_free(msnobj_base64);
}
diff --git a/libpurple/protocols/msn/slplink.h b/libpurple/protocols/msn/slplink.h
index e3b2e97de0..a1fff48f45 100644
--- a/libpurple/protocols/msn/slplink.h
+++ b/libpurple/protocols/msn/slplink.h
@@ -26,8 +26,6 @@
typedef struct _MsnSlpLink MsnSlpLink;
-#include "ft.h"
-
#include "directconn.h"
#include "session.h"
#include "slpcall.h"
@@ -59,8 +57,6 @@ struct _MsnSlpLink
MsnSlpLink *msn_slplink_ref(MsnSlpLink *slplink);
void msn_slplink_unref(MsnSlpLink *slplink);
-void msn_slplink_destroy(MsnSlpLink *slplink);
-
/**
* @return An MsnSlpLink for the given user, or NULL if there is no
* existing MsnSlpLink.
@@ -83,10 +79,8 @@ void msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
void msn_slplink_send_slpmsg(MsnSlpLink *slplink,
MsnSlpMessage *slpmsg);
void msn_slplink_send_queued_slpmsgs(MsnSlpLink *slplink);
-void msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpHeader *header, const char *data, gsize len);
-void msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer);
+void msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpMessagePart *part);
-void msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg);
/* Only exported for msn_xfer_write */
void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
diff --git a/libpurple/protocols/msn/slpmsg.c b/libpurple/protocols/msn/slpmsg.c
index 4b3f96a926..58c08df261 100644
--- a/libpurple/protocols/msn/slpmsg.c
+++ b/libpurple/protocols/msn/slpmsg.c
@@ -21,8 +21,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "debug.h"
+
#include "slpmsg.h"
+#include "slpmsg_part.h"
#include "slplink.h"
/**************************************************************************
@@ -39,10 +43,48 @@ msn_slpmsg_new(MsnSlpLink *slplink)
if (purple_debug_is_verbose())
purple_debug_info("msn", "slpmsg new (%p)\n", slpmsg);
- slpmsg->slplink = slplink;
+ if (slplink)
+ msn_slpmsg_set_slplink(slpmsg, slplink);
+ else
+ slpmsg->slplink = NULL;
- slplink->slp_msgs =
- g_list_append(slplink->slp_msgs, slpmsg);
+ slpmsg->header = g_new0(MsnP2PHeader, 1);
+ slpmsg->footer = NULL;
+
+ return slpmsg;
+}
+
+MsnSlpMessage *msn_slpmsg_new_from_data(const char *data, size_t data_len)
+{
+ MsnSlpMessage *slpmsg;
+ MsnP2PHeader *header;
+ const char *tmp;
+ int body_len;
+
+ tmp = data;
+ slpmsg = msn_slpmsg_new(NULL);
+
+ if (data_len < sizeof(*header)) {
+ return NULL;
+ }
+
+ /* Extract the binary SLP header */
+ slpmsg->header = msn_p2p_header_from_wire((MsnP2PHeader*)tmp);
+
+ /* Extract the body */
+ body_len = data_len - (tmp - data);
+ /* msg->body_len = msg->msnslp_header.length; */
+
+ if (body_len > 0) {
+ slpmsg->size = body_len;
+ slpmsg->buffer = g_malloc(body_len);
+ memcpy(slpmsg->buffer, tmp, body_len);
+ tmp += body_len;
+ }
+
+ /* Extract the footer */
+ if (body_len >= 0)
+ slpmsg->footer = msn_p2p_footer_from_wire((MsnP2PFooter*)tmp);
return slpmsg;
}
@@ -67,26 +109,41 @@ msn_slpmsg_destroy(MsnSlpMessage *slpmsg)
if (slpmsg->img == NULL)
g_free(slpmsg->buffer);
- for (cur = slpmsg->msgs; cur != NULL; cur = g_list_delete_link(cur, cur))
+ for (cur = slpmsg->parts; cur != NULL; cur = g_list_delete_link(cur, cur))
{
/* Something is pointing to this slpmsg, so we should remove that
* pointer to prevent a crash. */
/* Ex: a user goes offline and after that we receive an ACK */
- MsnMessage *msg = cur->data;
+ MsnSlpMessagePart *part = cur->data;
- msg->ack_cb = NULL;
- msg->nak_cb = NULL;
- msg->ack_data = NULL;
- msn_message_unref(msg);
+ part->ack_cb = NULL;
+ part->nak_cb = NULL;
+ part->ack_data = NULL;
+ msn_slpmsgpart_destroy(part);
}
slplink->slp_msgs = g_list_remove(slplink->slp_msgs, slpmsg);
+ g_free(slpmsg->header);
+ g_free(slpmsg->footer);
+
g_free(slpmsg);
}
void
+msn_slpmsg_set_slplink(MsnSlpMessage *slpmsg, MsnSlpLink *slplink)
+{
+ g_return_if_fail(slplink != NULL);
+
+ slpmsg->slplink = slplink;
+
+ slplink->slp_msgs =
+ g_list_append(slplink->slp_msgs, slpmsg);
+
+}
+
+void
msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body,
long long size)
{
@@ -116,34 +173,6 @@ msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img)
slpmsg->size = purple_imgstore_get_size(img);
}
-void
-msn_slpmsg_show(MsnMessage *msg)
-{
- const char *info;
- gboolean text;
- guint32 flags;
-
- text = FALSE;
-
- flags = GUINT32_TO_LE(msg->msnslp_header.flags);
-
- switch (flags)
- {
- case 0x0:
- info = "SLP CONTROL";
- text = TRUE;
- break;
- case 0x2:
- info = "SLP ACK"; break;
- case 0x20:
- case 0x1000030:
- info = "SLP DATA"; break;
- default:
- info = "SLP UNKNOWN"; break;
- }
-
- msn_message_show_readable(msg, info, text);
-}
MsnSlpMessage *
msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq,
@@ -206,3 +235,128 @@ msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq,
return slpmsg;
}
+
+MsnSlpMessage *msn_slpmsg_ack_new(MsnP2PHeader *header)
+{
+ MsnSlpMessage *slpmsg;
+
+ slpmsg = msn_slpmsg_new(NULL);
+
+ slpmsg->header->session_id = header->session_id;
+ slpmsg->size = header->total_size;
+ slpmsg->header->flags = P2P_ACK;
+ slpmsg->header->ack_id = header->id;
+ slpmsg->header->ack_sub_id = header->ack_id;
+ slpmsg->header->ack_size = header->total_size;
+ slpmsg->info = "SLP ACK";
+
+ return slpmsg;
+}
+
+MsnSlpMessage *msn_slpmsg_obj_new(MsnSlpCall *slpcall, PurpleStoredImage *img)
+{
+ MsnSlpMessage *slpmsg;
+
+ slpmsg = msn_slpmsg_new(NULL);
+ slpmsg->slpcall = slpcall;
+ slpmsg->header->flags = P2P_MSN_OBJ_DATA;
+ slpmsg->info = "SLP DATA";
+
+ msn_slpmsg_set_image(slpmsg, img);
+
+ return slpmsg;
+}
+
+MsnSlpMessage *msn_slpmsg_dataprep_new(MsnSlpCall *slpcall)
+{
+ MsnSlpMessage *slpmsg;
+
+ slpmsg = msn_slpmsg_new(NULL);
+
+ slpmsg->slpcall = slpcall;
+ slpmsg->header->session_id = slpcall->session_id;
+ msn_slpmsg_set_body(slpmsg, NULL, 4);
+ slpmsg->info = "SLP DATA PREP";
+
+ return slpmsg;
+
+}
+
+MsnSlpMessage *msn_slpmsg_file_new(MsnSlpCall *slpcall, size_t size)
+{
+ MsnSlpMessage *slpmsg;
+
+ slpmsg = msn_slpmsg_new(NULL);
+
+ slpmsg->slpcall = slpcall;
+ slpmsg->header->flags = P2P_FILE_DATA;
+ slpmsg->info = "SLP FILE";
+ slpmsg->size = size;
+
+ return slpmsg;
+}
+
+char *msn_slpmsg_serialize(MsnSlpMessage *slpmsg, size_t *ret_size)
+{
+ MsnP2PHeader *header;
+ MsnP2PFooter *footer;
+ char *base;
+ char *tmp;
+ size_t siz;
+
+ base = g_malloc(P2P_PACKET_HEADER_SIZE + slpmsg->size + sizeof(MsnP2PFooter));
+ tmp = base;
+
+ header = msn_p2p_header_to_wire(slpmsg->header);
+ footer = msn_p2p_footer_to_wire(slpmsg->footer);
+
+ siz = sizeof(MsnP2PHeader);
+ /* Copy header */
+ memcpy(tmp, (char*)header, siz);
+ tmp += siz;
+
+ /* Copy body */
+ memcpy(tmp, slpmsg->buffer, slpmsg->size);
+ tmp += slpmsg->size;
+
+ /* Copy footer */
+ siz = sizeof(MsnP2PFooter);
+ memcpy(tmp, (char*)footer, siz);
+ tmp += siz;
+
+ *ret_size = tmp - base;
+
+ return base;
+}
+
+void msn_slpmsg_show_readable(MsnSlpMessage *slpmsg)
+{
+ GString *str;
+
+ str = g_string_new(NULL);
+
+ g_string_append_printf(str, "Session ID: %u\r\n", slpmsg->header->session_id);
+ g_string_append_printf(str, "ID: %u\r\n", slpmsg->header->id);
+ g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", slpmsg->header->offset);
+ g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", slpmsg->header->total_size);
+ g_string_append_printf(str, "Length: %u\r\n", slpmsg->header->length);
+ g_string_append_printf(str, "Flags: 0x%x\r\n", slpmsg->header->flags);
+ g_string_append_printf(str, "ACK ID: %u\r\n", slpmsg->header->ack_id);
+ g_string_append_printf(str, "SUB ID: %u\r\n", slpmsg->header->ack_sub_id);
+ g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", slpmsg->header->ack_size);
+
+ if (purple_debug_is_verbose() && slpmsg->buffer != NULL) {
+ g_string_append_len(str, (gchar*)slpmsg->buffer, slpmsg->size);
+
+ if (slpmsg->buffer[slpmsg->size - 1] == '\0') {
+ str->len--;
+ g_string_append(str, " 0x00");
+ }
+ g_string_append(str, "\r\n");
+
+ }
+
+ g_string_append_printf(str, "Footer: %u\r\n", slpmsg->footer->value);
+
+ purple_debug_info("msn", "SlpMessage %s:\n{%s}\n", slpmsg->info, str->str);
+}
diff --git a/libpurple/protocols/msn/slpmsg.h b/libpurple/protocols/msn/slpmsg.h
index d85a462313..facff320f2 100644
--- a/libpurple/protocols/msn/slpmsg.h
+++ b/libpurple/protocols/msn/slpmsg.h
@@ -31,7 +31,7 @@ typedef struct _MsnSlpMessage MsnSlpMessage;
#include "slpcall.h"
#include "slplink.h"
#include "session.h"
-#include "msg.h"
+#include "p2p.h"
#include "slp.h"
@@ -45,37 +45,24 @@ struct _MsnSlpMessage
MsnSlpLink *slplink; /**< The slplink through which this slp message is being sent. */
MsnSession *session;
- long session_id;
+ MsnP2PHeader *header;
+ MsnP2PFooter *footer;
+
long id;
- long ack_id;
- long ack_sub_id;
- long long ack_size;
gboolean sip; /**< A flag that states if this is a SIP slp message. */
- long flags;
gboolean ft;
PurpleStoredImage *img;
guchar *buffer;
/**
- * For outgoing messages this is the number of bytes from buffer that
- * have already been sent out. For incoming messages this is the
- * number of bytes that have been written to buffer.
- */
- long long offset;
-
- /**
* This is the size of buffer, unless this is an outgoing file transfer,
* in which case this is the size of the file.
*/
long long size;
- GList *msgs; /**< The real messages. */
-
-#if 1
- MsnMessage *msg; /**< The temporary real message that will be sent. */
-#endif
+ GList *parts; /**< A list with the SlpMsgParts */
const char *info;
gboolean text_body;
@@ -85,17 +72,35 @@ struct _MsnSlpMessage
* Creates a new slp message
*
* @param slplink The slplink through which this slp message will be sent.
+ * If it's set to NULL, it is a temporary SlpMessage.
* @return The created slp message.
*/
MsnSlpMessage *msn_slpmsg_new(MsnSlpLink *slplink);
/**
+ * Creates a MsnSlpMessage without a MsnSlpLink by parsing the raw data.
+ *
+ * @param data The raw data with the slp message.
+ * @param data_len The len of the data
+ *
+ * @return The createed slp message.
+ */
+MsnSlpMessage *msn_slpmsg_new_from_data(const char *data, size_t data_len);
+
+/**
* Destroys a slp message
*
* @param slpmsg The slp message to destory.
*/
void msn_slpmsg_destroy(MsnSlpMessage *slpmsg);
+/**
+ * Relate this SlpMessage with an existing SlpLink
+ *
+ * @param slplink The SlpLink that will send this message.
+ */
+void msn_slpmsg_set_slplink(MsnSlpMessage *slpmsg, MsnSlpLink *slplink);
+
void msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body,
long long size);
void msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img);
@@ -107,6 +112,54 @@ MsnSlpMessage * msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq,
const char *content_type,
const char *content);
-void msn_slpmsg_show(MsnMessage *msg);
+/**
+ * Create a new SLP Ack message
+ *
+ * @param header the value of the header in this slpmsg.
+ *
+ * @return A new SlpMessage with ACK headers
+ */
+MsnSlpMessage *msn_slpmsg_ack_new(MsnP2PHeader *header);
+
+/**
+ * Create a new SLP message for MsnObject data.
+ *
+ * @param slpcall The slpcall that manages this message.
+ * @param img The image to be sent in this message.
+ *
+ * @return A new SlpMessage with MsnObject info.
+ */
+MsnSlpMessage *msn_slpmsg_obj_new(MsnSlpCall *slpcall, PurpleStoredImage *img);
+
+/**
+ * Create a new SLP message for data preparation.
+ *
+ * @param slpcall The slpcall that manages this message.
+ *
+ * @return A new SlpMessage with data preparation info.
+ */
+MsnSlpMessage *msn_slpmsg_dataprep_new(MsnSlpCall *slpcall);
+
+/**
+ * Create a new SLP message for File transfer.
+ *
+ * @param slpcall The slpcall that manages this message.
+ * @param size The size of the file being transsmited.
+ *
+ * @return A new SlpMessage with the file transfer info.
+ */
+MsnSlpMessage *msn_slpmsg_file_new(MsnSlpCall *slpcall, size_t size);
+
+/**
+ * Serialize the MsnSlpMessage in a way it can be used to be transmited
+ *
+ * @param slpmsg The MsnSlpMessage.
+ * @param ret_size The size of the buffer cointaining the message.
+ *
+ * @return a buffer with the serialized data.
+ */
+char *msn_slpmsg_serialize(MsnSlpMessage *slpmsg, size_t *ret_size);
+
+void msn_slpmsg_show_readable(MsnSlpMessage *slpmsg);
#endif /* _MSN_SLPMSG_H_ */
diff --git a/libpurple/protocols/msn/slpmsg_part.c b/libpurple/protocols/msn/slpmsg_part.c
new file mode 100644
index 0000000000..d89a878a67
--- /dev/null
+++ b/libpurple/protocols/msn/slpmsg_part.c
@@ -0,0 +1,210 @@
+#include "internal.h"
+#include "debug.h"
+
+#include "slpmsg.h"
+#include "slpmsg_part.h"
+
+MsnSlpMessagePart *msn_slpmsgpart_new(MsnP2PHeader *header, MsnP2PFooter *footer)
+{
+ MsnSlpMessagePart *part;
+
+ part = g_new0(MsnSlpMessagePart, 1);
+
+ if (header)
+ part->header = g_memdup(header, P2P_PACKET_HEADER_SIZE);
+ if (footer)
+ part->footer = g_memdup(footer, P2P_PACKET_FOOTER_SIZE);
+
+ part->ack_cb = msn_slpmsgpart_ack;
+ part->nak_cb = msn_slpmsgpart_nak;
+
+ return msn_slpmsgpart_ref(part);
+}
+
+MsnSlpMessagePart *msn_slpmsgpart_new_from_data(const char *data, size_t data_len)
+{
+ MsnSlpMessagePart *part;
+ MsnP2PHeader *header;
+ const char *tmp;
+ int body_len;
+
+ tmp = data;
+ part = msn_slpmsgpart_new(NULL, NULL);
+
+ if (data_len < sizeof(*header)) {
+ return NULL;
+ }
+
+ /* Extract the binary SLP header */
+ part->header = msn_p2p_header_from_wire((MsnP2PHeader*)tmp);
+ tmp += P2P_PACKET_HEADER_SIZE;
+
+ /* Extract the body */
+ body_len = data_len - P2P_PACKET_HEADER_SIZE - P2P_PACKET_FOOTER_SIZE;
+ /* msg->body_len = msg->msnslp_header.length; */
+
+ if (body_len > 0) {
+ part->size = body_len;
+ part->buffer = g_malloc(body_len);
+ memcpy(part->buffer, tmp, body_len);
+ tmp += body_len;
+ }
+
+ /* Extract the footer */
+ if (body_len >= 0)
+ part->footer = msn_p2p_footer_from_wire((MsnP2PFooter*)tmp);
+
+ return part;
+}
+
+void msn_slpmsgpart_destroy(MsnSlpMessagePart *part)
+{
+ if (!part)
+ return;
+
+ if (part->ref_count > 0) {
+ msn_slpmsgpart_unref(part);
+
+ return;
+ }
+
+ g_free(part->header);
+ g_free(part->footer);
+
+ g_free(part);
+
+}
+
+MsnSlpMessagePart *msn_slpmsgpart_ref(MsnSlpMessagePart *part)
+{
+ g_return_val_if_fail(part != NULL, NULL);
+ part->ref_count ++;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "part ref (%p)[%d]\n", part, part->ref_count);
+
+ return part;
+}
+
+MsnSlpMessagePart *msn_slpmsgpart_unref(MsnSlpMessagePart *part)
+{
+ g_return_val_if_fail(part != NULL, NULL);
+ g_return_val_if_fail(part->ref_count > 0, NULL);
+
+ part->ref_count--;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "part unref (%p)[%d]\n", part, part->ref_count);
+
+ if (part->ref_count == 0) {
+ msn_slpmsgpart_destroy(part);
+
+ return NULL;
+ }
+
+ return part;
+}
+
+void msn_slpmsgpart_set_bin_data(MsnSlpMessagePart *part, const void *data, size_t len)
+{
+ g_return_if_fail(part != NULL);
+
+ if (part->buffer != NULL)
+ g_free(part->buffer);
+
+ if (data != NULL && len > 0) {
+ part->buffer = g_malloc(len + 1);
+ memcpy(part->buffer, data, len);
+ part->buffer[len] = '\0';
+ part->size = len;
+ } else {
+ part->buffer = NULL;
+ part->size = 0;
+ }
+
+}
+
+char *msn_slpmsgpart_serialize(MsnSlpMessagePart *part, size_t *ret_size)
+{
+ MsnP2PHeader *header;
+ MsnP2PFooter *footer;
+ char *base;
+ char *tmp;
+ size_t siz;
+
+ base = g_malloc(P2P_PACKET_HEADER_SIZE + part->size + sizeof(MsnP2PFooter));
+ tmp = base;
+
+ header = msn_p2p_header_to_wire(part->header);
+ footer = msn_p2p_footer_to_wire(part->footer);
+
+ siz = sizeof(MsnP2PHeader);
+ /* Copy header */
+ memcpy(tmp, (char*)header, siz);
+ tmp += siz;
+
+ /* Copy body */
+ memcpy(tmp, part->buffer, part->size);
+ tmp += part->size;
+
+ /* Copy footer */
+ siz = sizeof(MsnP2PFooter);
+ memcpy(tmp, (char*)footer, siz);
+ tmp += siz;
+
+ *ret_size = tmp - base;
+
+ return base;
+}
+/* We have received the message ack */
+void
+msn_slpmsgpart_ack(MsnSlpMessagePart *part, void *data)
+{
+ MsnSlpMessage *slpmsg;
+ long long real_size;
+
+ slpmsg = data;
+
+ real_size = (slpmsg->header->flags == P2P_ACK) ? 0 : slpmsg->size;
+
+ slpmsg->header->offset += part->header->length;
+
+ slpmsg->parts = g_list_remove(slpmsg->parts, part);
+
+ if (slpmsg->header->offset < real_size)
+ {
+ if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED)
+ {
+ slpmsg->slpcall->xfer_msg = slpmsg;
+ purple_xfer_prpl_ready(slpmsg->slpcall->xfer);
+ }
+ else
+ msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
+ }
+ else
+ {
+ /* The whole message has been sent */
+ if (msn_p2p_msg_is_data(slpmsg->header->flags))
+ {
+ if (slpmsg->slpcall != NULL)
+ {
+ if (slpmsg->slpcall->cb)
+ slpmsg->slpcall->cb(slpmsg->slpcall,
+ NULL, 0);
+ }
+ }
+ }
+}
+
+/* We have received the message nak. */
+void
+msn_slpmsgpart_nak(MsnSlpMessagePart *part, void *data)
+{
+ MsnSlpMessage *slpmsg;
+
+ slpmsg = data;
+
+ msn_slplink_send_msgpart(slpmsg->slplink, slpmsg);
+
+ slpmsg->parts = g_list_remove(slpmsg->parts, part);
+}
diff --git a/libpurple/protocols/msn/slpmsg_part.h b/libpurple/protocols/msn/slpmsg_part.h
new file mode 100644
index 0000000000..8bdb428ea5
--- /dev/null
+++ b/libpurple/protocols/msn/slpmsg_part.h
@@ -0,0 +1,41 @@
+#ifndef MSN_SLPMSG_PART_H
+#define MSN_SLPMSG_PART_H
+
+#include "p2p.h"
+
+typedef struct _MsnSlpMessagePart MsnSlpMessagePart;
+typedef void (*MsnSlpPartCb)(MsnSlpMessagePart *part, void *data);
+
+struct _MsnSlpMessagePart
+{
+ int ref_count;
+
+ MsnP2PHeader *header;
+ MsnP2PFooter *footer;
+
+ MsnSlpPartCb ack_cb;
+ MsnSlpPartCb nak_cb;
+ void *ack_data;
+
+ guchar *buffer;
+ size_t size;
+};
+
+MsnSlpMessagePart *msn_slpmsgpart_new(MsnP2PHeader *header, MsnP2PFooter *footer);
+
+MsnSlpMessagePart *msn_slpmsgpart_new_from_data(const char *data, size_t data_len);
+
+void msn_slpmsgpart_destroy(MsnSlpMessagePart *part);
+
+MsnSlpMessagePart *msn_slpmsgpart_ref(MsnSlpMessagePart *part);
+
+MsnSlpMessagePart *msn_slpmsgpart_unref(MsnSlpMessagePart *part);
+
+void msn_slpmsgpart_set_bin_data(MsnSlpMessagePart *part, const void *data, size_t len);
+
+char *msn_slpmsgpart_serialize(MsnSlpMessagePart *part, size_t *ret_size);
+
+void msn_slpmsgpart_ack(MsnSlpMessagePart *part, void *data);
+
+void msn_slpmsgpart_nak(MsnSlpMessagePart *part, void *data);
+#endif /* MSN_SLPMSG_PART_H */
diff --git a/libpurple/protocols/msn/state.c b/libpurple/protocols/msn/state.c
index 536a36e33d..743627f898 100644
--- a/libpurple/protocols/msn/state.c
+++ b/libpurple/protocols/msn/state.c
@@ -23,10 +23,11 @@
*/
#include "internal.h"
+#include "debug.h"
#include "core.h"
-#include "msn.h"
+#include "notification.h"
#include "state.h"
static const char *away_text[] =
@@ -43,10 +44,6 @@ 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
*
@@ -56,7 +53,7 @@ static char *msn_build_psm(const char *psmstr,const char *mediastr,
* <CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia>"
*/
static char *
-msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr)
+msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr, guint protocol_ver)
{
xmlnode *dataNode,*psmNode,*mediaNode,*guidNode;
char *result;
@@ -82,60 +79,50 @@ msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr)
}
xmlnode_insert_child(dataNode, guidNode);
+ if (protocol_ver >= 16) {
+ /* TODO: What is this for? */
+ xmlnode *ddpNode = xmlnode_new("DDP");
+ xmlnode_insert_child(dataNode, ddpNode);
+ }
+
result = xmlnode_to_str(dataNode, &length);
xmlnode_free(dataNode);
return result;
}
-/* get the CurrentMedia info from the XML string */
+/* get the CurrentMedia info from the XML node */
char *
-msn_get_currentmedia(char *xml_str, gsize len)
+msn_get_currentmedia(xmlnode *payloadNode)
{
- xmlnode *payloadNode, *currentmediaNode;
+ xmlnode *currentmediaNode;
char *currentmedia;
purple_debug_info("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\n");
- xmlnode_free(payloadNode);
return NULL;
}
currentmedia = xmlnode_get_data(currentmediaNode);
- xmlnode_free(payloadNode);
-
return currentmedia;
}
-/*get the PSM info from the XML string*/
+/* Get the PSM info from the XML node */
char *
-msn_get_psm(char *xml_str, gsize len)
+msn_get_psm(xmlnode *payloadNode)
{
- xmlnode *payloadNode, *psmNode;
+ xmlnode *psmNode;
char *psm;
purple_debug_info("msn", "msn get PSM\n");
- payloadNode = xmlnode_from_str(xml_str, len);
- if (!payloadNode) {
- purple_debug_error("msn", "PSM XML parse Error!\n");
- return NULL;
- }
psmNode = xmlnode_get_child(payloadNode, "PSM");
if (psmNode == NULL) {
purple_debug_info("msn", "No PSM status Node\n");
- xmlnode_free(payloadNode);
return NULL;
}
psm = xmlnode_get_data(psmNode);
- xmlnode_free(payloadNode);
-
return psm;
}
@@ -175,14 +162,12 @@ create_media_string(PurplePresence *presence)
/* set the MSN's PSM info,Currently Read from the status Line
* Thanks for Cris Code
*/
-void
+static void
msn_set_psm(MsnSession *session)
{
PurpleAccount *account;
PurplePresence *presence;
PurpleStatus *status;
- MsnCmdProc *cmdproc;
- MsnTransaction *trans;
char *payload;
const char *statusline;
gchar *statusline_stripped, *media = NULL;
@@ -191,7 +176,6 @@ msn_set_psm(MsnSession *session)
g_return_if_fail(session->notification != NULL);
account = session->account;
- cmdproc = session->notification->cmdproc;
/* Get the PSM string from Purple's Status Line */
presence = purple_account_get_presence(account);
@@ -202,13 +186,11 @@ msn_set_psm(MsnSession *session)
statusline_stripped = purple_markup_strip_html(statusline);
media = create_media_string(presence);
g_free(session->psm);
- session->psm = msn_build_psm(statusline_stripped, media, NULL);
+ session->psm = msn_build_psm(statusline_stripped, media, session->protocol_ver >= 16 ? session->guid : NULL, session->protocol_ver);
payload = session->psm;
- purple_debug_misc("msn", "Sending UUX command with payload: %s\n", payload);
- trans = msn_transaction_new(cmdproc, "UUX", "%" G_GSIZE_FORMAT, strlen(payload));
- msn_transaction_set_payload(trans, payload, strlen(payload));
- msn_cmdproc_send_trans(cmdproc, trans);
+
+ msn_notification_send_uux(session, payload);
g_free(statusline_stripped);
g_free(media);
@@ -219,6 +201,7 @@ msn_change_status(MsnSession *session)
{
PurpleAccount *account;
MsnCmdProc *cmdproc;
+ MsnTransaction *trans;
MsnUser *user;
MsnObject *msnobj;
const char *state_text;
@@ -256,11 +239,16 @@ msn_change_status(MsnSession *session)
if (!session->logged_in)
return;
+ msn_set_psm(session);
+
msnobj = msn_user_get_object(user);
if (msnobj == NULL)
{
- msn_cmdproc_send(cmdproc, "CHG", "%s %u", state_text, caps);
+ if (session->protocol_ver >= 16)
+ trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u 0", state_text, caps, MSN_CLIENT_ID_EXT_CAPS);
+ else
+ trans = msn_transaction_new(cmdproc, "CHG", "%s %u", state_text, caps);
}
else
{
@@ -268,12 +256,17 @@ msn_change_status(MsnSession *session)
msnobj_str = msn_object_to_string(msnobj);
- msn_cmdproc_send(cmdproc, "CHG", "%s %u %s", state_text,
- caps, purple_url_encode(msnobj_str));
+ if (session->protocol_ver >= 16)
+ trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u %s", state_text,
+ caps, MSN_CLIENT_ID_EXT_CAPS, purple_url_encode(msnobj_str));
+ else
+ trans = msn_transaction_new(cmdproc, "CHG", "%s %u %s", state_text,
+ caps, purple_url_encode(msnobj_str));
g_free(msnobj_str);
}
- msn_set_psm(session);
+
+ msn_cmdproc_send_trans(cmdproc, trans);
}
const char *
diff --git a/libpurple/protocols/msn/state.h b/libpurple/protocols/msn/state.h
index 7e31afefea..eca877729c 100644
--- a/libpurple/protocols/msn/state.h
+++ b/libpurple/protocols/msn/state.h
@@ -58,13 +58,11 @@ const char *msn_away_get_text(MsnAwayType type);
const char *msn_state_get_text(MsnAwayType state);
-void msn_set_psm(MsnSession *session);
+/* Get the CurrentMedia info from the XML node */
+char *msn_get_currentmedia(xmlnode *payloadNode);
-/* 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);
+/* Get the PSM info from the XML node */
+char *msn_get_psm(xmlnode *payloadNode);
MsnAwayType msn_state_from_account(PurpleAccount *account);
diff --git a/libpurple/protocols/msn/switchboard.c b/libpurple/protocols/msn/switchboard.c
index 5f6e37b836..153b6a884c 100644
--- a/libpurple/protocols/msn/switchboard.c
+++ b/libpurple/protocols/msn/switchboard.c
@@ -21,18 +21,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
-#include "prefs.h"
-#include "switchboard.h"
-#include "notification.h"
-#include "msnutils.h"
-#include "error.h"
+#include "internal.h"
+#include "debug.h"
-static MsnTable *cbs_table;
+#include "msnutils.h"
+#include "switchboard.h"
+#include "sbconn.h"
+#include "slplink.h"
+#include "user.h"
+#include "userlist.h"
-static void msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg,
- MsnMsgErrorType error);
+static MsnTable *cbs_table;
/**************************************************************************
* Main
@@ -90,9 +90,11 @@ msn_switchboard_destroy(MsnSwitchBoard *swboard)
while (swboard->slplinks != NULL) {
MsnSlpLink *slplink = swboard->slplinks->data;
+ swboard->slplinks = g_list_remove(swboard->slplinks, slplink);
+
/* Destroy only those slplinks which use the switchboard */
if (slplink->dc == NULL)
- msn_slplink_destroy(slplink);
+ msn_slplink_unref(slplink);
else {
swboard->slplinks = g_list_remove(swboard->slplinks, slplink);
slplink->swboard = NULL;
@@ -123,7 +125,7 @@ msn_switchboard_destroy(MsnSwitchBoard *swboard)
g_free(swboard->session_id);
for (; swboard->users; swboard->users = g_list_delete_link(swboard->users, swboard->users))
- g_free(swboard->users->data);
+ msn_user_unref(swboard->users->data);
session = swboard->session;
session->switches = g_list_remove(session->switches, swboard);
@@ -231,6 +233,8 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
{
MsnCmdProc *cmdproc;
PurpleAccount *account;
+ MsnUserList *userlist;
+ MsnUser *msnuser;
char *semicolon;
char *passport;
@@ -246,13 +250,36 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
else
passport = g_strdup(user);
+ userlist = swboard->session->userlist;
+ msnuser = msn_userlist_find_user(userlist, passport);
+
/* Don't add multiple endpoints to the conversation. */
- if (g_list_find_custom(swboard->users, passport, (GCompareFunc)strcmp)) {
+ if (g_list_find_custom(swboard->users, passport, (GCompareFunc)msn_user_passport_cmp)) {
+ g_free(passport);
+ return;
+ }
+
+ /* Don't add ourselves either... */
+ if (g_str_equal(passport, purple_account_get_username(account))) {
g_free(passport);
return;
}
- swboard->users = g_list_prepend(swboard->users, passport);
+ /* Don't add ourselves either... */
+ if (g_str_equal(passport, purple_account_get_username(account))) {
+ g_free(passport);
+ return;
+ }
+
+ if (!msnuser) {
+ purple_debug_info("msn","User %s is not on our list.\n", passport);
+ msnuser = msn_user_new(userlist, passport, NULL);
+ } else
+ msn_user_ref(msnuser);
+
+ g_free(passport);
+
+ swboard->users = g_list_prepend(swboard->users, msnuser);
swboard->current_users++;
swboard->empty = FALSE;
@@ -270,11 +297,11 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
if ((swboard->conv != NULL) &&
(purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT))
{
- purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL,
+ purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), msnuser->passport, NULL,
PURPLE_CBFLAGS_NONE, TRUE);
msn_servconn_set_idle_timeout(swboard->servconn, 0);
}
- else if (swboard->current_users > 1 || swboard->total_users > 1)
+ else if (swboard->current_users > 1)
{
msn_servconn_set_idle_timeout(swboard->servconn, 0);
if (swboard->conv == NULL ||
@@ -298,8 +325,9 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
for (l = swboard->users; l != NULL; l = l->next)
{
const char *tmp_user;
+ user = l->data;
- tmp_user = l->data;
+ tmp_user = msnuser->passport;
purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv),
tmp_user, NULL, PURPLE_CBFLAGS_NONE, TRUE);
@@ -316,7 +344,7 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
else if (swboard->conv == NULL)
{
swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
- user, account);
+ msnuser->passport, account);
}
else
{
@@ -406,7 +434,7 @@ msg_resend_cb(gpointer data)
return FALSE;
}
-static void
+void
msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error)
{
MsnSwitchBoard *swboard;
@@ -514,7 +542,7 @@ msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error)
body_enc = g_markup_escape_text(body_str, -1);
g_free(body_str);
- format = msn_message_get_attr(msg, "X-MMS-IM-Format");
+ format = msn_message_get_header_value(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 : "");
@@ -543,17 +571,6 @@ msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error)
* Message Stuff
**************************************************************************/
-/** Called when a message times out. */
-static void
-msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans)
-{
- MsnMessage *msg;
-
- msg = trans->data;
-
- msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT);
-}
-
/** Called when we receive an error of a message. */
static void
msg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
@@ -588,96 +605,6 @@ msg_nak(MsnCmdProc *cmdproc, MsnCommand *cmd)
}
#endif
-static void
-release_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
-{
- MsnCmdProc *cmdproc;
- MsnTransaction *trans;
- char *payload;
- gsize payload_len;
- char flag;
-
- g_return_if_fail(swboard != NULL);
- g_return_if_fail(msg != NULL);
-
- cmdproc = swboard->cmdproc;
-
- payload = msn_message_gen_payload(msg, &payload_len);
-
- if (purple_debug_is_verbose()) {
- purple_debug_info("msn", "SB length:{%" G_GSIZE_FORMAT "}\n", payload_len);
- msn_message_show_readable(msg, "SB SEND", FALSE);
- }
-
- flag = msn_message_get_flag(msg);
- trans = msn_transaction_new(cmdproc, "MSG", "%c %" G_GSIZE_FORMAT,
- flag, payload_len);
-
- /* Data for callbacks */
- msn_transaction_set_data(trans, msg);
-
- if (flag != 'U') {
- if (msg->type == MSN_MSG_TEXT)
- {
- msg->ack_ref = TRUE;
- msn_message_ref(msg);
- swboard->ack_list = g_list_append(swboard->ack_list, msg);
- msn_transaction_set_timeout_cb(trans, msg_timeout);
- }
- else if (msg->type == MSN_MSG_SLP)
- {
- msg->ack_ref = TRUE;
- msn_message_ref(msg);
- swboard->ack_list = g_list_append(swboard->ack_list, msg);
- msn_transaction_set_timeout_cb(trans, msg_timeout);
-#if 0
- if (msg->ack_cb != NULL)
- {
- msn_transaction_add_cb(trans, "ACK", msg_ack);
- msn_transaction_add_cb(trans, "NAK", msg_nak);
- }
-#endif
- }
- }
-
- trans->payload = payload;
- trans->payload_len = payload_len;
-
- msg->trans = trans;
-
- msn_cmdproc_send_trans(cmdproc, trans);
-}
-
-static void
-queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
-{
- g_return_if_fail(swboard != NULL);
- g_return_if_fail(msg != NULL);
-
- purple_debug_info("msn", "Appending message to queue.\n");
-
- g_queue_push_tail(swboard->msg_queue, msg);
-
- msn_message_ref(msg);
-}
-
-static void
-process_queue(MsnSwitchBoard *swboard)
-{
- MsnMessage *msg;
-
- g_return_if_fail(swboard != NULL);
-
- purple_debug_info("msn", "Processing queue\n");
-
- while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
- {
- purple_debug_info("msn", "Sending message\n");
- release_msg(swboard, msg);
- msn_message_unref(msg);
- }
-}
-
gboolean
msn_switchboard_can_send(MsnSwitchBoard *swboard)
{
@@ -689,20 +616,6 @@ msn_switchboard_can_send(MsnSwitchBoard *swboard)
return TRUE;
}
-void
-msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
- gboolean queue)
-{
- g_return_if_fail(swboard != NULL);
- g_return_if_fail(msg != NULL);
-
- purple_debug_info("msn", "switchboard send msg..\n");
- if (msn_switchboard_can_send(swboard))
- release_msg(swboard, msg);
- else if (queue)
- queue_msg(swboard, msg);
-}
-
/**************************************************************************
* Switchboard Commands
**************************************************************************/
@@ -745,7 +658,10 @@ bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
purple_conv_chat_remove_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL);
passport = g_list_find_custom(swboard->users, user, (GCompareFunc)strcmp);
- g_free(passport->data);
+ if (passport)
+ g_free(passport->data);
+ else
+ purple_debug_warning("msn", "Can't find user %s in the switchboard\n", user);
swboard->users = g_list_delete_link(swboard->users, passport);
swboard->current_users--;
if (swboard->current_users == 0)
@@ -761,8 +677,12 @@ bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
static void
iro_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
+ PurpleAccount *account;
+ PurpleConnection *gc;
MsnSwitchBoard *swboard;
+ account = cmdproc->session->account;
+ gc = account->gc;
swboard = cmdproc->data;
swboard->total_users = atoi(cmd->params[2]);
@@ -774,17 +694,21 @@ static void
joi_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
+ PurpleAccount *account;
+ PurpleConnection *gc;
MsnSwitchBoard *swboard;
const char *passport;
passport = cmd->params[0];
session = cmdproc->session;
+ account = session->account;
+ gc = account->gc;
swboard = cmdproc->data;
msn_switchboard_add_user(swboard, passport);
- process_queue(swboard);
+ msn_sbconn_process_queue(swboard);
if (!session->http_method)
send_clientcaps(swboard);
@@ -847,8 +771,8 @@ ack_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
msg = cmd->trans->data;
- if (msg->ack_cb != NULL)
- msg->ack_cb(msg, msg->ack_data);
+ if (msg->part && msg->part->ack_cb != NULL)
+ msg->part->ack_cb(msg->part, msg->part->ack_data);
swboard = cmdproc->data;
if (swboard)
@@ -970,6 +894,7 @@ connect_cb(MsnServConn *servconn)
MsnTransaction *trans;
MsnCmdProc *cmdproc;
PurpleAccount *account;
+ char *username;
cmdproc = servconn->cmdproc;
g_return_if_fail(cmdproc != NULL);
@@ -978,24 +903,33 @@ connect_cb(MsnServConn *servconn)
swboard = cmdproc->data;
g_return_if_fail(swboard != NULL);
+ if (servconn->session->protocol_ver >= 16)
+ username = g_strdup_printf("%s;{%s}",
+ purple_account_get_username(account),
+ servconn->session->guid);
+ else
+ username = g_strdup(purple_account_get_username(account));
+
if (msn_switchboard_is_invited(swboard))
{
swboard->empty = FALSE;
trans = msn_transaction_new(cmdproc, "ANS", "%s %s %s",
- purple_account_get_username(account),
+ username,
swboard->auth_key, swboard->session_id);
}
else
{
trans = msn_transaction_new(cmdproc, "USR", "%s %s",
- purple_account_get_username(account),
+ username,
swboard->auth_key);
}
msn_transaction_set_error_cb(trans, ans_usr_error);
msn_transaction_set_data(trans, swboard);
msn_cmdproc_send_trans(cmdproc, trans);
+
+ g_free(username);
}
static void
@@ -1213,8 +1147,11 @@ msn_switchboard_close(MsnSwitchBoard *swboard)
!swboard->session->connected)
{
MsnCmdProc *cmdproc;
+ MsnTransaction *trans;
cmdproc = swboard->cmdproc;
- msn_cmdproc_send_quick(cmdproc, "OUT", NULL, NULL);
+ trans = msn_transaction_new(cmdproc, "OUT", NULL);
+ msn_transaction_set_saveable(trans, FALSE);
+ msn_cmdproc_send_trans(cmdproc, trans);
msn_switchboard_destroy(swboard);
}
diff --git a/libpurple/protocols/msn/switchboard.h b/libpurple/protocols/msn/switchboard.h
index 1a6c958914..213fe58f82 100644
--- a/libpurple/protocols/msn/switchboard.h
+++ b/libpurple/protocols/msn/switchboard.h
@@ -50,12 +50,10 @@ typedef enum
MSN_SB_FLAG_FT = 0x02 /**< This switchboard is being used for file transfer. */
} MsnSBFlag;
-#include "conversation.h"
-
+#include "cmdproc.h"
#include "msg.h"
#include "servconn.h"
-#include "slplink.h"
-#include "user.h"
+#include "session.h"
/**
* A switchboard.
@@ -246,6 +244,9 @@ gboolean msn_switchboard_can_send(MsnSwitchBoard *swboard);
void msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
gboolean queue);
+void
+msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error);
+
gboolean msn_switchboard_chat_leave(MsnSwitchBoard *swboard);
gboolean msn_switchboard_chat_invite(MsnSwitchBoard *swboard, const char *who);
@@ -253,30 +254,6 @@ void msn_switchboard_request(MsnSwitchBoard *swboard);
void msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user);
/**
- * Processes peer to peer messages.
- *
- * @param cmdproc The command processor.
- * @param msg The message.
- */
-void msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
-
-/**
- * Processes emoticon messages.
- *
- * @param cmdproc The command processor.
- * @param msg The message.
- */
-void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
-
-/**
- * Processes INVITE messages.
- *
- * @param cmdproc The command processor.
- * @param msg The message.
- */
-void msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg);
-
-/**
* Shows an ink message from this switchboard.
*
* @param swboard The switchboard.
diff --git a/libpurple/protocols/msn/sync.c b/libpurple/protocols/msn/sync.c
deleted file mode 100644
index 0fa21e734a..0000000000
--- a/libpurple/protocols/msn/sync.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/**
- * @file sync.c MSN list synchronization functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-#include "msn.h"
-#include "sync.h"
-#include "state.h"
-
-static MsnTable *cbs_table;
-
-static void
-blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
- PurpleConnection *gc = cmdproc->session->account->gc;
- const char *list_name;
-
- list_name = cmd->params[0];
-
- if (!g_ascii_strcasecmp(list_name, "AL"))
- {
- /*
- * If the current setting is AL, messages from users who
- * are not in BL will be delivered.
- *
- * In other words, deny some.
- */
- gc->account->perm_deny = PURPLE_PRIVACY_DENY_USERS;
- }
- else
- {
- /* If the current setting is BL, only messages from people
- * who are in the AL will be delivered.
- *
- * In other words, permit some.
- */
- gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS;
- }
-}
-
-static void
-prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
- MsnSession *session = cmdproc->session;
- const char *type, *value;
-
- type = cmd->params[0];
- value = cmd->params[1];
-
- if (cmd->param_count == 2)
- {
- if (!strcmp(type, "PHH"))
- msn_user_set_home_phone(session->user, purple_url_decode(value));
- else if (!strcmp(type, "PHW"))
- msn_user_set_work_phone(session->user, purple_url_decode(value));
- else if (!strcmp(type, "PHM"))
- msn_user_set_mobile_phone(session->user, purple_url_decode(value));
- }
- else
- {
- if (!strcmp(type, "PHH"))
- msn_user_set_home_phone(session->user, NULL);
- else if (!strcmp(type, "PHW"))
- msn_user_set_work_phone(session->user, NULL);
- else if (!strcmp(type, "PHM"))
- msn_user_set_mobile_phone(session->user, NULL);
- }
-}
-
-static void
-lsg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
- MsnSession *session = cmdproc->session;
- const char *name;
- const char *group_id;
-
- group_id = cmd->params[0];
- name = purple_url_decode(cmd->params[1]);
-
- msn_group_new(session->userlist, group_id, name);
-
- /* HACK */
- if (group_id == 0)
- {
- /* Group of ungroupped buddies */
- if (session->sync->total_users == 0)
- {
- cmdproc->cbs_table = session->sync->old_cbs_table;
-
- msn_session_finish_login(session);
-
- msn_sync_destroy(session->sync);
- session->sync = NULL;
- }
- return;
- }
-
- if ((purple_find_group(name)) == NULL)
- {
- PurpleGroup *g = purple_group_new(name);
- purple_blist_add_group(g, NULL);
- }
-}
-
-static void
-lst_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
- MsnSession *session = cmdproc->session;
- char *passport = NULL;
- const char *friend = NULL;
- int list_op;
- MsnUser *user;
-
- passport = cmd->params[0];
- friend = purple_url_decode(cmd->params[1]);
- list_op = atoi(cmd->params[2]);
-
- user = msn_user_new(session->userlist, passport, friend);
-
- msn_userlist_add_user(session->userlist, user);
-
- session->sync->last_user = user;
-
- /* TODO: This can be improved */
-
- if (list_op & MSN_LIST_FL_OP)
- {
- char **c;
- char **tokens;
- const char *group_nums;
- GSList *group_ids;
-
- group_nums = cmd->params[3];
-
- group_ids = NULL;
-
- tokens = g_strsplit(group_nums, ",", -1);
-
- for (c = tokens; *c != NULL; c++)
- {
- group_ids = g_slist_append(group_ids, *c);
- }
-
-
- msn_got_lst_user(session, user, list_op, group_ids);
-
- g_strfreev(tokens);
- g_slist_free(group_ids);
- }
- else
- {
- msn_got_lst_user(session, user, list_op, NULL);
- }
-
- session->sync->num_users++;
-
- if (session->sync->num_users == session->sync->total_users)
- {
- cmdproc->cbs_table = session->sync->old_cbs_table;
-
- msn_session_finish_login(session);
-
- msn_sync_destroy(session->sync);
- session->sync = NULL;
- }
-}
-
-static void
-bpr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
-{
- MsnSync *sync = cmdproc->session->sync;
- const char *type, *value;
- MsnUser *user;
-
- user = sync->last_user;
-
- g_return_if_fail(user != NULL);
-
- type = cmd->params[0];
- value = cmd->params[1];
-
- if (value)
- {
- if (!strcmp(type, "MOB"))
- {
- if (!strcmp(value, "Y"))
- user->mobile = TRUE;
- }
- else if (!strcmp(type, "PHH"))
- msn_user_set_home_phone(user, purple_url_decode(value));
- else if (!strcmp(type, "PHW"))
- msn_user_set_work_phone(user, purple_url_decode(value));
- else if (!strcmp(type, "PHM"))
- msn_user_set_mobile_phone(user, purple_url_decode(value));
- }
-}
-
-void
-msn_sync_init(void)
-{
- cbs_table = msn_table_new();
-
- /* Syncing */
- msn_table_add_cmd(cbs_table, NULL, "GTC", NULL);
- msn_table_add_cmd(cbs_table, NULL, "BLP", blp_cmd);
- msn_table_add_cmd(cbs_table, NULL, "PRP", prp_cmd);
- msn_table_add_cmd(cbs_table, NULL, "LSG", lsg_cmd);
- msn_table_add_cmd(cbs_table, NULL, "LST", lst_cmd);
- msn_table_add_cmd(cbs_table, NULL, "BPR", bpr_cmd);
-}
-
-void
-msn_sync_end(void)
-{
- msn_table_destroy(cbs_table);
-}
-
-MsnSync *
-msn_sync_new(MsnSession *session)
-{
- MsnSync *sync;
-
- sync = g_new0(MsnSync, 1);
-
- sync->session = session;
- sync->cbs_table = cbs_table;
-
- return sync;
-}
-
-void
-msn_sync_destroy(MsnSync *sync)
-{
- g_free(sync);
-}
diff --git a/libpurple/protocols/msn/sync.h b/libpurple/protocols/msn/sync.h
deleted file mode 100644
index 12ca1cc963..0000000000
--- a/libpurple/protocols/msn/sync.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * @file sync.h MSN list synchronization functions
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-#ifndef MSN_SYNC_H
-#define MSN_SYNC_H
-
-typedef struct _MsnSync MsnSync;
-
-#include "session.h"
-#include "table.h"
-#include "user.h"
-
-struct _MsnSync
-{
- MsnSession *session;
- MsnTable *cbs_table;
-
- /*
- * TODO: What is the intended purpose of old_cbs_table? Nothing
- * sets it and it is only read in two places.
- */
- MsnTable *old_cbs_table;
-
- int num_users;
- int total_users;
- int num_groups;
- int total_groups;
- MsnUser *last_user;
-};
-
-void msn_sync_init(void);
-void msn_sync_end(void);
-
-MsnSync *msn_sync_new(MsnSession *session);
-void msn_sync_destroy(MsnSync *sync);
-
-#endif /* MSN_SYNC_H */
diff --git a/libpurple/protocols/msn/table.h b/libpurple/protocols/msn/table.h
index f6a7b64478..83d9954d13 100644
--- a/libpurple/protocols/msn/table.h
+++ b/libpurple/protocols/msn/table.h
@@ -34,20 +34,60 @@ typedef void (*MsnMsgTypeCb)(MsnCmdProc *cmdproc, MsnMessage *msg);
struct _MsnTable
{
- GHashTable *cmds;
- GHashTable *msgs;
- GHashTable *errors;
+ GHashTable *cmds; /**< Callbacks that manage command response. */
+ GHashTable *msgs; /**< Callbacks that manage incoming messages. */
+ GHashTable *errors; /**< Callbacks that manage command errors. */
- GHashTable *async;
- GHashTable *fallback;
+ GHashTable *async; /**< Callbacks that manage incoming asyncronous messages. */
+ /* TODO: Does this one is really needed? */
+ GHashTable *fallback; /**< Fallback callback. */
};
+/**
+ * Create a new instance of a MsnTable which map commands, errors and messages
+ * with callbacks that will handle it.
+ *
+ * @return A new MsnTable.
+ */
MsnTable *msn_table_new(void);
+
+/**
+ * Destroy a MsnTable.
+ *
+ * @param table The MsnTable to be destroyed.
+ */
void msn_table_destroy(MsnTable *table);
+/**
+ * Relate an incomming command from server with a callback able to handle
+ * the event.
+ *
+ * @param table The MsnTable.
+ * @param command If NULL this add an incoming asyncronous command set in answer.
+ * Else, the command sent.
+ * @param answer The server answer to 'command'. If 'command' is NULL,
+ * the asyncronous command sent by the server.
+ * @param cb Callback to handle this event.
+ */
void msn_table_add_cmd(MsnTable *table, char *command, char *answer,
MsnTransCb cb);
+
+/**
+ * Set a callback to handle incoming command errors.
+ *
+ * @param table The MsnTable.
+ * @param answer Incoming command with error.
+ * @param cb Callback to handle this error.
+ */
void msn_table_add_error(MsnTable *table, char *answer, MsnErrorCb cb);
+
+/**
+ * Relate a message Content-type with a callback able to handle it.
+ *
+ * @param table The MsnTable.
+ * @param type The Message Content-Type.
+ * @param cb Callback to handle this Content-type.
+ */
void msn_table_add_msg_type(MsnTable *table, char *type, MsnMsgTypeCb cb);
#endif /* MSN_TABLE_H */
diff --git a/libpurple/protocols/msn/transaction.c b/libpurple/protocols/msn/transaction.c
index 936f665696..73ffcd2cdc 100644
--- a/libpurple/protocols/msn/transaction.c
+++ b/libpurple/protocols/msn/transaction.c
@@ -21,6 +21,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
+#include "internal.h"
+#include "debug.h"
+
#include "msn.h"
#include "transaction.h"
@@ -37,6 +41,7 @@ msn_transaction_new(MsnCmdProc *cmdproc, const char *command,
trans->cmdproc = cmdproc;
trans->command = g_strdup(command);
+ trans->saveable = TRUE;
if (format != NULL)
{
@@ -96,8 +101,10 @@ msn_transaction_to_string(MsnTransaction *trans)
if (trans->params != NULL)
str = g_strdup_printf("%s %u %s\r\n", trans->command, trans->trId, trans->params);
- else
+ else if (trans->saveable)
str = g_strdup_printf("%s %u\r\n", trans->command, trans->trId);
+ else
+ str = g_strdup_printf("%s\r\n", trans->command);
return str;
}
@@ -175,6 +182,14 @@ void msn_transaction_set_data_free(MsnTransaction *trans, GDestroyNotify fn)
}
void
+msn_transaction_set_saveable(MsnTransaction *trans, gboolean saveable)
+{
+ g_return_if_fail(trans != NULL);
+
+ trans->saveable = saveable;
+}
+
+void
msn_transaction_add_cb(MsnTransaction *trans, char *answer,
MsnTransCb cb)
{
diff --git a/libpurple/protocols/msn/transaction.h b/libpurple/protocols/msn/transaction.h
index 2e977fa3f8..7d4764145e 100644
--- a/libpurple/protocols/msn/transaction.h
+++ b/libpurple/protocols/msn/transaction.h
@@ -24,6 +24,8 @@
#ifndef MSN_TRANSACTION_H
#define MSN_TRANSACTION_H
+#include "internal.h"
+
typedef struct _MsnTransaction MsnTransaction;
#include "cmdproc.h"
@@ -40,7 +42,9 @@ typedef void (*MsnErrorCb)(MsnCmdProc *cmdproc, MsnTransaction *trans,
struct _MsnTransaction
{
MsnCmdProc *cmdproc;
- unsigned int trId;
+
+ gboolean saveable; /**< Whether to save this transaction in the history */
+ unsigned int trId; /**< The ID of this transaction, if it's being saved */
char *command;
char *params;
@@ -74,6 +78,7 @@ void msn_transaction_set_payload(MsnTransaction *trans,
const char *payload, int payload_len);
void msn_transaction_set_data(MsnTransaction *trans, void *data);
void msn_transaction_set_data_free(MsnTransaction *trans, GDestroyNotify fn);
+void msn_transaction_set_saveable(MsnTransaction *trans, gboolean saveable);
void msn_transaction_add_cb(MsnTransaction *trans, char *answer,
MsnTransCb cb);
void msn_transaction_set_error_cb(MsnTransaction *trans, MsnErrorCb cb);
diff --git a/libpurple/protocols/msn/user.c b/libpurple/protocols/msn/user.c
index 8e6ba9fe43..2898d4bc43 100644
--- a/libpurple/protocols/msn/user.c
+++ b/libpurple/protocols/msn/user.c
@@ -21,10 +21,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "msn.h"
+
+#include "internal.h"
+#include "debug.h"
+#include "util.h"
+
#include "user.h"
#include "slp.h"
+static void free_user_endpoint(MsnUserEndpoint *data)
+{
+ g_free(data->id);
+ g_free(data->name);
+ g_free(data);
+}
+
/*new a user object*/
MsnUser *
msn_user_new(MsnUserList *userlist, const char *passport,
@@ -39,15 +50,25 @@ msn_user_new(MsnUserList *userlist, const char *passport,
msn_user_set_passport(user, passport);
msn_user_set_friendly_name(user, friendly_name);
- return user;
+ return msn_user_ref(user);
}
/*destroy a user object*/
-void
+static void
msn_user_destroy(MsnUser *user)
{
g_return_if_fail(user != NULL);
+ if (user->refcount > 1) {
+ msn_user_unref(user);
+ return;
+ }
+
+ while (user->endpoints != NULL) {
+ free_user_endpoint(user->endpoints->data);
+ user->endpoints = g_slist_delete_link(user->endpoints, user->endpoints);
+ }
+
if (user->clientcaps != NULL)
g_hash_table_destroy(user->clientcaps);
@@ -82,6 +103,27 @@ msn_user_destroy(MsnUser *user)
g_free(user);
}
+MsnUser *
+msn_user_ref(MsnUser *user)
+{
+ g_return_val_if_fail(user != NULL, NULL);
+
+ user->refcount++;
+
+ return user;
+}
+
+void
+msn_user_unref(MsnUser *user)
+{
+ g_return_if_fail(user != NULL);
+
+ user->refcount--;
+
+ if(user->refcount == 0)
+ msn_user_destroy(user);
+}
+
void
msn_user_update(MsnUser *user)
{
@@ -186,6 +228,9 @@ msn_user_set_friendly_name(MsnUser *user, const char *name)
{
g_return_val_if_fail(user != NULL, FALSE);
+ if (user == user->userlist->session->user)
+ return FALSE;
+
if (user->friendly_name && name && (!strcmp(user->friendly_name, name) ||
!strcmp(user->passport, name)))
return FALSE;
@@ -217,6 +262,47 @@ msn_user_set_uid(MsnUser *user, const char *uid)
}
void
+msn_user_set_endpoint_data(MsnUser *user, const char *input, MsnUserEndpoint *newep)
+{
+ MsnUserEndpoint *ep;
+ char *endpoint;
+ GSList *l;
+
+ g_return_if_fail(user != NULL);
+ g_return_if_fail(input != NULL);
+
+ endpoint = g_ascii_strdown(input, -1);
+
+ for (l = user->endpoints; l; l = l->next) {
+ ep = l->data;
+ if (g_str_equal(ep->id, endpoint)) {
+ /* We have info about this endpoint! */
+
+ g_free(endpoint);
+
+ if (newep == NULL) {
+ /* Delete it and exit */
+ user->endpoints = g_slist_delete_link(user->endpoints, l);
+ free_user_endpoint(ep);
+ return;
+ }
+
+ /* Break out of our loop and update it */
+ break;
+ }
+ }
+ if (l == NULL) {
+ /* Need to add a new endpoint */
+ ep = g_new0(MsnUserEndpoint, 1);
+ ep->id = endpoint;
+ user->endpoints = g_slist_prepend(user->endpoints, ep);
+ }
+
+ ep->clientid = newep->clientid;
+ ep->extcaps = newep->extcaps;
+}
+
+void
msn_user_set_op(MsnUser *user, MsnListOp list_op)
{
g_return_if_fail(user != NULL);
@@ -406,6 +492,14 @@ msn_user_set_clientid(MsnUser *user, guint clientid)
}
void
+msn_user_set_extcaps(MsnUser *user, guint extcaps)
+{
+ g_return_if_fail(user != NULL);
+
+ user->extcaps = extcaps;
+}
+
+void
msn_user_set_network(MsnUser *user, MsnNetwork network)
{
g_return_if_fail(user != NULL);
@@ -413,18 +507,83 @@ msn_user_set_network(MsnUser *user, MsnNetwork network)
user->networkid = network;
}
+static gboolean
+buddy_icon_cached(PurpleConnection *gc, MsnObject *obj)
+{
+ PurpleAccount *account;
+ PurpleBuddy *buddy;
+ const char *old;
+ const char *new;
+
+ g_return_val_if_fail(obj != NULL, FALSE);
+
+ account = purple_connection_get_account(gc);
+
+ buddy = purple_find_buddy(account, msn_object_get_creator(obj));
+ if (buddy == NULL)
+ return FALSE;
+
+ old = purple_buddy_icons_get_checksum_for_user(buddy);
+ new = msn_object_get_sha1(obj);
+
+ if (new == NULL)
+ return FALSE;
+
+ /* If the old and new checksums are the same, and the file actually exists,
+ * then return TRUE */
+ if (old != NULL && !strcmp(old, new))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+queue_buddy_icon_request(MsnUser *user)
+{
+ PurpleAccount *account;
+ MsnObject *obj;
+ GQueue *queue;
+
+ g_return_if_fail(user != NULL);
+
+ account = user->userlist->session->account;
+
+ obj = msn_user_get_object(user);
+
+ if (obj == NULL) {
+ purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL);
+ return;
+ }
+
+ if (!buddy_icon_cached(account->gc, obj)) {
+ MsnUserList *userlist;
+
+ userlist = user->userlist;
+ queue = userlist->buddy_icon_requests;
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n",
+ user->passport, userlist->buddy_icon_window);
+
+ g_queue_push_tail(queue, user);
+
+ if (userlist->buddy_icon_window > 0)
+ msn_release_buddy_icon_request(userlist);
+ }
+}
+
void
msn_user_set_object(MsnUser *user, MsnObject *obj)
{
g_return_if_fail(user != NULL);
- if (user->msnobj != NULL)
+ if (user->msnobj != NULL && !msn_object_find_local(msn_object_get_sha1(obj)))
msn_object_destroy(user->msnobj);
user->msnobj = obj;
- if (user->list_op & MSN_LIST_FL_OP)
- msn_queue_buddy_icon_request(user);
+ if (user != user->userlist->session->user && user->list_op & MSN_LIST_FL_OP)
+ queue_buddy_icon_request(user);
}
void
@@ -496,6 +655,39 @@ msn_user_get_clientid(const MsnUser *user)
return user->clientid;
}
+guint
+msn_user_get_extcaps(const MsnUser *user)
+{
+ g_return_val_if_fail(user != NULL, 0);
+
+ return user->extcaps;
+}
+
+MsnUserEndpoint *
+msn_user_get_endpoint_data(MsnUser *user, const char *input)
+{
+ char *endpoint;
+ GSList *l;
+ MsnUserEndpoint *ep;
+
+ g_return_val_if_fail(user != NULL, NULL);
+ g_return_val_if_fail(input != NULL, NULL);
+
+ endpoint = g_ascii_strdown(input, -1);
+
+ for (l = user->endpoints; l; l = l->next) {
+ ep = l->data;
+ if (g_str_equal(ep->id, endpoint)) {
+ g_free(endpoint);
+ return ep;
+ }
+ }
+
+ g_free(endpoint);
+
+ return NULL;
+}
+
MsnObject *
msn_user_get_object(const MsnUser *user)
{
@@ -520,3 +712,60 @@ msn_user_get_invite_message(const MsnUser *user)
return user->invite_message;
}
+gboolean
+msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extcap)
+{
+ g_return_val_if_fail(user != NULL, FALSE);
+
+ if (endpoint != NULL) {
+ MsnUserEndpoint *ep = msn_user_get_endpoint_data(user, endpoint);
+ if (ep != NULL)
+ return (ep->clientid & capability) && (ep->extcaps & extcap);
+ else
+ return FALSE;
+ }
+
+ return (user->clientid & capability) && (user->extcaps & extcap);
+}
+
+/**************************************************************************
+ * Utility functions
+ **************************************************************************/
+
+int
+msn_user_passport_cmp(MsnUser *user, const char *passport)
+{
+ const char *str;
+ char *pass;
+ int result;
+
+ str = purple_normalize_nocase(NULL, msn_user_get_passport(user));
+ pass = g_strdup(str);
+
+ result = g_strcmp0(pass, purple_normalize_nocase(NULL, passport));
+ g_free(pass);
+
+ return result;
+}
+
+gboolean
+msn_user_is_in_group(MsnUser *user, const char * group_id)
+{
+ if (user == NULL)
+ return FALSE;
+
+ if (group_id == NULL)
+ return FALSE;
+
+ return (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp)) != NULL;
+}
+
+gboolean
+msn_user_is_in_list(MsnUser *user, MsnListId list_id)
+{
+ if (user == NULL)
+ return FALSE;
+
+ return (user->list_op & (1 << list_id));
+}
+
diff --git a/libpurple/protocols/msn/user.h b/libpurple/protocols/msn/user.h
index 49fa34ec1e..02cd297c25 100644
--- a/libpurple/protocols/msn/user.h
+++ b/libpurple/protocols/msn/user.h
@@ -79,10 +79,13 @@ struct _MsnUser
{
MsnUserList *userlist;
+ guint8 refcount; /**< The reference count of this object */
+
char *passport; /**< The passport account. */
char *friendly_name; /**< The friendly name. */
char *uid; /*< User ID */
+ GSList *endpoints; /*< Endpoint-specific data */
const char *status; /**< The state of the user. */
char *statusline; /**< The state of the user. */
@@ -102,6 +105,7 @@ struct _MsnUser
GHashTable *clientcaps; /**< The client's capabilities. */
guint clientid; /**< The client's ID */
+ guint extcaps; /**< The client's extended capabilities */
MsnNetwork networkid; /**< The user's network */
@@ -116,6 +120,18 @@ struct _MsnUser
char *invite_message; /**< Invite message of user request */
};
+/**
+ * A specific user endpoint.
+ */
+typedef struct MsnUserEndpoint {
+ char *id; /**< The client's endpoint ID */
+ char *name; /**< The client's endpoint's name */
+ int type; /**< The client's endpoint type */
+ guint clientid; /**< The client's ID */
+ guint extcaps; /**< The client's extended capabilites */
+
+} MsnUserEndpoint;
+
/**************************************************************************
** @name User API *
**************************************************************************/
@@ -134,12 +150,20 @@ MsnUser *msn_user_new(MsnUserList *userlist, const char *passport,
const char *friendly_name);
/**
- * Destroys a user structure.
+ * Increment the reference count.
+ *
+ * @param user The user.
*
- * @param user The user to destroy.
+ * @return user.
*/
-void msn_user_destroy(MsnUser *user);
+MsnUser *msn_user_ref(MsnUser *user);
+/**
+ * Decrement the reference count.
+ *
+ * @param user The user
+ */
+void msn_user_unref(MsnUser *user);
/**
* Updates the user.
@@ -252,6 +276,16 @@ void msn_user_set_work_phone(MsnUser *user, const char *number);
void msn_user_set_uid(MsnUser *user, const char *uid);
/**
+ * Sets endpoint data for a user.
+ *
+ * @param user The user.
+ * @param endpoint The endpoint.
+ * @param data The endpoint data.
+ */
+void
+msn_user_set_endpoint_data(MsnUser *user, const char *endpoint, MsnUserEndpoint *data);
+
+/**
* Sets the client id for a user.
*
* @param user The user.
@@ -260,6 +294,14 @@ void msn_user_set_uid(MsnUser *user, const char *uid);
void msn_user_set_clientid(MsnUser *user, guint clientid);
/**
+ * Sets the client id for a user.
+ *
+ * @param user The user.
+ * @param extcaps The client's extended capabilities.
+ */
+void msn_user_set_extcaps(MsnUser *user, guint extcaps);
+
+/**
* Sets the network id for a user.
*
* @param user The user.
@@ -346,6 +388,17 @@ const char *msn_user_get_work_phone(const MsnUser *user);
const char *msn_user_get_mobile_phone(const MsnUser *user);
/**
+ * Gets endpoint data for a user.
+ *
+ * @param user The user.
+ * @param endpoint The endpoint.
+ *
+ * @return The user's endpoint data.
+ */
+MsnUserEndpoint *
+msn_user_get_endpoint_data(MsnUser *user, const char *endpoint);
+
+/**
* Returns the client id for a user.
*
* @param user The user.
@@ -355,6 +408,39 @@ const char *msn_user_get_mobile_phone(const MsnUser *user);
guint msn_user_get_clientid(const MsnUser *user);
/**
+ * Returns the extended capabilities for a user.
+ *
+ * @param user The user.
+ *
+ * @return The user's extended capabilities.
+ */
+guint msn_user_get_extcaps(const MsnUser *user);
+
+/**************************************************************************
+ * Utility functions
+ **************************************************************************/
+
+
+/**
+ * Check if the user is part of the group.
+ *
+ * @param user The user we are asking group membership.
+ * @param group_id The group where the user may be in.
+ *
+ * @return TRUE if user is part of the group. Otherwise, FALSE.
+ */
+gboolean msn_user_is_in_group(MsnUser *user, const char * group_id);
+
+/**
+ * Check if user is on list.
+ *
+ * @param user The user we are asking list membership.
+ * @param list_id The list where the user may be in.
+ *
+ * @return TRUE if the user is on the list, else FALSE.
+ */
+gboolean msn_user_is_in_list(MsnUser *user, MsnListId list_id);
+/**
* Returns the network id for a user.
*
* @param user The user.
@@ -398,11 +484,36 @@ 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);
+gboolean msn_user_is_yahoo(PurpleAccount *account, const char *name);
void msn_user_set_op(MsnUser *user, MsnListOp list_op);
void msn_user_unset_op(MsnUser *user, MsnListOp list_op);
+/**
+ * Compare the given passport with the one of the user
+ *
+ * @param user User to compare.
+ * @oaran passport Passport to compare.
+ *
+ * @return Zero if the passport match with the one of the user, otherwise
+ * a positive integer if the user passport is greather than the one given
+ * and a negative integer if it is less.
+ */
+int msn_user_passport_cmp(MsnUser *user, const char *passport);
+
+/**
+ * Checks whether a user is capable of some task.
+ *
+ * @param user The user.
+ * @param endpoint The endpoint. Can be @NULL to check overall capabilities.
+ * @param capability The capability (including client version).
+ * @param extcap The extended capability.
+ *
+ * @return Whether the user supports the capability.
+ */
+gboolean
+msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extcap);
+
/*@}*/
#endif /* MSN_USER_H */
diff --git a/libpurple/protocols/msn/userlist.c b/libpurple/protocols/msn/userlist.c
index 157ef26945..aa8f1f68bf 100644
--- a/libpurple/protocols/msn/userlist.c
+++ b/libpurple/protocols/msn/userlist.c
@@ -21,7 +21,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
+
+#include "internal.h"
+#include "debug.h"
+#include "request.h"
+
#include "msn.h"
+#include "msnutils.h"
#include "userlist.h"
#include "contact.h"
@@ -107,31 +113,6 @@ got_new_entry(PurpleConnection *gc, const char *passport, const char *friendly,
}
/**************************************************************************
- * Utility functions
- **************************************************************************/
-
-gboolean
-msn_userlist_user_is_in_group(MsnUser *user, const char * group_id)
-{
- if (user == NULL)
- return FALSE;
-
- if (group_id == NULL)
- return FALSE;
-
- return (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp)) != NULL;
-}
-
-gboolean
-msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id)
-{
- if (user == NULL)
- return FALSE;
-
- return (user->list_op & (1 << list_id));
-}
-
-/**************************************************************************
* Server functions
**************************************************************************/
@@ -235,7 +216,7 @@ msn_userlist_destroy(MsnUserList *userlist)
/*destroy userlist*/
for (l = userlist->users; l != NULL; l = l->next)
{
- msn_user_destroy(l->data);
+ msn_user_unref(l->data);
}
g_list_free(userlist->users);
@@ -503,7 +484,7 @@ msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
g_return_if_fail(user != NULL);
- if ( !msn_userlist_user_is_in_list(user, list_id)) {
+ if ( !msn_user_is_in_list(user, list_id)) {
list = lists[list_id];
purple_debug_info("msn", "User %s is not in list %s, not removing.\n", who, list);
return;
@@ -569,13 +550,13 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
user = msn_userlist_find_add_user(userlist, who, who);
- if ( msn_userlist_user_is_in_list(user, MSN_LIST_FL) ) {
+ if ( msn_user_is_in_list(user, MSN_LIST_FL) ) {
purple_debug_info("msn", "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)) {
+ if (msn_user_is_in_group(user, group_id)) {
purple_debug_info("msn", "User %s is already in group %s, returning\n", who, new_group_name);
msn_callback_state_free(state);
return;
@@ -604,7 +585,7 @@ msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
user = msn_userlist_find_add_user(userlist, who, who);
/* First we're going to check if it's already there. */
- if (msn_userlist_user_is_in_list(user, list_id))
+ if (msn_user_is_in_list(user, list_id))
{
list = lists[list_id];
purple_debug_info("msn", "User '%s' is already in list: %s\n", who, list);
@@ -706,6 +687,42 @@ msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
msn_add_contact_to_group(userlist->session, state, who, new_group_id);
}
+void
+msn_release_buddy_icon_request(MsnUserList *userlist)
+{
+ MsnUser *user;
+
+ g_return_if_fail(userlist != NULL);
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn", "Releasing buddy icon request\n");
+
+ if (userlist->buddy_icon_window > 0) {
+ GQueue *queue;
+ PurpleAccount *account;
+ const char *username;
+
+ queue = userlist->buddy_icon_requests;
+
+ if (g_queue_is_empty(userlist->buddy_icon_requests))
+ return;
+
+ user = g_queue_pop_head(queue);
+
+ account = userlist->session->account;
+ username = user->passport;
+
+ userlist->buddy_icon_window--;
+
+ msn_request_user_display(user);
+
+ if (purple_debug_is_verbose())
+ purple_debug_info("msn",
+ "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n",
+ userlist->buddy_icon_window);
+ }
+}
+
/*load userlist from the Blist file cache*/
void
msn_userlist_load(MsnSession *session)
diff --git a/libpurple/protocols/msn/userlist.h b/libpurple/protocols/msn/userlist.h
index c7600e8263..0b7f9407a2 100644
--- a/libpurple/protocols/msn/userlist.h
+++ b/libpurple/protocols/msn/userlist.h
@@ -35,6 +35,16 @@ typedef enum
MSN_LIST_PL /**< Pending list */
} MsnListId;
+typedef enum
+{
+ MSN_LIST_FL_OP = 0x01,
+ MSN_LIST_AL_OP = 0x02,
+ MSN_LIST_BL_OP = 0x04,
+ MSN_LIST_RL_OP = 0x08,
+ MSN_LIST_PL_OP = 0x10
+} MsnListOp;
+#define MSN_LIST_OP_MASK 0x07
+
#include "group.h"
#include "msn.h"
#include "user.h"
@@ -52,9 +62,6 @@ 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);
-
void msn_got_lst_user(MsnSession *session, MsnUser *user,
MsnListOp list_op, GSList *group_ids);
@@ -98,6 +105,7 @@ 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_release_buddy_icon_request(MsnUserList *userlist);
void msn_userlist_load(MsnSession *session);
diff --git a/libpurple/protocols/msn/xfer.c b/libpurple/protocols/msn/xfer.c
new file mode 100644
index 0000000000..6ccf5f9c56
--- /dev/null
+++ b/libpurple/protocols/msn/xfer.c
@@ -0,0 +1,138 @@
+#include "internal.h"
+#include "debug.h"
+
+#include "sbconn.h"
+#include "xfer.h"
+
+/**************************************************************************
+ * Xfer
+ **************************************************************************/
+
+void
+msn_xfer_init(PurpleXfer *xfer)
+{
+ MsnSlpCall *slpcall;
+ /* MsnSlpLink *slplink; */
+ char *content;
+
+ purple_debug_info("msn", "xfer_init\n");
+
+ slpcall = xfer->data;
+
+ /* Send Ok */
+ content = g_strdup_printf("SessionID: %lu\r\n\r\n",
+ slpcall->session_id);
+
+ msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
+ content);
+
+ g_free(content);
+ msn_slplink_send_queued_slpmsgs(slpcall->slplink);
+}
+
+void
+msn_xfer_cancel(PurpleXfer *xfer)
+{
+ MsnSlpCall *slpcall;
+ char *content;
+
+ g_return_if_fail(xfer != NULL);
+ g_return_if_fail(xfer->data != NULL);
+
+ slpcall = xfer->data;
+
+ if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
+ {
+ if (slpcall->started)
+ {
+ msn_slpcall_close(slpcall);
+ }
+ else
+ {
+ content = g_strdup_printf("SessionID: %lu\r\n\r\n",
+ slpcall->session_id);
+
+ msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
+ content);
+
+ g_free(content);
+ msn_slplink_send_queued_slpmsgs(slpcall->slplink);
+
+ if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND)
+ slpcall->wasted = TRUE;
+ else
+ msn_slpcall_destroy(slpcall);
+ }
+ }
+}
+
+gssize
+msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer)
+{
+ MsnSlpCall *slpcall;
+
+ g_return_val_if_fail(xfer != NULL, -1);
+ g_return_val_if_fail(data != NULL, -1);
+ g_return_val_if_fail(len > 0, -1);
+
+ g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND, -1);
+
+ slpcall = xfer->data;
+ /* Not sure I trust it'll be there */
+ g_return_val_if_fail(slpcall != NULL, -1);
+
+ g_return_val_if_fail(slpcall->xfer_msg != NULL, -1);
+
+ slpcall->u.outgoing.len = len;
+ slpcall->u.outgoing.data = data;
+ msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg);
+
+ return MIN(MSN_SBCONN_MAX_SIZE, len);
+}
+
+gssize
+msn_xfer_read(guchar **data, PurpleXfer *xfer)
+{
+ MsnSlpCall *slpcall;
+ gsize len;
+
+ g_return_val_if_fail(xfer != NULL, -1);
+ g_return_val_if_fail(data != NULL, -1);
+
+ g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE, -1);
+
+ slpcall = xfer->data;
+ /* Not sure I trust it'll be there */
+ g_return_val_if_fail(slpcall != NULL, -1);
+
+ /* Just pass up the whole GByteArray. We'll make another. */
+ *data = slpcall->u.incoming_data->data;
+ len = slpcall->u.incoming_data->len;
+
+ g_byte_array_free(slpcall->u.incoming_data, FALSE);
+ slpcall->u.incoming_data = g_byte_array_new();
+
+ return len;
+}
+
+void
+msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session)
+{
+ if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) &&
+ (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) &&
+ (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL))
+ {
+ purple_xfer_cancel_remote(slpcall->xfer);
+ }
+}
+
+void
+msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body,
+ gsize size)
+{
+ PurpleXfer *xfer = slpcall->xfer;
+
+ purple_xfer_set_completed(xfer, TRUE);
+ purple_xfer_end(xfer);
+}
+
diff --git a/libpurple/protocols/msn/xfer.h b/libpurple/protocols/msn/xfer.h
new file mode 100644
index 0000000000..27edb9b77f
--- /dev/null
+++ b/libpurple/protocols/msn/xfer.h
@@ -0,0 +1,12 @@
+
+#include "slpcall.h"
+
+void msn_xfer_init(PurpleXfer *xfer);
+void msn_xfer_cancel(PurpleXfer *xfer);
+
+gssize msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer);
+gssize msn_xfer_read(guchar **data, PurpleXfer *xfer);
+
+void msn_xfer_completed_cb(MsnSlpCall *slpcall,
+ const guchar *body, gsize size);
+void msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e997de484d..40ff2a6aca 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -106,7 +106,6 @@ libpurple/protocols/jabber/usermood.c
libpurple/protocols/jabber/usernick.c
libpurple/protocols/jabber/xdata.c
libpurple/protocols/msn/contact.c
-libpurple/protocols/msn/dialog.c
libpurple/protocols/msn/error.c
libpurple/protocols/msn/group.h
libpurple/protocols/msn/msg.c