Mercurial > pidgin
diff libpurple/protocols/msn/directconn.c @ 30013:119bd7b072eb
Initial support for direct connections. Preliminary patch from ticket #247
by Gbor Szuromi. Still needs lots of testing and fixes.
References #247.
committer: Elliott Sales de Andrade <qulogic@pidgin.im>
author | kukkerman@gmail.com |
---|---|
date | Wed, 17 Mar 2010 03:45:07 +0000 |
parents | 3157a8ea0012 |
children | b1cda3f8fdc9 |
line wrap: on
line diff
--- a/libpurple/protocols/msn/directconn.c Tue Mar 16 06:20:05 2010 +0000 +++ b/libpurple/protocols/msn/directconn.c Wed Mar 17 03:45:07 2010 +0000 @@ -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; - - g_return_if_fail(directconn != NULL); + PurpleCipher *cipher = NULL; + PurpleCipherContext *context = NULL; + static guchar digest[20]; + int i; + + guint32 g1; + guint16 g2; + guint16 g3; + guint64 g4; - slplink = directconn->slplink; - - slpmsg = msn_slpmsg_new(slplink); - slpmsg->flags = 0x100; + cipher = purple_ciphers_find_cipher("sha1"); + g_return_if_fail(cipher != NULL); + + for (i = 0; i < 16; i++) + dc->nonce[i] = rand() & 0xff; - if (directconn->nonce != NULL) - { - guint32 t1; - guint16 t2; - guint16 t3; - guint16 t4; - guint64 t5; - - sscanf (directconn->nonce, "%08X-%04hX-%04hX-%04hX-%012" G_GINT64_MODIFIER "X", &t1, &t2, &t3, &t4, &t5); + 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); - 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); + 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) + ); +} - slpmsg->ack_id = t1; - slpmsg->ack_sub_id = t2 | (t3 << 16); - slpmsg->ack_size = t4 | t5; - } +static MsnDirectConnPacket* +msn_dc_new_packet() +{ + MsnDirectConnPacket *p; - g_free(directconn->nonce); + p = g_new0(MsnDirectConnPacket, 1); + p->data = NULL; + p->sent_cb = NULL; + p->msg = NULL; - msn_slplink_send_slpmsg(slplink, slpmsg); - - directconn->acked =TRUE; + return p; } -/************************************************************************** - * Connection Functions - **************************************************************************/ +static void +msn_dc_destroy_packet(MsnDirectConnPacket *p) +{ + if (p->data) + g_free(p->data); + + if (p->msg) + msn_message_unref(p->msg); -static int -create_listener(int port) -{ - int fd; - int flags; - const int on = 1; + g_free(p); +} -#if 0 - struct addrinfo hints; - struct addrinfo *c, *res; - char port_str[5]; +MsnDirectConn* +msn_dc_new(MsnSlpCall *slpcall) +{ + MsnDirectConn *dc; + + purple_debug_info("msn", "msn_dc_new\n"); - snprintf(port_str, sizeof(port_str), "%d", port); - - memset(&hints, 0, sizeof(hints)); + g_return_val_if_fail(slpcall != NULL, NULL); + + dc = g_new0(MsnDirectConn, 1); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; + dc->slplink = slpcall->slplink; + dc->slpcall = slpcall; + + if (dc->slplink->dc != NULL) + purple_debug_warning("msn", "msn_dc_new: slplink already has an allocated DC!\n"); + + dc->slplink->dc = dc; - if (getaddrinfo(NULL, port_str, &hints, &res) != 0) - { - purple_debug_error("msn", "Could not get address info: %s.\n", - port_str); - return -1; - } + 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; - for (c = res; c != NULL; c = c->ai_next) - { - fd = socket(c->ai_family, c->ai_socktype, c->ai_protocol); + msn_dc_generate_nonce(dc); + + return dc; +} - if (fd < 0) - continue; +void +msn_dc_destroy(MsnDirectConn *dc) +{ + MsnSlpLink *slplink; + + purple_debug_info("msn", "msn_dc_destroy\n"); + + g_return_if_fail(dc != NULL); - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - if (bind(fd, c->ai_addr, c->ai_addrlen) == 0) - break; + slplink = dc->slplink; - close(fd); - } + if (dc->slpcall != NULL) + dc->slpcall->wait_for_socket = FALSE; + + slplink->dc = NULL; + + if (slplink->swboard == NULL) + msn_slplink_destroy(slplink); - if (c == NULL) - { - purple_debug_error("msn", "Could not find socket: %s.\n", port_str); - return -1; + 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; } - freeaddrinfo(res); -#else - struct sockaddr_in sockin; - - fd = socket(AF_INET, SOCK_STREAM, 0); - - if (fd < 0) - return -1; - - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) - { - close(fd); - 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; } - memset(&sockin, 0, sizeof(struct sockaddr_in)); - sockin.sin_family = AF_INET; - sockin.sin_port = htons(port); + 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) ); - if (bind(fd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) - { - close(fd); - return -1; - } -#endif - - if (listen (fd, 4) != 0) - { - close (fd); - return -1; + g_queue_free(dc->out_queue); } - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); -#ifndef _WIN32 - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif + if (dc->ext_ip != NULL) { + g_free(dc->ext_ip); + dc->ext_ip = NULL; + } - return fd; + if (dc->timeout_handle != 0) { + purple_timeout_remove(dc->timeout_handle); + dc->timeout_handle = 0; + } + + g_free(dc); } -static gssize -msn_directconn_write(MsnDirectConn *directconn, - const char *data, size_t len) +/* +void +msn_dc_ref(MsnDirectConn *dc) { - char *buffer, *tmp; - size_t buf_size; - gssize ret; - guint32 sent_len; - - g_return_val_if_fail(directconn != NULL, 0); - - buf_size = len + 4; - buffer = tmp = g_malloc(buf_size); - - sent_len = GUINT32_TO_LE(len); + g_return_if_fail(dc != NULL); - memcpy(tmp, &sent_len, 4); - tmp += 4; - memcpy(tmp, data, len); - tmp += len; - - ret = write(directconn->fd, buffer, buf_size); - -#ifdef DEBUG_DC - char *str; - str = g_strdup_printf("%s/msntest/w%.4d.bin", g_get_home_dir(), directconn->c); - - FILE *tf = g_fopen(str, "w"); - fwrite(buffer, 1, buf_size, tf); - fclose(tf); - - g_free(str); -#endif - - g_free(buffer); - - directconn->c++; - - return ret; + dc->num_calls++; } -#if 0 void -msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce) +msn_dc_unref(MsnDirectConn *dc) { - guint32 t1; - guint16 t2; - guint16 t3; - guint16 t4; - guint64 t5; - - g_return_if_fail(directconn != NULL); - g_return_if_fail(nonce != NULL); + g_return_if_fail(dc != NULL); - sscanf (nonce, "%08X-%04hX-%04hX-%04hX-%012llX", &t1, &t2, &t3, &t4, &t5); - - 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); - - directconn->slpheader = g_new0(MsnSlpHeader, 1); - - directconn->slpheader->ack_id = t1; - directconn->slpheader->ack_sub_id = t2 | (t3 << 16); - directconn->slpheader->ack_size = t4 | t5; + + if (dc->num_calls > 0) { + dc->num_calls--; + } } -#endif +*/ void -msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg) +msn_dc_send_invite(MsnDirectConn *dc) { - char *body; - size_t body_len; + MsnSlpCall *slpcall; + MsnSlpMessage *msg; + gchar *header; + + purple_debug_info("msn", "msn_dc_send_invite\n"); + + g_return_if_fail(dc != NULL); + + slpcall = dc->slpcall; + g_return_if_fail(slpcall != NULL); - body = msn_message_gen_slp_body(msg, &body_len); + 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_directconn_write(directconn, body, body_len); + msn_slplink_queue_slpmsg(slpcall->slplink, msg); +} + +void +msn_dc_send_ok(MsnDirectConn *dc) +{ + 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; + + 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); } static void -read_cb(gpointer data, gint source, PurpleInputCondition cond) +msn_dc_fallback_to_p2p(MsnDirectConn *dc) { - MsnDirectConn* directconn; - char *body; - size_t body_len; - gssize len; + MsnSlpCall *slpcall; + PurpleXfer *xfer; + + purple_debug_info("msn", "msn_dc_try_fallback_to_p2p\n"); + + g_return_if_fail(dc != NULL); + + slpcall = dc->slpcall; + g_return_if_fail(slpcall != NULL); + + xfer = slpcall->xfer; + g_return_if_fail(xfer != NULL); + + msn_dc_destroy(dc); + + msn_slpcall_session_init(slpcall); + + /* + switch (purple_xfer_get_status(xfer)) { + case PURPLE_XFER_STATUS_NOT_STARTED: + case PURPLE_XFER_STATUS_ACCEPTED: + msn_slpcall_session_init(slpcall); + break; - purple_debug_info("msn", "read_cb: %d, %d\n", source, cond); + 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; - directconn = data; + 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); + + break; + } + */ +} + +static void +msn_dc_parse_binary_header(MsnDirectConn *dc) +{ + MsnSlpHeader *h; + gchar *buffer; + + g_return_if_fail(dc != NULL); + + h = &dc->header; + /* Skip packet size */ + buffer = dc->in_buffer + 4; - /* 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)); + 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); +} + +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); + + memcpy(&h, &dc->header, sizeof(h)); + + 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)); + + h.offset = GUINT64_TO_LE(h.offset); + memcpy(bin_header + DC_DATA_OFFSET_OFFS, &h.offset, sizeof(h.offset)); + + h.total_size = GUINT64_TO_LE(h.total_size); + memcpy(bin_header + DC_TOTAL_DATA_SIZE_OFFS, &h.total_size, sizeof(h.total_size)); + + 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; +} - if (len <= 0) - { - /* ERROR */ - purple_debug_error("msn", "error reading\n"); +/* +static void +msn_dc_send_bye(MsnDirectConn *dc) +{ + 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); + + 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", - if (directconn->inpa) - purple_input_remove(directconn->inpa); + 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); - close(directconn->fd); + 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_directconn_destroy(directconn); + msn_dc_send_packet(dc); +} - return; - } +static void +msn_dc_send_ack(MsnDirectConn *dc) +{ + 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; + + msn_dc_send_packet(dc); +} - body_len = GUINT32_FROM_LE(body_len); +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; + + msn_dc_send_packet(dc); +} - purple_debug_info("msn", "body_len=%" G_GSIZE_FORMAT "\n", body_len); +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); + + slpcall = xfer->data; + g_return_if_fail(slpcall != NULL); + + 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; + + default: + msn_dc_destroy(dc); + break; + } +} - if (body_len <= 0) - { - /* ERROR */ - purple_debug_error("msn", "error reading\n"); +static void +msn_dc_xfer_recv_cancel(PurpleXfer *xfer) +{ + MsnSlpCall *slpcall; + MsnDirectConn *dc; + + purple_debug_info("msn", "msn_dc_xfer_recv_cancel\n"); + + g_return_if_fail(xfer != NULL); + + slpcall = xfer->data; + g_return_if_fail(slpcall != NULL); + + 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; - if (directconn->inpa) - purple_input_remove(directconn->inpa); + default: + msn_dc_destroy(dc); + break; + } +} +*/ - close(directconn->fd); +static void +msn_dc_send_cb(gpointer data, gint fd, PurpleInputCondition cond) +{ + MsnDirectConn *dc = data; + MsnDirectConnPacket *p; + int bytes_to_send; + int bytes_sent; - msn_directconn_destroy(directconn); - + 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); - - if (body != NULL) - { - /* Let's read the data. */ - len = read(directconn->fd, body, body_len); + p = g_queue_peek_head(dc->out_queue); + bytes_to_send = p->length - dc->msg_pos; - purple_debug_info("msn", "len=%" G_GSIZE_FORMAT "\n", len); - } - else - { - purple_debug_error("msn", "Failed to allocate memory for read\n"); - len = 0; + bytes_sent = send(fd, p->data, bytes_to_send, 0); + if (bytes_sent < 0) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + return; + + purple_debug_warning("msn", "msn_dc_send_cb: send error\n"); + msn_dc_destroy(dc); + return; } - if (len > 0) - { - MsnMessage *msg; - -#ifdef DEBUG_DC - str = g_strdup_printf("%s/msntest/r%.4d.bin", g_get_home_dir(), directconn->c); - - FILE *tf = g_fopen(str, "w"); - fwrite(body, 1, len, tf); - fclose(tf); - - g_free(str); -#endif - - directconn->c++; + dc->progress = TRUE; - msg = msn_message_new_msnslp(); - msn_message_parse_slp_body(msg, body, body_len); + dc->msg_pos += bytes_sent; + if (dc->msg_pos == p->length) { + if (p->sent_cb != NULL) + p->sent_cb(p); - purple_debug_info("msn", "directconn: process_msg\n"); - msn_slplink_process_msg(directconn->slplink, msg); + g_queue_pop_head(dc->out_queue); + msn_dc_destroy_packet(p); + + dc->msg_pos = 0; } - else - { - /* ERROR */ - purple_debug_error("msn", "error reading\n"); - - if (directconn->inpa) - purple_input_remove(directconn->inpa); - - close(directconn->fd); - - msn_directconn_destroy(directconn); - } - - g_free(body); } static void -connect_cb(gpointer data, gint source, PurpleInputCondition cond) +msn_dc_enqueue_packet(MsnDirectConn *dc, MsnDirectConnPacket *p) { - MsnDirectConn* directconn; - int fd; - - purple_debug_misc("msn", "directconn: connect_cb: %d, %d.\n", source, cond); - - directconn = data; - directconn->connect_data = NULL; - - if (TRUE) - { - fd = source; - } - else - { - struct sockaddr_in client_addr; - socklen_t client; - fd = accept (source, (struct sockaddr *)&client_addr, &client); - } - - directconn->fd = fd; + gboolean was_empty; + + was_empty = g_queue_is_empty(dc->out_queue); + g_queue_push_tail(dc->out_queue, p); - if (fd > 0) - { - directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, read_cb, - directconn); - - if (TRUE) - { - /* Send foo. */ - msn_directconn_write(directconn, "foo", strlen("foo") + 1); - - /* Send Handshake */ - msn_directconn_send_handshake(directconn); - } - else - { - } - } - else - { - /* ERROR */ - purple_debug_error("msn", "could not add input\n"); - - if (directconn->inpa) - purple_input_remove(directconn->inpa); - - close(directconn->fd); + 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); } } static void -directconn_connect_cb(gpointer data, gint source, const gchar *error_message) +msn_dc_send_foo(MsnDirectConn *dc) { - if (error_message) - purple_debug_error("msn", "Error making direct connection: %s\n", error_message); + MsnDirectConnPacket *p; + + purple_debug_info("msn", "msn_dc_send_foo\n"); + + g_return_if_fail(dc != NULL); - connect_cb(data, source, PURPLE_INPUT_READ); + p = msn_dc_new_packet(); + + p->length = 8; + p->data = (guchar*)g_strdup("\4\0\0\0foo"); + p->sent_cb = NULL; + + msn_dc_enqueue_packet(dc, p); } -gboolean -msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port) +static void +msn_dc_send_handshake(MsnDirectConn *dc) { - MsnSession *session; + 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); - g_return_val_if_fail(directconn != NULL, FALSE); - g_return_val_if_fail(host != NULL, TRUE); - g_return_val_if_fail(port > 0, FALSE); + 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; - session = directconn->slplink->session; + 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); +} -#if 0 - if (session->http_method) - { - servconn->http_data->gateway_host = g_strdup(host); - } -#endif +static void +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); - directconn->connect_data = purple_proxy_connect(NULL, session->account, - host, port, directconn_connect_cb, directconn); + 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; - return (directconn->connect_data != NULL); + 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) +{ + if (p->msg != NULL && p->msg->ack_cb != NULL) + p->msg->ack_cb(p->msg, p->msg->ack_data); } void -msn_directconn_listen(MsnDirectConn *directconn) +msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg) +{ + MsnDirectConnPacket *p = msn_dc_new_packet(); + guint32 length = msg->body_len + DC_PACKET_HEADER_SIZE; + + p->length = 4 + length; + p->data = g_malloc(p->length); + + 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); + + p->sent_cb = msn_dc_send_packet_cb; + p->msg = msg; + msn_message_ref(msg); + + msn_dc_enqueue_packet(dc, p); +} + +static int +msn_dc_process_packet(MsnDirectConn *dc, guint32 packet_length) { - int port; - int fd; + 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; - port = 7000; + 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 + { + guint64 file_size; + int bytes_written; + PurpleXfer *xfer; + MsnSlpHeader *h = &dc->header; + + if (packet_length < DC_PACKET_HEADER_SIZE) + return DC_TRANSFER_FALLBACK; - for (fd = -1; fd < 0;) - fd = create_listener(++port); + /* + * 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); - directconn->fd = fd; + /* 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); - directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, connect_cb, - directconn); + 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 + ); + } + break; + } - directconn->port = port; - directconn->c = 0; + 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 + } + + return DC_PROCESS_OK; } -MsnDirectConn* -msn_directconn_new(MsnSlpLink *slplink) +static void +msn_dc_recv_cb(gpointer data, gint fd, PurpleInputCondition cond) { - MsnDirectConn *directconn; + 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; + } - directconn = g_new0(MsnDirectConn, 1); + /* 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; + } +} + +#if 0 +static gboolean +msn_dc_send_next_packet(MsnDirectConn *dc) +{ + MsnSlpMessage *msg; + + 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); - directconn->slplink = slplink; + 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; +} + +static int +msn_dc_send_process_packet_cb(MsnDirectConn *dc, guint32 packet_length) +{ + 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; + + if (memcmp(dc->in_buffer, "\4\0\0\0foo", 8) != 0) + return DC_TRANSFER_FALLBACK; + + dc->state = DC_STATE_HANDSHAKE; + break; + } + + 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; + } - if (slplink->directconn != NULL) - purple_debug_info("msn", "got_transresp: LEAK\n"); + 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; + } - slplink->directconn = directconn; + return DC_TRANSFER_OK; +} +#endif + +static gboolean +msn_dc_timeout(gpointer data) +{ + MsnDirectConn *dc = data; + + g_return_val_if_fail(dc != NULL, FALSE); + + if (dc->progress) + dc->progress = FALSE; + else + msn_dc_destroy(dc); - return directconn; + 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_destroy(MsnDirectConn *directconn) +msn_dc_connected_to_peer_cb(gpointer data, gint fd, const gchar *error_msg) { - if (directconn->connect_data != NULL) - purple_proxy_connect_cancel(directconn->connect_data); + MsnDirectConn *dc; + + purple_debug_info("msn", "msn_dc_connected_to_peer_cb\n"); + + g_return_if_fail(data != NULL); + + dc = data; + + 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; + } +} + +/* + * 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); + + 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; + } - if (directconn->inpa != 0) - purple_input_remove(directconn->inpa); + msn_dc_destroy(dc); + /* Start p2p file transfer */ + msn_slpcall_session_init(slpcall); + + return FALSE; +} + +/* + * 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 *dc = data; + + purple_debug_info("msn", "msn_dc_outgoing_connection_timeout_cb\n"); + + 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; + } + + if (dc->connect_data != NULL) { + purple_proxy_connect_cancel(dc->connect_data); + dc->connect_data = NULL; + } + + 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 + ); + + g_free(dc->ext_ip); + dc->ext_ip = NULL; + + 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; - if (directconn->fd >= 0) - close(directconn->fd); + 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_dc_listen_socket_created_cb(int listenfd, gpointer data) +{ + MsnDirectConn *dc = data; + + purple_debug_info("msn", "msn_dc_listen_socket_created_cb\n"); + + g_return_if_fail(dc != NULL); + + dc->listen_data = NULL; + + 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); + + 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 + ); + + 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", - directconn->slplink->directconn = NULL; + 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", - g_free(directconn); + 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; + } + } } +