summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJorge Villase?or <masca@cpw.pidgin.im>2010-09-15 17:34:21 +0000
committerJorge Villase?or <masca@cpw.pidgin.im>2010-09-15 17:34:21 +0000
commit2eacf4e53dc41bf30cbc84e70b47950ba453006a (patch)
treee673ed542b88b4db622e6064d009300f71a451bb
parentfc4eaa8c92c30a54d7f3b56aa1b6b3ebe71e0227 (diff)
parentbf212551cbf1541a2996a339f8c2a744607c4b82 (diff)
downloadpidgin-2eacf4e53dc41bf30cbc84e70b47950ba453006a.tar.gz
propagate from branch 'im.pidgin.cpw.qulogic.msnp16' (head b639ee32626228584d9b8ca1112317d51bf1de40)
to branch 'im.pidgin.soc.2010.msn-tlc' (head 35d41c8c8ed7a033278ee89640aaaa1ed0302659)
-rw-r--r--libpurple/protocols/msn/Makefile.am8
-rw-r--r--libpurple/protocols/msn/Makefile.mingw4
-rw-r--r--libpurple/protocols/msn/cmdproc.c6
-rw-r--r--libpurple/protocols/msn/cmdproc.h1
-rw-r--r--libpurple/protocols/msn/command.c3
-rw-r--r--libpurple/protocols/msn/contact.c8
-rw-r--r--libpurple/protocols/msn/directconn.c146
-rw-r--r--libpurple/protocols/msn/directconn.h13
-rw-r--r--libpurple/protocols/msn/error.c7
-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.c370
-rw-r--r--libpurple/protocols/msn/msg.h52
-rw-r--r--libpurple/protocols/msn/msn.c38
-rw-r--r--libpurple/protocols/msn/msn.h33
-rw-r--r--libpurple/protocols/msn/msnutils.c26
-rw-r--r--libpurple/protocols/msn/msnutils.h9
-rw-r--r--libpurple/protocols/msn/nexus.c6
-rw-r--r--libpurple/protocols/msn/nexus.h2
-rw-r--r--libpurple/protocols/msn/notification.c28
-rw-r--r--libpurple/protocols/msn/notification.h1
-rw-r--r--libpurple/protocols/msn/object.c2
-rw-r--r--libpurple/protocols/msn/object.h2
-rw-r--r--libpurple/protocols/msn/oim.c5
-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.c9
-rw-r--r--libpurple/protocols/msn/session.h6
-rw-r--r--libpurple/protocols/msn/slp.c1439
-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.c578
-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.c2
-rw-r--r--libpurple/protocols/msn/switchboard.c185
-rw-r--r--libpurple/protocols/msn/switchboard.h33
-rw-r--r--libpurple/protocols/msn/transaction.c4
-rw-r--r--libpurple/protocols/msn/transaction.h2
-rw-r--r--libpurple/protocols/msn/user.c141
-rw-r--r--libpurple/protocols/msn/user.h52
-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
54 files changed, 2977 insertions, 2312 deletions
diff --git a/libpurple/protocols/msn/Makefile.am b/libpurple/protocols/msn/Makefile.am
index 9b4a1c1ba7..dcde2f0a80 100644
--- a/libpurple/protocols/msn/Makefile.am
+++ b/libpurple/protocols/msn/Makefile.am
@@ -34,6 +34,8 @@ MSNSOURCES = \
object.h \
oim.c\
oim.h\
+ p2p.c \
+ p2p.h \
page.c \
page.h \
servconn.c \
@@ -48,10 +50,14 @@ 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 \
table.c \
@@ -62,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..4ef769129a 100644
--- a/libpurple/protocols/msn/Makefile.mingw
+++ b/libpurple/protocols/msn/Makefile.mingw
@@ -52,6 +52,7 @@ C_SRC = cmdproc.c \
notification.c \
object.c \
oim.c\
+ p2p.c \
page.c \
servconn.c \
session.c \
@@ -59,14 +60,17 @@ 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 bac1a96035..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)
diff --git a/libpurple/protocols/msn/cmdproc.h b/libpurple/protocols/msn/cmdproc.h
index 8c415789fa..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"
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/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/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 1b083a2b73..0ca5299732 100644
--- a/libpurple/protocols/msn/error.c
+++ b/libpurple/protocols/msn/error.c
@@ -21,7 +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"
+/* Masca: can we get rid of the sync issue dialog? */
+#include "request.h"
+
#include "error.h"
typedef struct
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 87c21e92c4..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)
@@ -66,6 +72,7 @@ msn_message_destroy(MsnMessage *msg)
g_hash_table_destroy(msg->header_table);
g_list_free(msg->header_list);
+ msn_slpmsgpart_destroy(msg->part);
g_free(msg);
}
@@ -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();
- }
-
- /* Extract the binary SLP 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);
-
- /* Extract 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)
@@ -292,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));
+ }
- /* Extract the binary SLP 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);
-
- /* Extract 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;
- }
-
- /* Extract 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);
@@ -380,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;
+ char *tmp;
- 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;
-
- 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 *
@@ -471,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
{
@@ -757,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)
{
@@ -782,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
{
@@ -883,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)
{
@@ -1031,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 f20941c460..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,9 +92,6 @@ 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 *header_table;
GList *header_list;
@@ -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 f4c4f033e1..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,6 +44,7 @@
#include "msnutils.h"
#include "version.h"
+#include "error.h"
#include "msg.h"
#include "switchboard.h"
#include "notification.h"
@@ -115,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)
{
@@ -818,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
@@ -1697,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);
@@ -1717,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);
}
diff --git a/libpurple/protocols/msn/msn.h b/libpurple/protocols/msn/msn.h
index b103ac9985..c9b6b826ec 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 16
+#define WLM_PROT_VER 15
-#define WLM_MAX_PROTOCOL 16
+#define WLM_MAX_PROTOCOL 15
#define WLM_MIN_PROTOCOL 15
#define MSN_TYPING_RECV_TIMEOUT 6
@@ -142,7 +116,6 @@ typedef enum
((MSN_CLIENT_ID_VERSION << 24) | \
(MSN_CLIENT_ID_CAPABILITIES))
-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 fb5a434bb5..e8032533a0 100644
--- a/libpurple/protocols/msn/msnutils.c
+++ b/libpurple/protocols/msn/msnutils.c
@@ -21,6 +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 "internal.h"
+
#include "msn.h"
#include "msnutils.h"
@@ -474,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 ac155d3a3b..1548be395b 100644
--- a/libpurple/protocols/msn/msnutils.h
+++ b/libpurple/protocols/msn/msnutils.h
@@ -64,6 +64,15 @@ void msn_import_html(const char *html, char **attributes, char **message);
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
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 5b1c02e1f6..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 "slplink.h"
static MsnTable *cbs_table;
@@ -93,6 +95,7 @@ connect_cb(MsnServConn *servconn)
MsnCmdProc *cmdproc;
MsnSession *session;
MsnTransaction *trans;
+ PurpleAccount *account;
GString *vers;
const char *ver_str;
int i;
@@ -101,6 +104,7 @@ connect_cb(MsnServConn *servconn)
cmdproc = servconn->cmdproc;
session = servconn->session;
+ account = session->account;
vers = g_string_new("");
@@ -186,8 +190,10 @@ 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"))
{
@@ -1016,6 +1022,7 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
PurpleAccount *account;
+ PurpleConnection *gc;
MsnUser *user;
MsnObject *msnobj = NULL;
unsigned long clientid, extcaps;
@@ -1026,6 +1033,7 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
session = cmdproc->session;
account = session->account;
+ gc = purple_account_get_connection(account);
state = cmd->params[1];
passport = cmd->params[2];
@@ -1239,6 +1247,7 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
{
MsnSession *session;
PurpleAccount *account;
+ PurpleConnection *gc;
MsnUser *user;
MsnObject *msnobj;
unsigned long clientid, extcaps;
@@ -1248,6 +1257,7 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
session = cmdproc->session;
account = session->account;
+ gc = purple_account_get_connection(account);
state = cmd->params[0];
passport = cmd->params[1];
@@ -1431,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);
@@ -1447,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]); */
@@ -1688,12 +1696,14 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
size_t len)
{
MsnSession *session;
+ PurpleAccount *account;
MsnUser *user;
const char *passport;
xmlnode *payloadNode;
char *psm_str, *str;
session = cmdproc->session;
+ account = session->account;
passport = cmd->params[0];
if (g_str_equal(passport, session->user->passport))
diff --git a/libpurple/protocols/msn/notification.h b/libpurple/protocols/msn/notification.h
index 089be38312..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
{
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 776436d8f8..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"
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 fa24530b17..25e1043caf 100644
--- a/libpurple/protocols/msn/session.c
+++ b/libpurple/protocols/msn/session.c
@@ -21,8 +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 "internal.h"
+#include "debug.h"
+
#include "error.h"
-#include "msn.h"
#include "msnutils.h"
#include "session.h"
#include "notification.h"
@@ -74,7 +77,7 @@ 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);
@@ -86,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);
diff --git a/libpurple/protocols/msn/session.h b/libpurple/protocols/msn/session.h
index f9595c37a1..4277112697 100644
--- a/libpurple/protocols/msn/session.h
+++ b/libpurple/protocols/msn/session.h
@@ -61,13 +61,11 @@ 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 "user.h"
#include "userlist.h"
diff --git a/libpurple/protocols/msn/slp.c b/libpurple/protocols/msn/slp.c
index a2490573d3..5a3de04bbb 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,10 +32,10 @@
#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
@@ -46,167 +49,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,1040 +92,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 (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")))
- {
- /* 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;
-}
-
/**************************************************************************
* 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.
@@ -1304,62 +116,35 @@ 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);
+
+#if 0
+ /* Free one window slot */
+ userlist->buddy_icon_window++;
+
+ purple_debug_info("msn", "got_user_display(): buddy_icon_window++ yields =%d\n",
+ userlist->buddy_icon_window);
+
+ msn_release_buddy_icon_request(userlist);
+#endif
}
static void
@@ -1419,7 +204,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;
@@ -1455,32 +276,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;
+ }
- purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info);
+ uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL);
- /* Free one window slot */
- session->userlist->buddy_icon_window++;
+ 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);
- if (purple_debug_is_verbose())
- purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n",
- 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);
- msn_release_buddy_icon_request(session->userlist);
+ 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);
+
+ 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 49a9fa3570..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;
-
- msg->msnslp_header.total_size = slpmsg->size;
+ slpmsg->header->id = slpmsg->id;
- passport = purple_normalize(slplink->session->account, slplink->remote_user);
- msn_message_set_header(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,279 +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;
-
- 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;
- }
- 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)
{
- 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)
{
- PurpleXfer *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)
- {
- slpmsg->slpcall->u.incoming_data =
- g_byte_array_append(slpmsg->slpcall->u.incoming_data, (const guchar *)data, len);
- purple_xfer_prpl_ready(slpmsg->slpcall->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
@@ -856,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 4592b61003..743627f898 100644
--- a/libpurple/protocols/msn/state.c
+++ b/libpurple/protocols/msn/state.c
@@ -23,10 +23,10 @@
*/
#include "internal.h"
+#include "debug.h"
#include "core.h"
-#include "msn.h"
#include "notification.h"
#include "state.h"
diff --git a/libpurple/protocols/msn/switchboard.c b/libpurple/protocols/msn/switchboard.c
index 9e789ef7f7..f443966fa7 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,8 +250,11 @@ 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;
}
@@ -258,7 +265,21 @@ msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user)
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;
@@ -276,7 +297,7 @@ 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);
}
@@ -304,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);
@@ -322,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
{
@@ -412,7 +434,7 @@ msg_resend_cb(gpointer data)
return FALSE;
}
-static void
+void
msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error)
{
MsnSwitchBoard *swboard;
@@ -549,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)
@@ -594,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)
{
@@ -695,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
**************************************************************************/
@@ -767,8 +674,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]);
@@ -780,17 +691,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);
@@ -853,8 +768,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)
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/transaction.c b/libpurple/protocols/msn/transaction.c
index 4c87a862b6..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"
diff --git a/libpurple/protocols/msn/transaction.h b/libpurple/protocols/msn/transaction.h
index 2f9a29d20f..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"
diff --git a/libpurple/protocols/msn/user.c b/libpurple/protocols/msn/user.c
index 02a0e5244a..f03b27a26c 100644
--- a/libpurple/protocols/msn/user.c
+++ b/libpurple/protocols/msn/user.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 "debug.h"
+#include "util.h"
+
#include "user.h"
#include "slp.h"
@@ -46,15 +50,20 @@ 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);
@@ -94,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)
{
@@ -477,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 != user->userlist->session->user && user->list_op & MSN_LIST_FL_OP)
- msn_queue_buddy_icon_request(user);
+ queue_buddy_icon_request(user);
}
void
@@ -632,3 +727,39 @@ msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extca
return (user->clientid & capability) && (user->extcaps & extcap);
}
+
+/**************************************************************************
+ * Utility functions
+ **************************************************************************/
+
+int
+msn_user_passport_cmp(MsnUser *user, const char *passport)
+{
+ const char *pass;
+
+ pass = msn_user_get_passport(user);
+
+ return strcmp(pass, purple_normalize_nocase(NULL, passport));
+}
+
+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 d6ec36d97c..02cd297c25 100644
--- a/libpurple/protocols/msn/user.h
+++ b/libpurple/protocols/msn/user.h
@@ -79,6 +79,8 @@ struct _MsnUser
{
MsnUserList *userlist;
+ guint8 refcount; /**< The reference count of this object */
+
char *passport; /**< The passport account. */
char *friendly_name; /**< The friendly name. */
@@ -148,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.
@@ -406,6 +416,30 @@ guint msn_user_get_clientid(const MsnUser *user);
*/
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.
*
@@ -456,6 +490,18 @@ 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.
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);