pidgin: 2d2efd5f: Initial support for direct connections. ...
qulogic at pidgin.im
qulogic at pidgin.im
Fri May 21 18:02:55 EDT 2010
-----------------------------------------------------------------
Revision: 2d2efd5f9499aeb508a426b9b7c916a9ac7a3f99
Ancestor: 8524850cd868993d8572075af80943765b232d45
Author: kukkerman at gmail.com
Date: 2010-03-17T03:45:07
Branch: im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/2d2efd5f9499aeb508a426b9b7c916a9ac7a3f99
Modified files:
COPYRIGHT ChangeLog libpurple/protocols/msn/Makefile.am
libpurple/protocols/msn/Makefile.mingw
libpurple/protocols/msn/directconn.c
libpurple/protocols/msn/directconn.h
libpurple/protocols/msn/msg.c libpurple/protocols/msn/slp.c
libpurple/protocols/msn/slp.h
libpurple/protocols/msn/slpcall.c
libpurple/protocols/msn/slpcall.h
libpurple/protocols/msn/slplink.c
libpurple/protocols/msn/slplink.h
libpurple/protocols/msn/switchboard.c
ChangeLog:
Initial support for direct connections. Preliminary patch from ticket #247
by G?bor Szuromi. Still needs lots of testing and fixes.
References #247.
-------------- next part --------------
============================================================
--- COPYRIGHT e5b292f95d56ea70e4a48995364046eb99026565
+++ COPYRIGHT 78a38343d1b2c681275be9ff9395c625279b4673
@@ -468,6 +468,7 @@ Amir Szekely (kichik)
Marcus Sundberg
M?rten Svantesson (fursten)
Amir Szekely (kichik)
+G?bor Szuromi (kukkerman)
Robert T.
Greg Taeger
Rob Taft
============================================================
--- ChangeLog fe0e3923a0d78718a45d71530daf8be78dc18e1b
+++ ChangeLog e76d1ab898dae650abb118fdf099d3c3ae1342eb
@@ -42,6 +42,8 @@ version 2.7.0 (??/??/????):
MSN:
* Support for version 9 of the MSN protocol has been removed. This
version is no longer supported on the servers.
+ * Support for direct connections, enabling faster file transfers,
+ smiley and buddy icon loading. (G?bor Szuromi)
XMPP:
* Direct messages to a specific resource only upon receipt of a message
============================================================
--- libpurple/protocols/msn/Makefile.am bc0d9535367c5da381e6515af27b49fb54888e96
+++ libpurple/protocols/msn/Makefile.am e7c9f92d4df55c26c8822cffe3330821ce67221d
@@ -14,6 +14,8 @@ MSNSOURCES = \
contact.h\
dialog.c \
dialog.h \
+ directconn.c \
+ directconn.h \
error.c \
error.h \
group.c \
============================================================
--- libpurple/protocols/msn/Makefile.mingw 2fc5eab590015d1ba8b3e3effb4896a0338d7d7a
+++ libpurple/protocols/msn/Makefile.mingw 6d868e3ff6b0abc9ab2505916a7783da251bd486
@@ -41,6 +41,7 @@ C_SRC = cmdproc.c \
command.c \
contact.c\
dialog.c \
+ directconn.c \
error.c \
group.c \
history.c \
============================================================
--- libpurple/protocols/msn/directconn.c 255e1b5e3987031670d14fd45f14318ec336f4fb
+++ libpurple/protocols/msn/directconn.c c11e6c8c9e829c95737bab7a19d3f777dd60361e
@@ -27,479 +27,1379 @@
#include "slp.h"
#include "slpmsg.h"
-/**************************************************************************
- * Directconn Specific
- **************************************************************************/
+#define DC_SESSION_ID_OFFS 0
+#define DC_SEQ_ID_OFFS 4
+#define DC_DATA_OFFSET_OFFS 8
+#define DC_TOTAL_DATA_SIZE_OFFS 16
+#define DC_MESSAGE_LENGTH_OFFS 24
+#define DC_FLAGS_OFFS 28
+#define DC_ACK_ID_OFFS 32
+#define DC_ACK_UID_OFFS 36
+#define DC_ACK_DATA_SIZE_OFFS 40
+#define DC_MESSAGE_BODY_OFFS 48
-void
-msn_directconn_send_handshake(MsnDirectConn *directconn)
+#define DC_PACKET_HEADER_SIZE 48
+#define DC_MAX_BODY_SIZE 1352
+#define DC_MAX_PACKET_SIZE (DC_PACKET_HEADER_SIZE + DC_MAX_BODY_SIZE)
+
+static void
+msn_dc_generate_nonce(MsnDirectConn *dc)
{
- MsnSlpLink *slplink;
- MsnSlpMessage *slpmsg;
+ PurpleCipher *cipher = NULL;
+ PurpleCipherContext *context = NULL;
+ static guchar digest[20];
+ int i;
+
+ guint32 g1;
+ guint16 g2;
+ guint16 g3;
+ guint64 g4;
- g_return_if_fail(directconn != NULL);
+ cipher = purple_ciphers_find_cipher("sha1");
+ g_return_if_fail(cipher != NULL);
+
+ for (i = 0; i < 16; i++)
+ dc->nonce[i] = rand() & 0xff;
- slplink = directconn->slplink;
+ context = purple_cipher_context_new(cipher, NULL);
+ purple_cipher_context_append(context, dc->nonce, 16);
+ purple_cipher_context_digest(context, 20, digest, NULL);
+ purple_cipher_context_destroy(context);
- slpmsg = msn_slpmsg_new(slplink);
- slpmsg->flags = 0x100;
+ g1 = *((guint32*)(digest + 0));
+ g1 = GUINT32_FROM_LE(g1);
+
+ g2 = *((guint16*)(digest + 4));
+ g2 = GUINT16_FROM_LE(g2);
+
+ g3 = *((guint16*)(digest + 6));
+ g3 = GUINT32_FROM_LE(g3);
+
+ g4 = *((guint64*)(digest + 8));
+ g4 = GUINT64_FROM_BE(g4);
+
+ g_sprintf(
+ dc->nonce_hash,
+ "%08X-%04X-%04X-%04X-%08X%04X",
+ g1,
+ g2,
+ g3,
+ (guint16)(g4 >> 48),
+ (guint32)((g4 >> 16) & 0xffffffff),
+ (guint16)(g4 & 0xffff)
+ );
+}
- if (directconn->nonce != NULL)
- {
- guint32 t1;
- guint16 t2;
- guint16 t3;
- guint16 t4;
- guint64 t5;
+static MsnDirectConnPacket*
+msn_dc_new_packet()
+{
+ MsnDirectConnPacket *p;
- sscanf (directconn->nonce, "%08X-%04hX-%04hX-%04hX-%012" G_GINT64_MODIFIER "X", &t1, &t2, &t3, &t4, &t5);
+ p = g_new0(MsnDirectConnPacket, 1);
+ p->data = NULL;
+ p->sent_cb = NULL;
+ p->msg = NULL;
- t1 = GUINT32_TO_LE(t1);
- t2 = GUINT16_TO_LE(t2);
- t3 = GUINT16_TO_LE(t3);
- t4 = GUINT16_TO_BE(t4);
- t5 = GUINT64_TO_BE(t5);
+ return p;
+}
- slpmsg->ack_id = t1;
- slpmsg->ack_sub_id = t2 | (t3 << 16);
- slpmsg->ack_size = t4 | t5;
- }
+static void
+msn_dc_destroy_packet(MsnDirectConnPacket *p)
+{
+ if (p->data)
+ g_free(p->data);
+
+ if (p->msg)
+ msn_message_unref(p->msg);
- g_free(directconn->nonce);
+ g_free(p);
+}
- msn_slplink_send_slpmsg(slplink, slpmsg);
+MsnDirectConn*
+msn_dc_new(MsnSlpCall *slpcall)
+{
+ MsnDirectConn *dc;
- directconn->acked =TRUE;
-}
+ purple_debug_info("msn", "msn_dc_new\n");
-/**************************************************************************
- * Connection Functions
- **************************************************************************/
+ g_return_val_if_fail(slpcall != NULL, NULL);
+
+ dc = g_new0(MsnDirectConn, 1);
-static int
-create_listener(int port)
-{
- int fd;
- int flags;
- const int on = 1;
+ dc->slplink = slpcall->slplink;
+ dc->slpcall = slpcall;
-#if 0
- struct addrinfo hints;
- struct addrinfo *c, *res;
- char port_str[5];
+ if (dc->slplink->dc != NULL)
+ purple_debug_warning("msn", "msn_dc_new: slplink already has an allocated DC!\n");
- snprintf(port_str, sizeof(port_str), "%d", port);
+ dc->slplink->dc = dc;
- memset(&hints, 0, sizeof(hints));
+ dc->msg_body = NULL;
+ dc->prev_ack = NULL;
+ dc->listen_data = NULL;
+ dc->connect_data = NULL;
+ dc->listenfd = -1;
+ dc->listenfd_handle = 0;
+ dc->connect_timeout_handle = 0;
+ dc->fd = -1;
+ dc->recv_handle = 0;
+ dc->send_handle = 0;
+ dc->state = DC_STATE_CLOSED;
+ dc->in_buffer = NULL;
+ dc->out_queue = g_queue_new();
+ dc->msg_pos = 0;
+ dc->send_connection_info_msg_cb = NULL;
+ dc->ext_ip = NULL;
+ dc->timeout_handle = 0;
+ dc->progress = FALSE;
+ //dc->num_calls = 1;
- hints.ai_flags = AI_PASSIVE;
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
+ msn_dc_generate_nonce(dc);
- if (getaddrinfo(NULL, port_str, &hints, &res) != 0)
- {
- purple_debug_error("msn", "Could not get address info: %s.\n",
- port_str);
- return -1;
- }
+ return dc;
+}
- for (c = res; c != NULL; c = c->ai_next)
- {
- fd = socket(c->ai_family, c->ai_socktype, c->ai_protocol);
+void
+msn_dc_destroy(MsnDirectConn *dc)
+{
+ MsnSlpLink *slplink;
+
+ purple_debug_info("msn", "msn_dc_destroy\n");
+
+ g_return_if_fail(dc != NULL);
- if (fd < 0)
- continue;
+ slplink = dc->slplink;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (dc->slpcall != NULL)
+ dc->slpcall->wait_for_socket = FALSE;
+
+ slplink->dc = NULL;
- if (bind(fd, c->ai_addr, c->ai_addrlen) == 0)
- break;
+ if (slplink->swboard == NULL)
+ msn_slplink_destroy(slplink);
- close(fd);
+ if (dc->msg_body != NULL) {
+ g_free(dc->msg_body);
+ dc->msg_body = NULL;
}
+
+ if (dc->prev_ack) {
+ msn_slpmsg_destroy(dc->prev_ack);
+ dc->prev_ack = NULL;
+ }
- if (c == NULL)
- {
- purple_debug_error("msn", "Could not find socket: %s.\n", port_str);
- return -1;
+ if (dc->listen_data != NULL) {
+ purple_network_listen_cancel(dc->listen_data);
+ dc->listen_data = NULL;
}
+
+ if (dc->connect_data != NULL) {
+ purple_proxy_connect_cancel(dc->connect_data);
+ dc->connect_data = NULL;
+ }
+
+ if (dc->listenfd != -1) {
+ purple_network_remove_port_mapping(dc->listenfd);
+ close(dc->listenfd);
+ dc->listenfd = -1;
+ }
+
+ if (dc->listenfd_handle != 0) {
+ purple_timeout_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->fd != -1) {
+ close(dc->fd);
+ dc->fd = -1;
+ }
+
+ if (dc->send_handle != 0) {
+ purple_input_remove(dc->send_handle);
+ dc->send_handle = 0;
+ }
+
+ if (dc->recv_handle != 0) {
+ purple_input_remove(dc->recv_handle);
+ dc->recv_handle = 0;
+ }
- freeaddrinfo(res);
-#else
- struct sockaddr_in sockin;
+ if (dc->in_buffer != NULL) {
+ g_free(dc->in_buffer);
+ dc->in_buffer = NULL;
+ }
+
+ if (dc->out_queue != NULL) {
+ while (!g_queue_is_empty(dc->out_queue))
+ msn_dc_destroy_packet( g_queue_pop_head(dc->out_queue) );
- fd = socket(AF_INET, SOCK_STREAM, 0);
+ g_queue_free(dc->out_queue);
+ }
- if (fd < 0)
- return -1;
+ if (dc->ext_ip != NULL) {
+ g_free(dc->ext_ip);
+ dc->ext_ip = NULL;
+ }
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0)
- {
- close(fd);
- return -1;
+ if (dc->timeout_handle != 0) {
+ purple_timeout_remove(dc->timeout_handle);
+ dc->timeout_handle = 0;
}
- memset(&sockin, 0, sizeof(struct sockaddr_in));
- sockin.sin_family = AF_INET;
- sockin.sin_port = htons(port);
+ g_free(dc);
+}
- if (bind(fd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0)
- {
- close(fd);
- return -1;
+/*
+void
+msn_dc_ref(MsnDirectConn *dc)
+{
+ g_return_if_fail(dc != NULL);
+
+ dc->num_calls++;
+}
+
+void
+msn_dc_unref(MsnDirectConn *dc)
+{
+ g_return_if_fail(dc != NULL);
+
+
+ if (dc->num_calls > 0) {
+ dc->num_calls--;
}
-#endif
+}
+*/
- if (listen (fd, 4) != 0)
- {
- close (fd);
- return -1;
- }
+void
+msn_dc_send_invite(MsnDirectConn *dc)
+{
+ MsnSlpCall *slpcall;
+ MsnSlpMessage *msg;
+ gchar *header;
+
+ purple_debug_info("msn", "msn_dc_send_invite\n");
- flags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-#ifndef _WIN32
- fcntl(fd, F_SETFD, FD_CLOEXEC);
-#endif
+ g_return_if_fail(dc != NULL);
- return fd;
+ slpcall = dc->slpcall;
+ g_return_if_fail(slpcall != NULL);
+
+ header = g_strdup_printf(
+ "INVITE MSNMSGR:%s MSNSLP/1.0",
+ slpcall->slplink->remote_user
+ );
+
+ msg = msn_slpmsg_sip_new(
+ slpcall,
+ 0,
+ header,
+ slpcall->branch,
+ "application/x-msnmsgr-transrespbody",
+ dc->msg_body
+ );
+ g_free(header);
+ g_free(dc->msg_body);
+ dc->msg_body = NULL;
+
+ msn_slplink_queue_slpmsg(slpcall->slplink, msg);
}
-static gssize
-msn_directconn_write(MsnDirectConn *directconn,
- const char *data, size_t len)
+void
+msn_dc_send_ok(MsnDirectConn *dc)
{
- char *buffer, *tmp;
- size_t buf_size;
- gssize ret;
- guint32 sent_len;
+ purple_debug_info("msn", "msn_dc_send_ok\n");
+
+ g_return_if_fail(dc != NULL);
+
+ msn_slp_send_ok(dc->slpcall, dc->slpcall->branch,
+ "application/x-msnmsgr-transrespbody", dc->msg_body);
+ g_free(dc->msg_body);
+ dc->msg_body = NULL;
- g_return_val_if_fail(directconn != NULL, 0);
+ msn_slplink_send_slpmsg(dc->slpcall->slplink, dc->prev_ack);
+ msn_slpmsg_destroy(dc->prev_ack);
+ dc->prev_ack = NULL;
+ msn_slplink_send_queued_slpmsgs(dc->slpcall->slplink);
+}
- buf_size = len + 4;
- buffer = tmp = g_malloc(buf_size);
+static void
+msn_dc_fallback_to_p2p(MsnDirectConn *dc)
+{
+ MsnSlpCall *slpcall;
+ PurpleXfer *xfer;
+
+ purple_debug_info("msn", "msn_dc_try_fallback_to_p2p\n");
+
+ g_return_if_fail(dc != NULL);
- sent_len = GUINT32_TO_LE(len);
+ slpcall = dc->slpcall;
+ g_return_if_fail(slpcall != NULL);
+
+ xfer = slpcall->xfer;
+ g_return_if_fail(xfer != NULL);
+
+ msn_dc_destroy(dc);
- memcpy(tmp, &sent_len, 4);
- tmp += 4;
- memcpy(tmp, data, len);
- tmp += len;
+ msn_slpcall_session_init(slpcall);
- ret = write(directconn->fd, buffer, buf_size);
+ /*
+ switch (purple_xfer_get_status(xfer)) {
+ case PURPLE_XFER_STATUS_NOT_STARTED:
+ case PURPLE_XFER_STATUS_ACCEPTED:
+ msn_slpcall_session_init(slpcall);
+ break;
-#ifdef DEBUG_DC
- char *str;
- str = g_strdup_printf("%s/msntest/w%.4d.bin", g_get_home_dir(), directconn->c);
+ case PURPLE_XFER_STATUS_STARTED:
+ slpcall->session_init_cb = NULL;
+ slpcall->end_cb = NULL;
+ slpcall->progress_cb = NULL;
+ slpcall->cb = NULL;
+
+ if (fail_local)
+ purple_xfer_cancel_local(xfer);
+ else
+ purple_xfer_cancel_remote(xfer);
+ break;
- FILE *tf = g_fopen(str, "w");
- fwrite(buffer, 1, buf_size, tf);
- fclose(tf);
+ default:
+ slpcall->session_init_cb = NULL;
+ slpcall->end_cb = NULL;
+ slpcall->progress_cb = NULL;
+ slpcall->cb = NULL;
+
+ if (fail_local)
+ purple_xfer_cancel_local(xfer);
+ else
+ purple_xfer_cancel_remote(xfer);
- g_free(str);
-#endif
+ break;
+ }
+ */
+}
- g_free(buffer);
+static void
+msn_dc_parse_binary_header(MsnDirectConn *dc)
+{
+ MsnSlpHeader *h;
+ gchar *buffer;
- directconn->c++;
+ g_return_if_fail(dc != NULL);
- return ret;
+ h = &dc->header;
+ /* Skip packet size */
+ buffer = dc->in_buffer + 4;
+
+ memcpy(&h->session_id, buffer + DC_SESSION_ID_OFFS, sizeof(h->session_id));
+ h->session_id = GUINT32_FROM_LE(h->session_id);
+
+ memcpy(&h->id, buffer + DC_SEQ_ID_OFFS, sizeof(h->id));
+ h->id = GUINT32_FROM_LE(h->id);
+
+ memcpy(&h->offset, buffer + DC_DATA_OFFSET_OFFS, sizeof(h->offset));
+ h->offset = GUINT64_FROM_LE(h->offset);
+
+ memcpy(&h->total_size, buffer + DC_TOTAL_DATA_SIZE_OFFS, sizeof(h->total_size));
+ h->total_size = GUINT64_FROM_LE(h->total_size);
+
+ memcpy(&h->length, buffer + DC_MESSAGE_LENGTH_OFFS, sizeof(h->length));
+ h->length = GUINT32_FROM_LE(h->length);
+
+ memcpy(&h->flags, buffer + DC_FLAGS_OFFS, sizeof(h->flags));
+ h->flags = GUINT32_FROM_LE(h->flags);
+
+ memcpy(&h->ack_id, buffer + DC_ACK_ID_OFFS, sizeof(h->ack_id));
+ h->ack_id = GUINT32_FROM_LE(h->ack_id);
+
+ memcpy(&h->ack_sub_id, buffer + DC_ACK_UID_OFFS, sizeof(h->ack_sub_id));
+ h->ack_sub_id = GUINT32_FROM_LE(h->ack_sub_id);
+
+ memcpy(&h->ack_size, buffer + DC_ACK_DATA_SIZE_OFFS, sizeof(h->ack_size));
+ h->ack_size = GUINT64_FROM_LE(h->ack_size);
}
-#if 0
-void
-msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce)
-{
- guint32 t1;
- guint16 t2;
- guint16 t3;
- guint16 t4;
- guint64 t5;
+static gchar*
+msn_dc_serialize_binary_header(MsnDirectConn *dc) {
+ static MsnSlpHeader h;
+ static gchar bin_header[DC_PACKET_HEADER_SIZE];
+
+ g_return_val_if_fail(dc != NULL, NULL);
- g_return_if_fail(directconn != NULL);
- g_return_if_fail(nonce != NULL);
+ memcpy(&h, &dc->header, sizeof(h));
- sscanf (nonce, "%08X-%04hX-%04hX-%04hX-%012llX", &t1, &t2, &t3, &t4, &t5);
+ h.session_id = GUINT32_TO_LE(h.session_id);
+ memcpy(bin_header + DC_SESSION_ID_OFFS, &h.session_id, sizeof(h.session_id));
+
+ h.id = GUINT32_TO_LE(h.id);
+ memcpy(bin_header + DC_SEQ_ID_OFFS, &h.id, sizeof(h.id));
- t1 = GUINT32_TO_LE(t1);
- t2 = GUINT16_TO_LE(t2);
- t3 = GUINT16_TO_LE(t3);
- t4 = GUINT16_TO_BE(t4);
- t5 = GUINT64_TO_BE(t5);
+ h.offset = GUINT64_TO_LE(h.offset);
+ memcpy(bin_header + DC_DATA_OFFSET_OFFS, &h.offset, sizeof(h.offset));
- directconn->slpheader = g_new0(MsnSlpHeader, 1);
+ h.total_size = GUINT64_TO_LE(h.total_size);
+ memcpy(bin_header + DC_TOTAL_DATA_SIZE_OFFS, &h.total_size, sizeof(h.total_size));
- directconn->slpheader->ack_id = t1;
- directconn->slpheader->ack_sub_id = t2 | (t3 << 16);
- directconn->slpheader->ack_size = t4 | t5;
+ h.length = GUINT32_TO_LE(h.length);
+ memcpy(bin_header + DC_MESSAGE_LENGTH_OFFS, &h.length, sizeof(h.length));
+
+ h.flags = GUINT32_TO_LE(h.flags);
+ memcpy(bin_header + DC_FLAGS_OFFS, &h.flags, sizeof(h.flags));
+
+ h.ack_id = GUINT32_TO_LE(h.ack_id);
+ memcpy(bin_header + DC_ACK_ID_OFFS, &h.ack_id, sizeof(h.ack_id));
+
+ h.ack_sub_id = GUINT32_TO_LE(h.ack_sub_id);
+ memcpy(bin_header + DC_ACK_UID_OFFS, &h.ack_sub_id, sizeof(h.ack_sub_id));
+
+ h.ack_size = GUINT64_TO_LE(h.ack_size);
+ memcpy(bin_header + DC_ACK_DATA_SIZE_OFFS, &h.ack_size, sizeof(h.ack_size));
+
+ return bin_header;
}
-#endif
-void
-msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg)
+/*
+static void
+msn_dc_send_bye(MsnDirectConn *dc)
{
- char *body;
- size_t body_len;
+ MsnSlpLink *slplink;
+ PurpleAccount *account;
+ char *body;
+ int body_len;
+
+ purple_debug_info("msn", "msn_dc_send_bye\n");
+
+ g_return_if_fail(dc != NULL);
+ g_return_if_fail(dc->slpcall != NULL);
- body = msn_message_gen_slp_body(msg, &body_len);
+ slplink = dc->slpcall->slplink;
+ account = slplink->session->account;
+
+ dc->header.session_id = 0;
+ dc->header.id = dc->slpcall->slplink->slp_seq_id++;
+ dc->header.offset = 0;
+
+ body = g_strdup_printf(
+ "BYE MSNMSGR:%s MSNSLP/1.0\r\n"
+ "To: <msnmsgr:%s>\r\n"
+ "From: <msnmsgr:%s>\r\n"
+ "Via: MSNSLP/1.0/TLP ;branch={%s}\r\n"
+ "CSeq: 0\r\n"
+ "Call-ID: {%s}\r\n"
+ "Max-Forwards: 0\r\n"
+ "Content-Type: application/x-msnmsgr-sessionclosebody\r\n"
+ "Content-Length: 3\r\n"
+ "\r\n\r\n",
- msn_directconn_write(directconn, body, body_len);
+ slplink->remote_user,
+ slplink->remote_user,
+ purple_account_get_username(account),
+ dc->slpcall->branch,
+ dc->slpcall->id
+ );
+ body_len = strlen(body) + 1;
+ memcpy(dc->buffer, body, body_len);
+ g_free(body);
+
+ dc->header.total_size = body_len;
+ dc->header.length = body_len;
+ dc->header.flags = 0;
+ dc->header.ack_sub_id = 0;
+ dc->header.ack_size = 0;
+
+ msn_dc_send_packet(dc);
}
static void
-read_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_dc_send_ack(MsnDirectConn *dc)
{
- MsnDirectConn* directconn;
- char *body;
- size_t body_len;
- gssize len;
+ g_return_if_fail(dc != NULL);
+
+ dc->header.session_id = 0;
+ dc->header.ack_sub_id = dc->header.ack_id;
+ dc->header.ack_id = dc->header.id;
+ dc->header.id = dc->slpcall->slplink->slp_seq_id++;
+ dc->header.offset = 0;
+ dc->header.length = 0;
+ dc->header.flags = 0x02;
+ dc->header.ack_size = dc->header.total_size;
- purple_debug_info("msn", "read_cb: %d, %d\n", source, cond);
+ msn_dc_send_packet(dc);
+}
- directconn = data;
+static void
+msn_dc_send_data_ack(MsnDirectConn *dc)
+{
+ g_return_if_fail(dc != NULL);
+
+ dc->header.session_id = dc->slpcall->session_id;
+ dc->header.ack_sub_id = dc->header.ack_id;
+ dc->header.ack_id = dc->header.id;
+ dc->header.id = dc->slpcall->slplink->slp_seq_id++;
+ dc->header.offset = 0;
+ dc->header.length = 0;
+ dc->header.flags = 0x02;
+ dc->header.ack_size = dc->header.total_size;
- /* Let's read the length of the data. */
-#error This code is broken. See the note below.
- /*
- * TODO: This has problems! First of all, sizeof(body_len) will be
- * different on 32bit systems and on 64bit systems (4 bytes
- * vs. 8 bytes).
- * Secondly, we're reading from a TCP stream. There is no
- * guarantee that we have received the number of bytes we're
- * trying to read. We need to read into a buffer. If read
- * returns <0 then we need to check errno. If errno is EAGAIN
- * then don't destroy anything, just exit and wait for more
- * data. See every other function in libpurple that does this
- * correctly for an example.
- */
- len = read(directconn->fd, &body_len, sizeof(body_len));
+ msn_dc_send_packet(dc);
+}
- if (len <= 0)
- {
- /* ERROR */
- purple_debug_error("msn", "error reading\n");
+static void
+msn_dc_xfer_send_cancel(PurpleXfer *xfer)
+{
+ MsnSlpCall *slpcall;
+ MsnDirectConn *dc;
+
+ purple_debug_info("msn", "msn_dc_xfer_send_cancel\n");
+
+ g_return_if_fail(xfer != NULL);
- if (directconn->inpa)
- purple_input_remove(directconn->inpa);
+ slpcall = xfer->data;
+ g_return_if_fail(slpcall != NULL);
- close(directconn->fd);
+ dc = slpcall->dc;
+ g_return_if_fail(dc != NULL);
+
+ switch (dc->state) {
+ case DC_STATE_TRANSFER:
+ msn_dc_send_bye(dc);
+ dc->state = DC_STATE_CANCELLED;
+ break;
- msn_directconn_destroy(directconn);
+ default:
+ msn_dc_destroy(dc);
+ break;
+ }
+}
- return;
- }
+static void
+msn_dc_xfer_recv_cancel(PurpleXfer *xfer)
+{
+ MsnSlpCall *slpcall;
+ MsnDirectConn *dc;
+
+ purple_debug_info("msn", "msn_dc_xfer_recv_cancel\n");
- body_len = GUINT32_FROM_LE(body_len);
+ g_return_if_fail(xfer != NULL);
- purple_debug_info("msn", "body_len=%" G_GSIZE_FORMAT "\n", body_len);
+ slpcall = xfer->data;
+ g_return_if_fail(slpcall != NULL);
- if (body_len <= 0)
- {
- /* ERROR */
- purple_debug_error("msn", "error reading\n");
+ dc = slpcall->dc;
+ g_return_if_fail(dc != NULL);
- if (directconn->inpa)
- purple_input_remove(directconn->inpa);
+ switch (dc->state) {
+ case DC_STATE_TRANSFER:
+ msn_dc_send_bye(dc);
+ dc->state = DC_STATE_CANCELLED;
+ break;
- close(directconn->fd);
+ default:
+ msn_dc_destroy(dc);
+ break;
+ }
+}
+*/
- msn_directconn_destroy(directconn);
+static void
+msn_dc_send_cb(gpointer data, gint fd, PurpleInputCondition cond)
+{
+ MsnDirectConn *dc = data;
+ MsnDirectConnPacket *p;
+ int bytes_to_send;
+ int bytes_sent;
+ g_return_if_fail(dc != NULL);
+ g_return_if_fail(fd != -1);
+
+ if(g_queue_is_empty(dc->out_queue)) {
+ if (dc->send_handle != 0) {
+ purple_input_remove(dc->send_handle);
+ dc->send_handle = 0;
+ }
return;
}
- body = g_try_malloc(body_len);
+ p = g_queue_peek_head(dc->out_queue);
+ bytes_to_send = p->length - dc->msg_pos;
- if (body != NULL)
- {
- /* Let's read the data. */
- len = read(directconn->fd, body, body_len);
+ bytes_sent = send(fd, p->data, bytes_to_send, 0);
+ if (bytes_sent < 0) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+ return;
- purple_debug_info("msn", "len=%" G_GSIZE_FORMAT "\n", len);
+ purple_debug_warning("msn", "msn_dc_send_cb: send error\n");
+ msn_dc_destroy(dc);
+ return;
}
- else
- {
- purple_debug_error("msn", "Failed to allocate memory for read\n");
- len = 0;
+
+ dc->progress = TRUE;
+
+ dc->msg_pos += bytes_sent;
+ if (dc->msg_pos == p->length) {
+ if (p->sent_cb != NULL)
+ p->sent_cb(p);
+
+ g_queue_pop_head(dc->out_queue);
+ msn_dc_destroy_packet(p);
+
+ dc->msg_pos = 0;
}
+}
- if (len > 0)
- {
- MsnMessage *msg;
+static void
+msn_dc_enqueue_packet(MsnDirectConn *dc, MsnDirectConnPacket *p)
+{
+ gboolean was_empty;
+
+ was_empty = g_queue_is_empty(dc->out_queue);
+ g_queue_push_tail(dc->out_queue, p);
-#ifdef DEBUG_DC
- str = g_strdup_printf("%s/msntest/r%.4d.bin", g_get_home_dir(), directconn->c);
+ if (was_empty && dc->send_handle == 0) {
+ dc->send_handle = purple_input_add(dc->fd, PURPLE_INPUT_WRITE, msn_dc_send_cb, dc);
+ msn_dc_send_cb(dc, dc->fd, PURPLE_INPUT_WRITE);
+ }
+}
- FILE *tf = g_fopen(str, "w");
- fwrite(body, 1, len, tf);
- fclose(tf);
+static void
+msn_dc_send_foo(MsnDirectConn *dc)
+{
+ MsnDirectConnPacket *p;
+
+ purple_debug_info("msn", "msn_dc_send_foo\n");
+
+ g_return_if_fail(dc != NULL);
- g_free(str);
-#endif
+ p = msn_dc_new_packet();
- directconn->c++;
+ p->length = 8;
+ p->data = (guchar*)g_strdup("\4\0\0\0foo");
+ p->sent_cb = NULL;
- msg = msn_message_new_msnslp();
- msn_message_parse_slp_body(msg, body, body_len);
+ msn_dc_enqueue_packet(dc, p);
+}
- purple_debug_info("msn", "directconn: process_msg\n");
- msn_slplink_process_msg(directconn->slplink, msg);
- }
- else
- {
- /* ERROR */
- purple_debug_error("msn", "error reading\n");
+static void
+msn_dc_send_handshake(MsnDirectConn *dc)
+{
+ MsnDirectConnPacket *p;
+ gchar *h;
+ guint32 l;
- if (directconn->inpa)
- purple_input_remove(directconn->inpa);
+ g_return_if_fail(dc != NULL);
- close(directconn->fd);
+ p = msn_dc_new_packet();
- msn_directconn_destroy(directconn);
- }
+ p->length = 4 + DC_PACKET_HEADER_SIZE;
+ p->data = g_malloc(p->length);
- g_free(body);
+ l = DC_PACKET_HEADER_SIZE;
+ l = GUINT32_TO_LE(l);
+ memcpy(p->data, &l, 4);
+
+ dc->header.session_id = 0;
+ dc->header.id = dc->slpcall->slplink->slp_seq_id++;
+ dc->header.offset = 0;
+ dc->header.total_size = 0;
+ dc->header.length = 0;
+ dc->header.flags = 0x100;
+
+ h = msn_dc_serialize_binary_header(dc);
+ memcpy(p->data + 4, h, DC_PACKET_HEADER_SIZE);
+ memcpy(p->data + 4 + DC_ACK_ID_OFFS, dc->nonce, 16);
+
+ msn_dc_enqueue_packet(dc, p);
}
static void
-connect_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_dc_send_handshake_reply(MsnDirectConn *dc)
+{
+ MsnDirectConnPacket *p;
+ gchar *h;
+ guint32 l;
+
+ g_return_if_fail(dc != NULL);
+
+ p = msn_dc_new_packet();
+
+ p->length = 4 + DC_PACKET_HEADER_SIZE;
+ p->data = g_malloc(p->length);
+
+ l = DC_PACKET_HEADER_SIZE;
+ l = GUINT32_TO_LE(l);
+ memcpy(p->data, &l, 4);
+
+ dc->header.id = dc->slpcall->slplink->slp_seq_id++;
+ dc->header.length = 0;
+
+ h = msn_dc_serialize_binary_header(dc);
+ memcpy(p->data + 4, h, DC_PACKET_HEADER_SIZE);
+ memcpy(p->data + 4 + DC_ACK_ID_OFFS, dc->nonce, 16);
+
+ msn_dc_enqueue_packet(dc, p);
+}
+
+static void
+msn_dc_send_packet_cb(MsnDirectConnPacket *p)
{
- MsnDirectConn* directconn;
- int fd;
+ if (p->msg != NULL && p->msg->ack_cb != NULL)
+ p->msg->ack_cb(p->msg, p->msg->ack_data);
+}
- purple_debug_misc("msn", "directconn: connect_cb: %d, %d.\n", source, cond);
+void
+msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg)
+{
+ MsnDirectConnPacket *p = msn_dc_new_packet();
+ guint32 length = msg->body_len + DC_PACKET_HEADER_SIZE;
- directconn = data;
- directconn->connect_data = NULL;
+ p->length = 4 + length;
+ p->data = g_malloc(p->length);
- if (TRUE)
- {
- fd = source;
- }
- else
- {
- struct sockaddr_in client_addr;
- socklen_t client;
- fd = accept (source, (struct sockaddr *)&client_addr, &client);
- }
+ length = GUINT32_TO_LE(length);
+ memcpy(p->data, &length, 4);
+ memcpy(p->data + 4, &msg->msnslp_header, DC_PACKET_HEADER_SIZE);
+ memcpy(p->data + 4 + DC_PACKET_HEADER_SIZE, msg->body, msg->body_len);
- directconn->fd = fd;
+ p->sent_cb = msn_dc_send_packet_cb;
+ p->msg = msg;
+ msn_message_ref(msg);
- if (fd > 0)
- {
- directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, read_cb,
- directconn);
+ msn_dc_enqueue_packet(dc, p);
+}
- if (TRUE)
+static int
+msn_dc_process_packet(MsnDirectConn *dc, guint32 packet_length)
+{
+ g_return_val_if_fail(dc != NULL, DC_PROCESS_ERROR);
+
+ switch (dc->state) {
+ case DC_STATE_CLOSED:
+ break;
+
+ case DC_STATE_FOO: {
+ /* FOO message is always 4 bytes long */
+ if (packet_length != 4 || memcmp(dc->in_buffer, "\4\0\0\0foo", 8) != 0)
+ return DC_PROCESS_FALLBACK;
+
+ dc->state = DC_STATE_HANDSHAKE;
+ break;
+ }
+
+ case DC_STATE_HANDSHAKE: {
+ if (packet_length != DC_PACKET_HEADER_SIZE)
+ return DC_PROCESS_FALLBACK;
+
+ /* TODO: Check! */
+ msn_dc_send_handshake_reply(dc);
+ dc->state = DC_STATE_ESTABILISHED;
+
+ msn_slpcall_session_init(dc->slpcall);
+ dc->slpcall = NULL;
+ break;
+ }
+
+ case DC_STATE_HANDSHAKE_REPLY:
+ /* TODO: Check! */
+ dc->state = DC_STATE_ESTABILISHED;
+
+ msn_slpcall_session_init(dc->slpcall);
+ dc->slpcall = NULL;
+ break;
+
+ case DC_STATE_ESTABILISHED:
+ msn_slplink_process_msg(
+ dc->slplink,
+ &dc->header,
+ dc->in_buffer + 4 + DC_PACKET_HEADER_SIZE,
+ dc->header.length
+ );
+
+ /*
+ if (dc->num_calls == 0) {
+ msn_dc_destroy(dc);
+
+ return DC_PROCESS_CLOSE;
+ }
+ */
+ break;
+#if 0
{
- /* Send foo. */
- msn_directconn_write(directconn, "foo", strlen("foo") + 1);
+ guint64 file_size;
+ int bytes_written;
+ PurpleXfer *xfer;
+ MsnSlpHeader *h = &dc->header;
- /* Send Handshake */
- msn_directconn_send_handshake(directconn);
+ if (packet_length < DC_PACKET_HEADER_SIZE)
+ return DC_TRANSFER_FALLBACK;
+
+ /*
+ * TODO: MSN Messenger 7.0 sends BYE with flags 0x0000000 so we'll get rid of
+ * 0x1000000 bit but file data is always sent with flags 0x1000030 in both
+ * MSN Messenger and Live.*/
+ switch (h->flags) {
+ case 0x0000000:
+ case 0x1000000:
+ msn_dc_send_ack(dc);
+ if (strncmp(dc->buffer, "BYE", 3) == 0) {
+ /* Remote side cancelled the transfer. */
+ purple_xfer_cancel_remote(dc->slpcall->xfer);
+ return DC_TRANSFER_CANCELLED;
+ }
+ break;
+
+ case 0x1000030:
+ /* File data */
+ xfer = dc->slpcall->xfer;
+ file_size = purple_xfer_get_size(xfer);
+
+ /* Packet sanity checks */
+ if ( h->session_id != dc->slpcall->session_id ||
+ h->offset >= file_size ||
+ h->total_size != file_size ||
+ h->length != packet_length - DC_PACKET_HEADER_SIZE ||
+ h->offset + h->length > file_size) {
+
+ purple_debug_warning("msn", "msn_dc_recv_process_packet_cb: packet range check error!\n");
+ purple_xfer_cancel_local(dc->slpcall->xfer);
+ return DC_TRANSFER_CANCELLED;
+ }
+
+ bytes_written = fwrite(dc->buffer, 1, h->length, xfer->dest_fp);
+ if (bytes_written != h->length) {
+ purple_debug_warning("msn", "msn_dc_recv_process_packet_cb: cannot write whole packet to file!\n");
+ purple_xfer_cancel_local(dc->slpcall->xfer);
+ return DC_TRANSFER_CANCELLED;
+ }
+
+ xfer->bytes_sent = (h->offset + h->length);
+ xfer->bytes_remaining = h->total_size - xfer->bytes_sent;
+
+ purple_xfer_update_progress(xfer);
+
+ if (xfer->bytes_remaining == 0) {
+ /* ACK only the last data packet */
+ msn_dc_send_data_ack(dc);
+ purple_xfer_set_completed(xfer, TRUE);
+ dc->state = DC_STATE_BYE;
+ }
+ break;
+ default:
+ /*
+ * TODO: Packet with unknown flags. Should we ACK these?
+ */
+ msn_dc_send_ack(dc);
+
+ purple_debug_warning(
+ "msn",
+ "msn_dc_recv_process_packet_cb: received packet with unknown flags: 0x%08x\n",
+ dc->header.flags
+ );
}
- else
- {
+ break;
}
+
+ case DC_STATE_BYE:
+ /* TODO: Check! */
+ switch (dc->header.flags) {
+ case 0x0000000:
+ case 0x1000000:
+ msn_dc_send_ack(dc);
+ if (strncmp(dc->buffer, "BYE", 3) == 0) {
+ dc->state = DC_STATE_COMPLETED;
+ return DC_TRANSFER_COMPLETED;
+ }
+ break;
+
+ default:
+ /*
+ * TODO: Packet with unknown flags. Should we ACK these?
+ */
+ msn_dc_send_ack(dc);
+ purple_debug_warning(
+ "msn",
+ "msn_dc_recv_process_packet_cb: received packet with unknown flags: 0x%08x\n",
+ dc->header.flags
+ );
+ }
+ break;
+#endif
}
- else
- {
- /* ERROR */
- purple_debug_error("msn", "could not add input\n");
- if (directconn->inpa)
- purple_input_remove(directconn->inpa);
+ return DC_PROCESS_OK;
+}
- close(directconn->fd);
+static void
+msn_dc_recv_cb(gpointer data, gint fd, PurpleInputCondition cond)
+{
+ MsnDirectConn *dc;
+ int free_buf_space;
+ int bytes_received;
+ guint32 packet_length;
+
+ g_return_if_fail(data != NULL);
+ g_return_if_fail(fd != -1);
+
+ dc = data;
+ free_buf_space = dc->in_size - dc->in_pos;
+
+ bytes_received = recv(fd, dc->in_buffer + dc->in_pos, free_buf_space, 0);
+ if (bytes_received < 0) {
+ if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
+ return;
+
+ purple_debug_warning("msn", "msn_dc_recv_cb: recv error\n");
+
+ if(dc->state != DC_STATE_ESTABILISHED)
+ msn_dc_fallback_to_p2p(dc);
+ else
+ msn_dc_destroy(dc);
+ return;
+
+ } else if (bytes_received == 0) {
+ /* EOF. Remote side closed connection. */
+ purple_debug_info("msn", "msn_dc_recv_cb: recv EOF\n");
+
+ if(dc->state != DC_STATE_ESTABILISHED)
+ msn_dc_fallback_to_p2p(dc);
+ else
+ msn_dc_destroy(dc);
+ return;
}
+
+ dc->progress = TRUE;
+
+ dc->in_pos += bytes_received;
+
+ /* Wait for packet length */
+ while (dc->in_pos >= 4) {
+ packet_length = *((guint32*)dc->in_buffer);
+ packet_length = GUINT32_FROM_LE(packet_length);
+
+ if (packet_length > DC_MAX_PACKET_SIZE) {
+ /* Oversized packet */
+ purple_debug_warning("msn", "msn_dc_recv_cb: oversized packet received\n");
+ return;
+ }
+
+ /* Wait for the whole packet to arrive */
+ if (dc->in_pos < 4 + packet_length)
+ return;
+
+ if (dc->state != DC_STATE_FOO) {
+ msn_dc_parse_binary_header(dc);
+ }
+
+ switch (msn_dc_process_packet(dc, packet_length)) {
+ case DC_PROCESS_CLOSE:
+ return;
+
+ case DC_PROCESS_FALLBACK:
+ purple_debug_warning("msn", "msn_dc_recv_cb: packet processing error, fall back to p2p\n");
+ msn_dc_fallback_to_p2p(dc);
+ return;
+
+ }
+
+ if (dc->in_pos > packet_length + 4) {
+ memcpy(dc->in_buffer, dc->in_buffer + 4 + packet_length, dc->in_pos - packet_length - 4);
+ }
+
+ dc->in_pos -= packet_length + 4;
+ }
}
-static void
-directconn_connect_cb(gpointer data, gint source, const gchar *error_message)
+#if 0
+static gboolean
+msn_dc_send_next_packet(MsnDirectConn *dc)
{
- if (error_message)
- purple_debug_error("msn", "Error making direct connection: %s\n", error_message);
+ MsnSlpMessage *msg;
- connect_cb(data, source, PURPLE_INPUT_READ);
+ if(g_queue_is_empty(dc->out_queue))
+ return TRUE;
+
+ msg = g_queue_peek_head(dc->out_queue);
+ msn_slplink_send_msgpart(dc->slplink, msg);
+
+
+
+ PurpleXfer *xfer;
+ int bytes_read;
+
+ g_return_val_if_fail(dc != NULL, FALSE);
+ g_return_val_if_fail(dc->slpcall != NULL, FALSE);
+
+ xfer = dc->slpcall->xfer;
+
+ bytes_read = fread(dc->buffer, 1, DC_MAX_BODY_SIZE, xfer->dest_fp);
+
+ if (bytes_read > 0) {
+ dc->header.session_id = dc->slpcall->session_id;
+ /* Only increment seq. ID before sending BYE */
+ dc->header.id = dc->slpcall->slplink->slp_seq_id;
+ dc->header.offset = xfer->bytes_sent;
+ dc->header.total_size = xfer->size;
+ dc->header.length = bytes_read;
+ dc->header.flags = 0x1000030;
+ dc->header.ack_id = rand() % G_MAXUINT32;
+ dc->header.ack_sub_id = 0;
+ dc->header.ack_size = 0;
+
+ msn_dc_send_packet(dc);
+
+ xfer->bytes_sent += bytes_read;
+ xfer->bytes_remaining -= bytes_read;
+ purple_xfer_update_progress(xfer);
+
+ if (xfer->bytes_remaining == 0) {
+ purple_xfer_set_completed(xfer, TRUE);
+
+ /* Increment seq. ID for the next BYE message */
+ dc->slpcall->slplink->slp_seq_id++;
+ dc->state = DC_STATE_DATA_ACK;
+ }
+
+ } else {
+ /* File read error */
+ purple_xfer_cancel_local(xfer);
+ return FALSE;
+ }
+
+ return TRUE;
}
-gboolean
-msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port)
+static int
+msn_dc_send_process_packet_cb(MsnDirectConn *dc, guint32 packet_length)
{
- MsnSession *session;
+ g_return_val_if_fail(dc != NULL, DC_TRANSFER_CANCELLED);
+
+ switch (dc->state) {
+ case DC_STATE_FOO: {
+ if (packet_length != 4)
+ return DC_TRANSFER_FALLBACK;
- g_return_val_if_fail(directconn != NULL, FALSE);
- g_return_val_if_fail(host != NULL, TRUE);
- g_return_val_if_fail(port > 0, FALSE);
+ if (memcmp(dc->in_buffer, "\4\0\0\0foo", 8) != 0)
+ return DC_TRANSFER_FALLBACK;
- session = directconn->slplink->session;
+ dc->state = DC_STATE_HANDSHAKE;
+ break;
+ }
-#if 0
- if (session->http_method)
- {
- servconn->http_data->gateway_host = g_strdup(host);
+ case DC_STATE_HANDSHAKE: {
+ if (packet_length != DC_PACKET_HEADER_SIZE)
+ return DC_TRANSFER_FALLBACK;
+
+ /* TODO: Check! */
+ msn_dc_send_handshake_reply(dc);
+ dc->state = DC_STATE_TRANSFER;
+
+ purple_xfer_set_request_denied_fnc(dc->slpcall->xfer, msn_dc_xfer_send_cancel);
+ purple_xfer_set_cancel_send_fnc(dc->slpcall->xfer, msn_dc_xfer_send_cancel);
+ purple_xfer_set_end_fnc(dc->slpcall->xfer, msn_dc_xfer_end);
+ purple_xfer_start(dc->slpcall->xfer, -1, NULL, 0);
+ break;
+ }
+
+ case DC_STATE_HANDSHAKE_REPLY:
+ /* TODO: Check! */
+ dc->state = DC_STATE_TRANSFER;
+ break;
+
+ case DC_STATE_TRANSFER: {
+ switch (dc->header.flags) {
+ case 0x0000000:
+ case 0x1000000:
+ msn_dc_send_ack(dc);
+ if (strncmp(dc->buffer, "BYE", 3) == 0) {
+ /* Remote side cancelled the transfer. */
+ purple_xfer_cancel_remote(dc->slpcall->xfer);
+ return DC_TRANSFER_CANCELLED;
+ }
+ break;
+ }
+ break;
+ }
+
+ case DC_STATE_DATA_ACK: {
+ /* TODO: Check! */
+ msn_dc_send_bye(dc);
+ dc->state = DC_STATE_BYE_ACK;
+ break;
+ }
+
+ case DC_STATE_BYE_ACK:
+ /* TODO: Check! */
+ dc->state = DC_STATE_COMPLETED;
+ return DC_TRANSFER_COMPLETED;
}
+
+ return DC_TRANSFER_OK;
+}
#endif
- directconn->connect_data = purple_proxy_connect(NULL, session->account,
- host, port, directconn_connect_cb, directconn);
+static gboolean
+msn_dc_timeout(gpointer data)
+{
+ MsnDirectConn *dc = data;
+
+ g_return_val_if_fail(dc != NULL, FALSE);
- return (directconn->connect_data != NULL);
+ if (dc->progress)
+ dc->progress = FALSE;
+ else
+ msn_dc_destroy(dc);
+
+ return TRUE;
}
+static void
+msn_dc_init(MsnDirectConn *dc)
+{
+ g_return_if_fail(dc != NULL);
+
+ dc->in_size = DC_MAX_PACKET_SIZE + 4;
+ dc->in_pos = 0;
+ dc->in_buffer = g_malloc(dc->in_size);
+
+ dc->recv_handle = purple_input_add(dc->fd, PURPLE_INPUT_READ, msn_dc_recv_cb, dc);
+ dc->send_handle = purple_input_add(dc->fd, PURPLE_INPUT_WRITE, msn_dc_send_cb, dc);
+
+ dc->timeout_handle = purple_timeout_add_seconds(DC_TIMEOUT, msn_dc_timeout, dc);
+}
+
void
-msn_directconn_listen(MsnDirectConn *directconn)
+msn_dc_connected_to_peer_cb(gpointer data, gint fd, const gchar *error_msg)
{
- int port;
- int fd;
+ MsnDirectConn *dc;
- port = 7000;
+ purple_debug_info("msn", "msn_dc_connected_to_peer_cb\n");
+
+ g_return_if_fail(data != NULL);
- for (fd = -1; fd < 0;)
- fd = create_listener(++port);
+ dc = data;
- directconn->fd = fd;
+ dc->connect_data = NULL;
+ purple_timeout_remove(dc->connect_timeout_handle);
+ dc->connect_timeout_handle = 0;
+
+ dc->fd = fd;
+ if (dc->fd != -1) {
+ msn_dc_init(dc);
+ msn_dc_send_foo(dc);
+ msn_dc_send_handshake(dc);
+ dc->state = DC_STATE_HANDSHAKE_REPLY;
+ }
+}
- directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, connect_cb,
- directconn);
+/*
+ * This callback will be called when we're the server
+ * and nobody has connected us in DC_CONNECT_TIMEOUT seconds
+ */
+static gboolean
+msn_dc_incoming_connection_timeout_cb(gpointer data) {
+ MsnDirectConn *dc = data;
+ MsnSlpCall *slpcall = dc->slpcall;
+
+ purple_debug_info("msn", "msn_dc_incoming_connection_timeout_cb\n");
+
+ dc = data;
+ g_return_val_if_fail(dc != NULL, FALSE);
- directconn->port = port;
- directconn->c = 0;
+ slpcall = dc->slpcall;
+ g_return_val_if_fail(slpcall != NULL, FALSE);
+
+ if (dc->listen_data != NULL) {
+ purple_network_listen_cancel(dc->listen_data);
+ dc->listen_data = NULL;
+ }
+
+ if (dc->listenfd_handle != 0) {
+ purple_timeout_remove(dc->listenfd_handle);
+ dc->listenfd_handle = 0;
+ }
+
+ if (dc->listenfd != -1) {
+ purple_network_remove_port_mapping(dc->listenfd);
+ close(dc->listenfd);
+ dc->listenfd = -1;
+ }
+
+ msn_dc_destroy(dc);
+ /* Start p2p file transfer */
+ msn_slpcall_session_init(slpcall);
+
+ return FALSE;
}
-MsnDirectConn*
-msn_directconn_new(MsnSlpLink *slplink)
+/*
+ * This callback will be called when we're unable to connect to
+ * the remote host in DC_CONNECT_TIMEOUT seconds.
+ */
+gboolean
+msn_dc_outgoing_connection_timeout_cb(gpointer data)
{
- MsnDirectConn *directconn;
+ MsnDirectConn *dc = data;
+
+ purple_debug_info("msn", "msn_dc_outgoing_connection_timeout_cb\n");
- directconn = g_new0(MsnDirectConn, 1);
+ g_return_val_if_fail(dc != NULL, FALSE);
+
+ if (dc->connect_timeout_handle != 0) {
+ purple_timeout_remove(dc->connect_timeout_handle);
+ dc->connect_timeout_handle = 0;
+ }
- directconn->slplink = slplink;
+ if (dc->connect_data != NULL) {
+ purple_proxy_connect_cancel(dc->connect_data);
+ dc->connect_data = NULL;
+ }
- if (slplink->directconn != NULL)
- purple_debug_info("msn", "got_transresp: LEAK\n");
+ if (dc->ext_ip && dc->ext_port) {
+ /* Try external IP/port if available. */
+ dc->connect_data = purple_proxy_connect(
+ NULL,
+ dc->slpcall->slplink->session->account,
+ dc->ext_ip,
+ dc->ext_port,
+ msn_dc_connected_to_peer_cb,
+ dc
+ );
- slplink->directconn = directconn;
+ g_free(dc->ext_ip);
+ dc->ext_ip = NULL;
- return directconn;
+ if (dc->connect_data) {
+ dc->connect_timeout_handle = purple_timeout_add_seconds(
+ DC_CONNECT_TIMEOUT,
+ msn_dc_outgoing_connection_timeout_cb,
+ dc
+ );
+ }
+
+ } else {
+ /*
+ * Both internal and external connection attempts are failed.
+ * Fall back to p2p transfer.
+ */
+ MsnSlpCall *slpcall = dc->slpcall;
+
+ msn_dc_destroy(dc);
+ /* Start p2p file transfer */
+ msn_slpcall_session_init(slpcall);
+ }
+
+ return FALSE;
}
+/*
+ * This callback will be called when we're the server
+ * and somebody has connected to us in DC_CONNECT_TIMEOUT seconds.
+ */
+static void
+msn_dc_incoming_connection_cb(gpointer data, gint listenfd, PurpleInputCondition cond)
+{
+ MsnDirectConn *dc = data;
+
+ purple_debug_info("msn", "msn_dc_incoming_connection_cb\n");
+
+ g_return_if_fail(dc != NULL);
+
+ if (dc->connect_timeout_handle != 0) {
+ purple_timeout_remove(dc->connect_timeout_handle);
+ dc->connect_timeout_handle = 0;
+ }
+
+ if (dc->listenfd_handle != 0) {
+ purple_input_remove(dc->listenfd_handle);
+ dc->listenfd_handle = 0;
+ }
+
+ dc->fd = accept(listenfd, NULL, 0);
+
+ purple_network_remove_port_mapping(dc->listenfd);
+ close(dc->listenfd);
+ dc->listenfd = -1;
+
+ if (dc->fd != -1) {
+ msn_dc_init(dc);
+ dc->state = DC_STATE_FOO;
+ }
+}
+
void
-msn_directconn_destroy(MsnDirectConn *directconn)
+msn_dc_listen_socket_created_cb(int listenfd, gpointer data)
{
- if (directconn->connect_data != NULL)
- purple_proxy_connect_cancel(directconn->connect_data);
+ MsnDirectConn *dc = data;
- if (directconn->inpa != 0)
- purple_input_remove(directconn->inpa);
+ purple_debug_info("msn", "msn_dc_listen_socket_created_cb\n");
+
+ g_return_if_fail(dc != NULL);
+
+ dc->listen_data = NULL;
- if (directconn->fd >= 0)
- close(directconn->fd);
+ if (listenfd != -1) {
+ const char *ext_ip;
+ const char *int_ip;
+ int port;
- if (directconn->nonce != NULL)
- g_free(directconn->nonce);
+ ext_ip = purple_network_get_my_ip(listenfd);
+ int_ip = purple_network_get_local_system_ip(listenfd);
+ port = purple_network_get_port_from_fd(listenfd);
- directconn->slplink->directconn = NULL;
+ dc->listenfd = listenfd;
+ dc->listenfd_handle = purple_input_add(
+ listenfd,
+ PURPLE_INPUT_READ,
+ msn_dc_incoming_connection_cb,
+ dc
+ );
+ dc->connect_timeout_handle = purple_timeout_add_seconds(
+ DC_CONNECT_TIMEOUT * 2, /* Internal + external connection attempts */
+ msn_dc_incoming_connection_timeout_cb,
+ dc
+ );
- g_free(directconn);
+ if (strcmp(int_ip, ext_ip) != 0) {
+ dc->msg_body = g_strdup_printf(
+ "Bridge: TCPv1\r\n"
+ "Listening: true\r\n"
+ "Hashed-Nonce: {%s}\r\n"
+ "IPv4External-Addrs: %s\r\n"
+ "IPv4External-Port: %d\r\n"
+ "IPv4Internal-Addrs: %s\r\n"
+ "IPv4Internal-Port: %d\r\n"
+ "\r\n",
+
+ dc->nonce_hash,
+ ext_ip,
+ port,
+ int_ip,
+ port
+ );
+
+ } else {
+ dc->msg_body = g_strdup_printf(
+ "Bridge: TCPv1\r\n"
+ "Listening: true\r\n"
+ "Hashed-Nonce: {%s}\r\n"
+ "IPv4External-Addrs: %s\r\n"
+ "IPv4External-Port: %d\r\n"
+ "\r\n",
+
+ dc->nonce_hash,
+ ext_ip,
+ port
+ );
+ }
+
+ if (dc->slpcall->wait_for_socket) {
+ if (dc->send_connection_info_msg_cb != NULL)
+ dc->send_connection_info_msg_cb(dc);
+
+ dc->slpcall->wait_for_socket = FALSE;
+ }
+ }
}
+
============================================================
--- libpurple/protocols/msn/directconn.h ef7f82578f19c35b65685ac763f5f0dcfbc3dca5
+++ libpurple/protocols/msn/directconn.h fe91c3030d03074aa9e290e96455e565a1e5a8bc
@@ -26,36 +26,153 @@ typedef struct _MsnDirectConn MsnDirectC
typedef struct _MsnDirectConn MsnDirectConn;
+#include "network.h"
+#include "proxy.h"
+#include "circbuffer.h"
+
#include "msg.h"
#include "slp.h"
#include "slplink.h"
+#include "slpmsg.h"
+typedef enum
+{
+ DC_STATE_CLOSED, /*< No socket opened yet */
+ DC_STATE_FOO, /*< Waiting for FOO message */
+ DC_STATE_HANDSHAKE, /*< Waiting for handshake message */
+ DC_STATE_HANDSHAKE_REPLY, /*< Waiting for handshake reply message */
+ DC_STATE_ESTABILISHED /*< Handshake complete */
+} MsnDirectConnState;
+
+typedef enum
+{
+ DC_PROCESS_OK = 0,
+ DC_PROCESS_ERROR,
+ DC_PROCESS_FALLBACK,
+ DC_PROCESS_CLOSE
+
+} MsnDirectConnProcessResult;
+
+typedef struct _MsnDirectConnPacket MsnDirectConnPacket;
+
+struct _MsnDirectConnPacket {
+ guint32 length;
+ guchar *data;
+
+ void (*sent_cb)(struct _MsnDirectConnPacket*);
+ MsnMessage *msg;
+};
+
struct _MsnDirectConn
{
- MsnSlpLink *slplink;
- MsnSlpCall *initial_call;
+ MsnDirectConnState state; /**< Direct connection status */
+ MsnSlpLink *slplink; /**< The slplink using this direct connection */
+ MsnSlpCall *slpcall; /**< The slpcall which initiated the direct connection */
+ char *msg_body; /**< The body of message sent by send_connection_info_msg_cb */
+ MsnSlpMessage *prev_ack; /**< The saved SLP ACK message */
- PurpleProxyConnectData *connect_data;
+ guchar nonce[16]; /**< The nonce used for direct connection handshake */
+ gchar nonce_hash[37]; /**< The hash of nonce */
- gboolean acked;
+ PurpleNetworkListenData *listen_data; /**< The pending socket creation request */
+ PurpleProxyConnectData *connect_data; /**< The pending connection attempt */
+ int listenfd; /**< The socket we're listening for incoming connections */
+ guint listenfd_handle; /**< The timeout handle for incoming connection */
+ guint connect_timeout_handle; /**< The timeout handle for outgoing connection */
- char *nonce;
+ int fd; /**< The direct connection socket */
+ guint recv_handle; /**< The incoming data callback handle */
+ guint send_handle; /**< The outgoing data callback handle */
- int fd;
+ gchar *in_buffer; /**< The receive buffer */
+ int in_size; /**< The receive buffer size */
+ int in_pos; /**< The first free position in receive buffer */
+ GQueue *out_queue; /**< The outgoing packet queue */
+ int msg_pos; /**< The position of next byte to be sent in the actual packet */
- int port;
- int inpa;
+ MsnSlpHeader header; /**< SLP header for parsing / serializing */
+
+ /**< The callback used for sending information to the peer about the opened scoket */
+ void (*send_connection_info_msg_cb)(struct _MsnDirectConn*);
- int c;
+ gchar *ext_ip; /**< Our external IP address */
+ int ext_port; /**< Our external port */
+
+ guint timeout_handle;
+ gboolean progress;
+
+ //int num_calls; /**< The number of slpcalls using this direct connection */
};
-MsnDirectConn *msn_directconn_new(MsnSlpLink *slplink);
-gboolean msn_directconn_connect(MsnDirectConn *directconn,
- const char *host, int port);
-void msn_directconn_listen(MsnDirectConn *directconn);
-void msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg);
-void msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce);
-void msn_directconn_destroy(MsnDirectConn *directconn);
-void msn_directconn_send_handshake(MsnDirectConn *directconn);
+#define DC_CONNECT_TIMEOUT 5
+#define DC_TIMEOUT 60
+/*
+ * Queues an MSN message to be sent via direct connection.
+ */
+void
+msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg);
+
+/*
+ * Creates initializes and returns a new MsnDirectConn structure.
+ */
+MsnDirectConn*
+msn_dc_new(MsnSlpCall *slplink);
+
+/*
+ * Destroys an MsnDirectConn structure. Frees every buffer allocated earlier
+ * restores saved callbacks, etc.
+ */
+void
+msn_dc_destroy(MsnDirectConn *dc);
+
+/*
+ * Increases the slpcall counter in DC. The direct connection remains open
+ * until all slpcalls using it are destroyed.
+ */
+void
+msn_dc_ref(MsnDirectConn *dc);
+
+/*
+ * Decrease the slpcall counter in DC. The direct connection remains open
+ * until all slpcalls using it are destroyed.
+ */
+void
+msn_dc_unref(MsnDirectConn *dc);
+
+/*
+ * Sends a direct connect INVITE message on the associated slplink
+ * with the corresponding connection type and information.
+ */
+void
+msn_dc_send_invite(MsnDirectConn *dc);
+
+/*
+ * Sends a direct connect OK message as a response to an INVITE received earliaer
+ * on the corresponding slplink.
+ */
+void
+msn_dc_send_ok(MsnDirectConn *dc);
+
+/*
+ * This callback will be called when we're successfully connected to
+ * the remote host.
+ */
+void
+msn_dc_connected_to_peer_cb(gpointer data, gint fd, const gchar *error_msg);
+
+/*
+ * This callback will be called when we're unable to connect to
+ * the remote host in DC_CONNECT_TIMEOUT seconds.
+ */
+gboolean
+msn_dc_outgoing_connection_timeout_cb(gpointer data);
+
+/*
+ * This callback will be called when the listening socket is successfully
+ * created and it's parameters (IP/port) are available.
+ */
+void
+msn_dc_listen_socket_created_cb(int listenfd, gpointer data);
+
#endif /* MSN_DIRECTCONN_H */
============================================================
--- libpurple/protocols/msn/msg.c cc85e31a1d1aba5b7a1ed9b76ec7f5350619cd1b
+++ libpurple/protocols/msn/msg.c 7a21a44bb3048cce025a944c7dabb940cef5ee18
@@ -1100,7 +1100,8 @@ msn_invite_msg(MsnCmdProc *cmdproc, MsnM
msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
GHashTable *body;
- const gchar *guid;
+ const gchar *command;
+ const gchar *cookie;
gboolean accepted = FALSE;
g_return_if_fail(cmdproc != NULL);
@@ -1113,59 +1114,75 @@ msn_invite_msg(MsnCmdProc *cmdproc, MsnM
"Unable to parse invite msg body.\n");
return;
}
+
+ /*
+ * GUID is NOT always present but Invitation-Command and Invitation-Cookie
+ * are mandatory.
+ */
+ command = g_hash_table_lookup(body, "Invitation-Command");
+ cookie = g_hash_table_lookup(body, "Invitation-Cookie");
- guid = g_hash_table_lookup(body, "Application-GUID");
+ if (command == NULL || cookie == NULL) {
+ purple_debug_warning("msn",
+ "Invalid invitation message: "
+ "either Invitation-Command or Invitation-Cookie is missing or invaild"
+ );
+ return;
- if (guid == NULL) {
- const gchar *cmd = g_hash_table_lookup(
- body, "Invitation-Command");
+ } else if (!strcmp(command, "INVITE")) {
- if (cmd && !strcmp(cmd, "CANCEL")) {
- const gchar *code = g_hash_table_lookup(
- body, "Cancel-Code");
- purple_debug_info("msn",
- "MSMSGS invitation cancelled: %s.\n",
- code ? code : "no reason given");
- } else
- purple_debug_warning("msn", "Invite msg missing "
- "Application-GUID.\n");
+ const gchar *guid = g_hash_table_lookup(body, "Application-GUID");
+
+ if (guid == NULL) {
+ const gchar *cmd = g_hash_table_lookup(
+ body, "Invitation-Command");
- accepted = TRUE;
+ if (cmd && !strcmp(cmd, "CANCEL")) {
+ const gchar *code = g_hash_table_lookup(
+ body, "Cancel-Code");
+ purple_debug_info("msn",
+ "MSMSGS invitation cancelled: %s.\n",
+ code ? code : "no reason given");
+ } else
+ purple_debug_warning("msn", "Invite msg missing "
+ "Application-GUID.\n");
- } else if (!strcmp(guid, "{02D3C01F-BF30-4825-A83A-DE7AF41648AA}")) {
- purple_debug_info("msn", "Computer call\n");
+ accepted = TRUE;
- if (cmdproc->session) {
- PurpleConversation *conv = NULL;
- gchar *from = msg->remote_user;
- gchar *buf = NULL;
+ } else if (!strcmp(guid, MSN_FT_GUID)) {
- if (from)
- conv = purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_IM, from,
- cmdproc->session->account);
- if (conv)
- buf = g_strdup_printf(
- _("%s sent you a voice chat "
- "invite, which is not yet "
- "supported."), from);
- if (buf) {
- purple_conversation_write(conv, NULL, buf,
- PURPLE_MESSAGE_SYSTEM |
- PURPLE_MESSAGE_NOTIFY,
- time(NULL));
- g_free(buf);
+ } else if (!strcmp(guid, "{02D3C01F-BF30-4825-A83A-DE7AF41648AA}")) {
+ purple_debug_info("msn", "Computer call\n");
+
+ if (cmdproc->session) {
+ PurpleConversation *conv = NULL;
+ gchar *from = msg->remote_user;
+ gchar *buf = NULL;
+
+ if (from)
+ conv = purple_find_conversation_with_account(
+ PURPLE_CONV_TYPE_IM, from,
+ cmdproc->session->account);
+ if (conv)
+ buf = g_strdup_printf(
+ _("%s sent you a voice chat "
+ "invite, which is not yet "
+ "supported."), from);
+ if (buf) {
+ purple_conversation_write(conv, NULL, buf,
+ PURPLE_MESSAGE_SYSTEM |
+ PURPLE_MESSAGE_NOTIFY,
+ time(NULL));
+ g_free(buf);
+ }
}
+ } else {
+ const gchar *application = g_hash_table_lookup(body, "Application-Name");
+ purple_debug_warning("msn", "Unhandled invite msg with GUID %s: %s.\n",
+ guid, application ? application : "(null)");
}
- } else {
- const gchar *application = g_hash_table_lookup(body, "Application-Name");
- purple_debug_warning("msn", "Unhandled invite msg with GUID %s: %s.\n",
- guid, application ? application : "(null)");
- }
-
- if (!accepted) {
- const gchar *cookie = g_hash_table_lookup(body, "Invitation-Cookie");
- if (cookie) {
+
+ if (!accepted) {
MsnSwitchBoard *swboard = cmdproc->data;
char *text;
MsnMessage *cancel;
@@ -1176,15 +1193,21 @@ msn_invite_msg(MsnCmdProc *cmdproc, MsnM
msn_message_set_flag(cancel, 'U');
text = g_strdup_printf("Invitation-Command: CANCEL\r\n"
- "Invitation-Cookie: %s\r\n"
- "Cancel-Code: REJECT_NOT_INSTALLED\r\n",
- cookie);
+ "Invitation-Cookie: %s\r\n"
+ "Cancel-Code: REJECT_NOT_INSTALLED\r\n",
+ cookie);
msn_message_set_bin_data(cancel, text, strlen(text));
g_free(text);
msn_switchboard_send_msg(swboard, cancel, TRUE);
msn_message_destroy(cancel);
}
+
+ } else {
+ /*
+ * Some other already estabilished invitation session.
+ * Can be retrieved by Invitation-Cookie.
+ */
}
g_hash_table_destroy(body);
============================================================
--- libpurple/protocols/msn/slp.c 8347895b2576ff2044f0fe63e29f0e464084bf83
+++ libpurple/protocols/msn/slp.c 3dfabff8586552838cdc43b2448ac52f7cd7a9aa
@@ -25,24 +25,28 @@
#include "slp.h"
#include "slpcall.h"
#include "slpmsg.h"
+#include "msnutils.h"
#include "object.h"
#include "user.h"
#include "switchboard.h"
+#include "directconn.h"
#include "smiley.h"
/* ms to delay between sending buddy icon requests to the server. */
#define BUDDY_ICON_DELAY 20
-static void send_ok(MsnSlpCall *slpcall, const char *branch,
+/*
+static void msn_slp_send_ok(MsnSlpCall *slpcall, const char *branch,
const char *type, const char *content);
-static void send_decline(MsnSlpCall *slpcall, const char *branch,
+static void msn_slp_send_decline(MsnSlpCall *slpcall, const char *branch,
const char *type, const char *content);
-
+*/
static void request_user_display(MsnUser *user);
+
/**************************************************************************
* Util
**************************************************************************/
@@ -91,7 +95,7 @@ msn_xfer_init(PurpleXfer *xfer)
content = g_strdup_printf("SessionID: %lu\r\n\r\n",
slpcall->session_id);
- send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
+ msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
content);
g_free(content);
@@ -109,6 +113,7 @@ msn_xfer_cancel(PurpleXfer *xfer)
slpcall = xfer->data;
+
if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
{
if (slpcall->started)
@@ -120,7 +125,7 @@ msn_xfer_cancel(PurpleXfer *xfer)
content = g_strdup_printf("SessionID: %lu\r\n\r\n",
slpcall->session_id);
- send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
+ msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody",
content);
g_free(content);
@@ -185,6 +190,8 @@ msn_xfer_end_cb(MsnSlpCall *slpcall, Msn
void
msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session)
{
+ purple_debug_info("msn", "msn_xfer_end_cb\n");
+
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))
@@ -198,6 +205,7 @@ msn_xfer_completed_cb(MsnSlpCall *slpcal
gsize size)
{
PurpleXfer *xfer = slpcall->xfer;
+
purple_xfer_set_completed(xfer, TRUE);
purple_xfer_end(xfer);
}
@@ -234,8 +242,8 @@ got_transresp(MsnSlpCall *slpcall, const
}
#endif
-static void
-send_ok(MsnSlpCall *slpcall, const char *branch,
+void
+msn_slp_send_ok(MsnSlpCall *slpcall, const char *branch,
const char *type, const char *content)
{
MsnSlpLink *slplink;
@@ -253,11 +261,18 @@ send_ok(MsnSlpCall *slpcall, const char
msn_slplink_queue_slpmsg(slplink, slpmsg);
- msn_slpcall_session_init(slpcall);
+ /*
+ * TODO: Removed because it interferes with
+ * direct file transfer. If we're sending some file
+ * then this call initiates a p2p file transfer which is
+ * undesirable.
+ */
+
+ /* msn_slpcall_session_init(slpcall); */
}
-static void
-send_decline(MsnSlpCall *slpcall, const char *branch,
+void
+msn_slp_send_decline(MsnSlpCall *slpcall, const char *branch,
const char *type, const char *content)
{
MsnSlpLink *slplink;
@@ -308,6 +323,119 @@ find_valid_emoticon(PurpleAccount *accou
return NULL;
}
+static gboolean
+msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content)
+{
+ /* A direct connection negotiation response */
+ char *bridge;
+ MsnDirectConn *dc = slpcall->slplink->dc;
+
+ purple_debug_info("msn", "process_transresp\n");
+
+ g_return_val_if_fail(dc != NULL, FALSE);
+ g_return_val_if_fail(dc->state == DC_STATE_CLOSED, FALSE);
+
+ bridge = get_token(content, "Bridge: ", "\r\n");
+ if(bridge && strcmp(bridge, "TCPv1") == 0) {
+ /* Ok, the client supports direct TCP connection */
+
+ if (dc->listen_data != NULL || dc->listenfd != -1) {
+ 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 {
+ /* The listening socket is ready. Send the INVITE here. */
+ msn_dc_send_invite(dc);
+ }
+
+ return TRUE;
+
+ } else {
+ /*
+ * We should connect to the client so parse
+ * IP/port from response.
+ */
+ char *ip, *port_str;
+ int port = 0;
+
+ /* 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_CONNECT_TIMEOUT,
+ msn_dc_outgoing_connection_timeout_cb,
+ dc
+ );
+ return TRUE;
+
+ } else {
+ /*
+ * Connection failed
+ * Try external IP/port (if specified)
+ */
+ msn_dc_outgoing_connection_timeout_cb(dc);
+ return TRUE;
+ }
+
+ g_free(ip);
+
+ } else {
+ /*
+ * Omitted or invalid internal IP address / port
+ * Try external IP/port (if specified)
+ */
+ msn_dc_outgoing_connection_timeout_cb(dc);
+ return TRUE;
+ }
+
+ if (ip)
+ g_free(ip);
+ }
+
+ } else {
+ /*
+ * Invalid direct connect invitation or
+ * TCP connection is not supported
+ */
+ }
+
+ if (bridge)
+ g_free(bridge);
+
+ return FALSE;
+}
+
static void
got_sessionreq(MsnSlpCall *slpcall, const char *branch,
const char *euf_guid, const char *context)
@@ -330,7 +458,7 @@ got_sessionreq(MsnSlpCall *slpcall, cons
content = g_strdup_printf("SessionID: %lu\r\n\r\n",
slpcall->session_id);
- send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
+ msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody",
content);
g_free(content);
@@ -479,7 +607,7 @@ got_sessionreq(MsnSlpCall *slpcall, cons
if (!accepted) {
char *content = g_strdup_printf("SessionID: %lu\r\n\r\n",
slpcall->session_id);
- send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content);
+ msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content);
g_free(content);
}
}
@@ -548,67 +676,75 @@ got_invite(MsnSlpCall *slpcall,
}
else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
{
- /* A direct connection? */
+ /* A direct connection negotiation request */
+ char *bridges;
+
+ purple_debug_info("msn", "got_invite: transreqbody received\n");
+
+ g_return_if_fail(slpcall->xfer != NULL);
- char *listening, *nonce;
- char *content;
+ /* Don't do anything if we already have a direct connection */
+ g_return_if_fail(slpcall->slplink->dc == NULL);
+
+ bridges = get_token(content, "Bridges: ", "\r\n");
+ if(bridges && strstr(bridges, "TCPv1") != NULL) {
+ /*
+ * Ok, the client supports direct TCP connection
+ * Try to create a listening port
+ */
+ char *content;
+ MsnDirectConn *dc;
- if (FALSE)
- {
-#if 0
- MsnDirectConn *directconn;
- /* const char *ip_addr; */
- char *ip_port;
- int port;
+ dc = msn_dc_new(slpcall);
- /* ip_addr = purple_prefs_get_string("/purple/ft/public_ip"); */
- ip_port = "5190";
- listening = "true";
- nonce = rand_guid();
+ dc->listen_data = purple_network_listen_range(
+ 0, 0,
+ SOCK_STREAM,
+ msn_dc_listen_socket_created_cb,
+ dc
+ );
- directconn = msn_directconn_new(slplink);
+ if (dc->listen_data == NULL) {
+ /* Listen socket creation failed */
+
+ purple_debug_info("msn", "got_invite: listening failed\n");
+
+ content = g_strdup(
+ "Bridge: TCPv1\r\n"
+ "Listening: false\r\n"
+ "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
+ "\r\n"
+ );
+ msn_slp_send_ok(slpcall, branch,
+ "application/x-msnmsgr-transrespbody", content);
+ g_free(content);
- /* msn_directconn_parse_nonce(directconn, nonce); */
- directconn->nonce = g_strdup(nonce);
+ } 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;
+ }
- msn_directconn_listen(directconn);
-
- port = directconn->port;
-
- content = g_strdup_printf(
- "Bridge: TCPv1\r\n"
- "Listening: %s\r\n"
- "Nonce: {%s}\r\n"
- "Ipv4Internal-Addrs: 192.168.0.82\r\n"
- "Ipv4Internal-Port: %d\r\n"
- "\r\n",
- listening,
- nonce,
- port);
-#endif
+ } else {
+ /*
+ * Invalid direct connect invitation or
+ * TCP connection is not supported.
+ */
}
- else
- {
- listening = "false";
- nonce = g_strdup("00000000-0000-0000-0000-000000000000");
-
- content = g_strdup_printf(
- "Bridge: TCPv1\r\n"
- "Listening: %s\r\n"
- "Nonce: {%s}\r\n"
- "\r\n",
- listening,
- nonce);
- }
-
- send_ok(slpcall, branch,
- "application/x-msnmsgr-transrespbody", content);
-
- g_free(content);
- g_free(nonce);
}
else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
{
+ /* A direct connection negotiation response */
+ g_return_if_fail(slpcall->xfer != NULL);
+
+ msn_slp_process_transresp(slpcall, content);
#if 0
char *ip_addrs;
char *temp;
@@ -646,6 +782,86 @@ got_ok(MsnSlpCall *slpcall,
if (!strcmp(type, "application/x-msnmsgr-sessionreqbody"))
{
+ char *content;
+ char *header;
+ MsnSlpMessage *msg;
+ MsnDirectConn *dc;
+
+ g_return_if_fail(slpcall->xfer != NULL);
+
+ if(slpcall->slplink->dc != NULL) {
+ /*
+ * If we already have an estabilished direct connection
+ * then just start the 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->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"
+ "Hashed-Nonce: {%s}\r\n"
+ "\r\n",
+
+ rand() % G_MAXUINT32,
+ dc->nonce_hash
+ );
+
+ } 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"
+ "Hashed-Nonce: {%s}\r\n"
+ "\r\n",
+
+ dc->nonce_hash
+ );
+ }
+
+ msg = msn_slpmsg_sip_new(
+ slpcall,
+ 0,
+ header,
+ slpcall->branch,
+ "application/x-msnmsgr-transreqbody",
+ content
+ );
+ g_free(header);
+ g_free(content);
+
+ msn_slplink_queue_slpmsg(slpcall->slplink, msg);
#if 0
if (slpcall->type == MSN_SLPCALL_DC)
{
@@ -690,7 +906,12 @@ got_ok(MsnSlpCall *slpcall,
msn_slpcall_session_init(slpcall);
}
#else
- msn_slpcall_session_init(slpcall);
+ /*
+ * Removed because it messes up direct connection by
+ * starting p2p transfer
+ */
+
+ /* msn_slpcall_session_init(slpcall); */
#endif
}
else if (!strcmp(type, "application/x-msnmsgr-transreqbody"))
@@ -700,6 +921,7 @@ got_ok(MsnSlpCall *slpcall,
}
else if (!strcmp(type, "application/x-msnmsgr-transrespbody"))
{
+ msn_slp_process_transresp(slpcall, content);
#if 0
char *ip_addrs;
char *temp;
@@ -767,11 +989,25 @@ msn_slp_sip_recv(MsnSlpLink *slplink, co
content = get_token(body, "\r\n\r\n", NULL);
- if (branch && call_id && content_type && content)
+ if (branch && call_id)
{
- slpcall = msn_slpcall_new(slplink);
- slpcall->id = call_id;
- got_invite(slpcall, branch, content_type, content);
+ slpcall = msn_slplink_find_slp_call(slplink, call_id);
+ if (slpcall)
+ {
+ g_free(slpcall->branch);
+ slpcall->branch = g_strdup(branch);
+ }
+ else if (content_type && content)
+ {
+ slpcall = msn_slpcall_new(slplink);
+ slpcall->id = call_id;
+ got_invite(slpcall, branch, content_type, content);
+ }
+ else
+ {
+ g_free(call_id);
+ slpcall = NULL;
+ }
}
else
{
@@ -860,6 +1096,8 @@ msn_p2p_msg(MsnCmdProc *cmdproc, MsnMess
{
MsnSession *session;
MsnSlpLink *slplink;
+ const char *data;
+ gsize len;
session = cmdproc->servconn->session;
slplink = msn_session_get_slplink(session, msg->remote_user);
@@ -882,7 +1120,9 @@ msn_p2p_msg(MsnCmdProc *cmdproc, MsnMess
}
}
- msn_slplink_process_msg(slplink, msg);
+ data = msn_message_get_bin_data(msg, &len);
+
+ msn_slplink_process_msg(slplink, &msg->msnslp_header, data, len);
}
static void
============================================================
--- libpurple/protocols/msn/slp.h e93fe47209aa2cfc90cf4b618212471d1b2bd94d
+++ libpurple/protocols/msn/slp.h 5adea01512320beeb7891227b7fbcea1f1abf58e
@@ -51,7 +51,15 @@ MsnSlpCall * msn_slp_sip_recv(MsnSlpLink
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);
+
+void
+msn_slp_send_decline(MsnSlpCall *slpcall, const char *branch,
+ const char *type, const char *content);
+
void send_bye(MsnSlpCall *slpcall, const char *type);
void msn_xfer_completed_cb(MsnSlpCall *slpcall,
============================================================
--- libpurple/protocols/msn/slpcall.c 6bc12efafffec9f0cf74910ba6be61eb65167bbb
+++ libpurple/protocols/msn/slpcall.c 23a48bf6e6cdde0cc800464339f6d2e3a55316f3
@@ -66,6 +66,10 @@ msn_slpcall_new(MsnSlpLink *slplink)
slpcall->slplink = slplink;
+ slpcall->wait_for_socket = FALSE;
+ slpcall->xfer = NULL;
+ slpcall->branch = NULL;
+
msn_slplink_add_slpcall(slplink, slpcall);
slpcall->timer = purple_timeout_add_seconds(MSN_SLPCALL_TIMEOUT, msn_slpcall_timeout, slpcall);
============================================================
--- libpurple/protocols/msn/slpcall.h 5f42b8ecb787d6d17126d372d175636f179d11dc
+++ libpurple/protocols/msn/slpcall.h a5db5e34a1b286e11946ebec6ddb0964e18532b7
@@ -64,13 +64,15 @@ struct _MsnSlpCall
gboolean started; /**< A flag that states if this slpcall's session has
been initiated. */
+ gboolean wait_for_socket;
+
void (*progress_cb)(MsnSlpCall *slpcall,
gsize total_length, gsize len, gsize offset);
void (*session_init_cb)(MsnSlpCall *slpcall);
/* Can be checksum, or smile */
char *data_info;
-
+
PurpleXfer *xfer;
union {
GByteArray *incoming_data;
============================================================
--- libpurple/protocols/msn/slplink.c d4aefb42cb2a3334a2dda05c1e016848f7484141
+++ libpurple/protocols/msn/slplink.c ffcac100b1646a9a73fa14dff81273f6c0b0e285
@@ -155,11 +155,21 @@ msn_slplink_add_slpcall(MsnSlpLink *slpl
slplink->swboard->flag |= MSN_SB_FLAG_FT;
slplink->slp_calls = g_list_append(slplink->slp_calls, slpcall);
+
+ /*
+ if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABILISHED)
+ msn_dc_ref(slplink->dc);
+ */
}
void
msn_slplink_remove_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall)
{
+ /*
+ if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABILISHED)
+ msn_dc_unref(slplink->dc);
+ */
+
slplink->slp_calls = g_list_remove(slplink->slp_calls, slpcall);
/* The slplink has no slpcalls in it, release it from MSN_SB_FLAG_FT.
@@ -209,13 +219,11 @@ msn_slplink_send_msg(MsnSlpLink *slplink
static void
msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg)
{
-#if 0
- if (slplink->directconn != NULL)
+ if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABILISHED)
{
- msn_directconn_send_msg(slplink->directconn, msg);
+ msn_dc_enqueue_msg(slplink->dc, msg);
}
else
-#endif
{
if (slplink->swboard == NULL)
{
@@ -431,11 +439,30 @@ msn_slplink_send_queued_slpmsgs(MsnSlpLi
}
}
+static MsnSlpMessage*
+msn_slplink_create_ack(MsnSlpLink *slplink, MsnSlpHeader *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";
+
+ return slpmsg;
+}
+
static void
-msn_slplink_send_ack(MsnSlpLink *slplink, MsnMessage *msg)
+msn_slplink_send_ack(MsnSlpLink *slplink, MsnSlpHeader *header)
{
- MsnSlpMessage *slpmsg;
+ MsnSlpMessage *slpmsg = msn_slplink_create_ack(slplink, header);
+ /*
slpmsg = msn_slpmsg_new(slplink);
slpmsg->session_id = msg->msnslp_header.session_id;
@@ -445,6 +472,7 @@ msn_slplink_send_ack(MsnSlpLink *slplink
slpmsg->ack_sub_id = msg->msnslp_header.ack_id;
slpmsg->ack_size = msg->msnslp_header.total_size;
slpmsg->info = "SLP ACK";
+ */
msn_slplink_send_slpmsg(slplink, slpmsg);
msn_slpmsg_destroy(slpmsg);
@@ -491,38 +519,36 @@ void
}
void
-msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
+msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpHeader *header, const char *data, gsize len)
{
MsnSlpMessage *slpmsg;
- const char *data;
guint64 offset;
- gsize len;
PurpleXfer *xfer = NULL;
+ /*
if (purple_debug_is_verbose())
msn_slpmsg_show(msg);
+ */
#ifdef MSN_DEBUG_SLP_FILES
- debug_msg_to_file(msg, FALSE);
#endif
-
- if (msg->msnslp_header.total_size < msg->msnslp_header.length)
+ if (header->total_size < header->length)
{
purple_debug_error("msn", "This can't be good\n");
g_return_if_reached();
}
- data = msn_message_get_bin_data(msg, &len);
+ /* data = msn_message_get_bin_data(msg, &len); */
- offset = msg->msnslp_header.offset;
+ offset = header->offset;
if (offset == 0)
{
slpmsg = msn_slpmsg_new(slplink);
- slpmsg->id = msg->msnslp_header.id;
- slpmsg->session_id = msg->msnslp_header.session_id;
- slpmsg->size = msg->msnslp_header.total_size;
- slpmsg->flags = msg->msnslp_header.flags;
+ slpmsg->id = header->id;
+ slpmsg->session_id = header->session_id;
+ slpmsg->size = header->total_size;
+ slpmsg->flags = header->flags;
if (slpmsg->session_id)
{
@@ -567,7 +593,7 @@ msn_slplink_process_msg(MsnSlpLink *slpl
}
else
{
- slpmsg = msn_slplink_message_find(slplink, msg->msnslp_header.session_id, msg->msnslp_header.id);
+ slpmsg = msn_slplink_message_find(slplink, header->session_id, header->id);
if (slpmsg == NULL)
{
/* Probably the transfer was canceled */
@@ -615,45 +641,60 @@ msn_slplink_process_msg(MsnSlpLink *slpl
return;
#endif
- if (msg->msnslp_header.offset + msg->msnslp_header.length
- >= msg->msnslp_header.total_size)
+ 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");
- if (!slpcall->wasted) {
+ /*if (!slpcall->wasted) {*/
if (slpmsg->flags == 0x100)
{
+#if 0
MsnDirectConn *directconn;
directconn = slplink->directconn;
-#if 0
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)
+ slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 ||
+ slpmsg->flags == 0x1000030)
{
/* Release all the messages and send the ACK */
-
- msn_slplink_send_ack(slplink, msg);
- msn_slplink_send_queued_slpmsgs(slplink);
+
+ if (slpcall != NULL && 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 {
+ 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->wasted)
+ if (slpcall != NULL && !slpcall->wait_for_socket && slpcall->wasted)
msn_slpcall_destroy(slpcall);
}
}
============================================================
--- libpurple/protocols/msn/slplink.h 45e42061dd6c5fa0c89c80de95eea64a23a09fab
+++ libpurple/protocols/msn/slplink.h 0c28a7671359beb932186c70684349d46e5824bb
@@ -42,13 +42,12 @@ struct _MsnSlpLink
{
MsnSession *session;
MsnSwitchBoard *swboard;
+ MsnDirectConn *dc;
char *remote_user;
int slp_seq_id;
- MsnDirectConn *directconn;
-
GList *slp_calls;
GList *slp_msgs;
@@ -79,7 +78,7 @@ void msn_slplink_send_queued_slpmsgs(Msn
void msn_slplink_send_slpmsg(MsnSlpLink *slplink,
MsnSlpMessage *slpmsg);
void msn_slplink_send_queued_slpmsgs(MsnSlpLink *slplink);
-void msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg);
+void msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpHeader *header, const char *data, gsize len);
void msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer);
/* Only exported for msn_xfer_write */
============================================================
--- libpurple/protocols/msn/switchboard.c f5779f38f8135f80e89603a65206110bbc836dd2
+++ libpurple/protocols/msn/switchboard.c 618760ce3fb1e2190a27b7c074155afbdb15a796
@@ -87,9 +87,18 @@ msn_switchboard_destroy(MsnSwitchBoard *
purple_timeout_remove(swboard->reconn_timeout_h);
/* If it linked us is because its looking for trouble */
- while (swboard->slplinks != NULL)
- msn_slplink_destroy(swboard->slplinks->data);
+ while (swboard->slplinks != NULL) {
+ /* Destroy only those slplinks which use the switchboard */
+ MsnSlpLink *slplink = swboard->slplinks->data;
+ if (slplink->dc == NULL)
+ msn_slplink_destroy(slplink);
+ else {
+ swboard->slplinks = g_list_remove(swboard->slplinks, slplink);
+ slplink->swboard = NULL;
+ }
+ }
+
/* Destroy the message queue */
while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL)
{
More information about the Commits
mailing list