Mercurial > pidgin.yaz
view libpurple/protocols/msn/directconn.c @ 30746:c492cef26b88
jabber: Only take the character data from a <body/> element (non-XHTML)
rfc3921 and draft-ietf-xmpp-3921bis talk about the XML character data
of the <body/> and 3921bis also says it MUST NOT contain mixed content
(see 3.2.2 of the XML 1.0 spec). This should fix Google Talk's private
chats showing an empty line whenever someone joins/leaves (caused by
some ugly XMPP traffic from Google), and seems correct to me otherwise.
This was changed from _get_data to _to_str 7 years ago in
76319226b46e6e64b1ef61933baeb43a5a484a61.
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sat, 31 Jul 2010 20:39:55 +0000 |
parents | f87a1844aff0 |
children | c7fa7c7aca7d |
line wrap: on
line source
/** * @file directconn.c MSN direct connection functions * * purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "msn.h" #include "directconn.h" #include "slp.h" #include "slpmsg.h" #pragma pack(push,1) typedef struct { guint32 session_id; guint32 seq_id; guint64 offset; guint64 total_size; guint32 length; guint32 flags; guint32 ack_id; guint32 ack_uid; guint64 ack_size; /* guint8 body[1]; */ } MsnDcContext; #pragma pack(pop) #define DC_PACKET_HEADER_SIZE sizeof(MsnDcContext) #define DC_MAX_BODY_SIZE 8*1024 #define DC_MAX_PACKET_SIZE (DC_PACKET_HEADER_SIZE + DC_MAX_BODY_SIZE) static void msn_dc_calculate_nonce_hash(MsnDirectConnNonceType type, const guchar nonce[16], gchar nonce_hash[37]) { guchar digest[20]; if (type == DC_NONCE_SHA1) { PurpleCipher *cipher = purple_ciphers_find_cipher("sha1"); PurpleCipherContext *context = purple_cipher_context_new(cipher, NULL); purple_cipher_context_append(context, nonce, sizeof(nonce)); purple_cipher_context_digest(context, sizeof(digest), digest, NULL); purple_cipher_context_destroy(context); } else if (type == DC_NONCE_PLAIN) { memcpy(digest, nonce, 16); } g_sprintf(nonce_hash, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", digest[3], digest[2], digest[1], digest[0], digest[5], digest[4], digest[7], digest[6], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15] ); } static void msn_dc_generate_nonce(MsnDirectConn *dc) { guint32 *nonce; int i; nonce = (guint32 *)&dc->nonce; for (i = 0; i < 4; i++) nonce[i] = rand(); msn_dc_calculate_nonce_hash(dc->nonce_type, dc->nonce, dc->nonce_hash); if (purple_debug_is_verbose()) purple_debug_info("msn", "DC %p generated nonce %s\n", dc, dc->nonce_hash); } static MsnDirectConnPacket * msn_dc_new_packet(guint32 length) { MsnDirectConnPacket *p; p = g_new0(MsnDirectConnPacket, 1); p->length = length; p->data = g_malloc(length); return p; } static void msn_dc_destroy_packet(MsnDirectConnPacket *p) { g_free(p->data); if (p->msg) msn_message_unref(p->msg); g_free(p); } MsnDirectConn * msn_dc_new(MsnSlpCall *slpcall) { MsnDirectConn *dc; g_return_val_if_fail(slpcall != NULL, NULL); dc = g_new0(MsnDirectConn, 1); if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_new %p\n", dc); 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; 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 = -1; dc->send_connection_info_msg_cb = NULL; dc->ext_ip = NULL; dc->timeout_handle = 0; dc->progress = FALSE; /*dc->num_calls = 1;*/ /* TODO: Probably should set this based on buddy caps */ dc->nonce_type = DC_NONCE_PLAIN; msn_dc_generate_nonce(dc); return dc; } void msn_dc_destroy(MsnDirectConn *dc) { MsnSlpLink *slplink; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_destroy %p\n", dc); g_return_if_fail(dc != NULL); if (dc->slpcall != NULL) dc->slpcall->wait_for_socket = FALSE; slplink = dc->slplink; if (slplink) { slplink->dc = NULL; if (slplink->swboard == NULL) msn_slplink_destroy(slplink); } g_free(dc->msg_body); if (dc->prev_ack) { msn_slpmsg_destroy(dc->prev_ack); } if (dc->listen_data != NULL) { purple_network_listen_cancel(dc->listen_data); } if (dc->connect_data != NULL) { purple_proxy_connect_cancel(dc->connect_data); } if (dc->listenfd != -1) { purple_network_remove_port_mapping(dc->listenfd); close(dc->listenfd); } if (dc->listenfd_handle != 0) { purple_input_remove(dc->listenfd_handle); } if (dc->connect_timeout_handle != 0) { purple_timeout_remove(dc->connect_timeout_handle); } if (dc->fd != -1) { close(dc->fd); } if (dc->send_handle != 0) { purple_input_remove(dc->send_handle); } if (dc->recv_handle != 0) { purple_input_remove(dc->recv_handle); } g_free(dc->in_buffer); if (dc->out_queue != NULL) { while (!g_queue_is_empty(dc->out_queue)) msn_dc_destroy_packet( g_queue_pop_head(dc->out_queue) ); g_queue_free(dc->out_queue); } g_free(dc->ext_ip); if (dc->timeout_handle != 0) { purple_timeout_remove(dc->timeout_handle); } g_free(dc); } /* 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--; } } */ void msn_dc_send_invite(MsnDirectConn *dc) { MsnSlpCall *slpcall; MsnSlpMessage *msg; gchar *header; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_send_invite %p\n", dc); g_return_if_fail(dc != NULL); 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 ); msg->info = "DC INVITE"; msg->text_body = TRUE; g_free(header); g_free(dc->msg_body); dc->msg_body = NULL; msn_slplink_queue_slpmsg(slpcall->slplink, msg); } void msn_dc_send_ok(MsnDirectConn *dc) { if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_send_ok %p\n", dc); 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); } void msn_dc_fallback_to_sb(MsnDirectConn *dc) { MsnSlpLink *slplink; MsnSlpCall *slpcall; GQueue *queue = NULL; purple_debug_info("msn", "msn_dc_fallback_to_sb %p\n", dc); g_return_if_fail(dc != NULL); slpcall = dc->slpcall; slplink = msn_slplink_ref(dc->slplink); if (slpcall && !g_queue_is_empty(dc->out_queue)) { queue = dc->out_queue; dc->out_queue = NULL; } msn_dc_destroy(dc); if (slpcall) { msn_slpcall_session_init(slpcall); if (queue) { while (!g_queue_is_empty(queue)) { MsnDirectConnPacket *p = g_queue_pop_head(queue); msn_slplink_send_msg(slplink, p->msg); msn_dc_destroy_packet(p); } g_queue_free(queue); } } msn_slplink_unref(slplink); } static void msn_dc_parse_binary_header(MsnDirectConn *dc) { MsnSlpHeader *h; MsnDcContext *context; g_return_if_fail(dc != NULL); h = &dc->header; /* Skip packet size */ context = (MsnDcContext *)(dc->in_buffer + 4); h->session_id = GUINT32_FROM_LE(context->session_id); h->id = GUINT32_FROM_LE(context->seq_id); h->offset = GUINT64_FROM_LE(context->offset); h->total_size = GUINT64_FROM_LE(context->total_size); h->length = GUINT32_FROM_LE(context->length); h->flags = GUINT32_FROM_LE(context->flags); h->ack_id = GUINT32_FROM_LE(context->ack_id); h->ack_sub_id = GUINT32_FROM_LE(context->ack_uid); h->ack_size = GUINT64_FROM_LE(context->ack_size); } static const gchar * msn_dc_serialize_binary_header(MsnDirectConn *dc) { MsnSlpHeader *h; static MsnDcContext bin_header; g_return_val_if_fail(dc != NULL, NULL); h = &dc->header; bin_header.session_id = GUINT32_TO_LE(h->session_id); bin_header.seq_id = GUINT32_TO_LE(h->id); bin_header.offset = GUINT64_TO_LE(h->offset); bin_header.total_size = GUINT64_TO_LE(h->total_size); bin_header.length = GUINT32_TO_LE(h->length); bin_header.flags = GUINT32_TO_LE(h->flags); bin_header.ack_id = GUINT32_TO_LE(h->ack_id); bin_header.ack_uid = GUINT32_TO_LE(h->ack_sub_id); bin_header.ack_size = GUINT64_TO_LE(h->ack_size); return (const gchar *)&bin_header; } static void msn_dc_send_cb(gpointer data, gint fd, PurpleInputCondition cond) { MsnDirectConn *dc = data; 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; } p = g_queue_peek_head(dc->out_queue); if (dc->msg_pos < 0) { /* First we send the length of the packet */ guint32 len = GUINT32_TO_LE(p->length); bytes_sent = send(fd, &len, 4, 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; } dc->msg_pos = 0; } bytes_to_send = p->length - dc->msg_pos; bytes_sent = send(fd, p->data + dc->msg_pos, 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; } 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 = -1; } } 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); 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 msn_dc_send_foo(MsnDirectConn *dc) { MsnDirectConnPacket *p; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_send_foo %p\n", dc); p = msn_dc_new_packet(4); memcpy(p->data, "foo\0", 4); msn_dc_enqueue_packet(dc, p); } static void msn_dc_send_handshake_with_nonce(MsnDirectConn *dc, MsnDirectConnPacket *p) { const gchar *h; h = msn_dc_serialize_binary_header(dc); memcpy(p->data, h, DC_PACKET_HEADER_SIZE); memcpy(p->data + offsetof(MsnDcContext, ack_id), dc->nonce, 16); msn_dc_enqueue_packet(dc, p); } static void msn_dc_send_handshake(MsnDirectConn *dc) { MsnDirectConnPacket *p; p = msn_dc_new_packet(DC_PACKET_HEADER_SIZE); 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; msn_dc_send_handshake_with_nonce(dc, p); } static void msn_dc_send_handshake_reply(MsnDirectConn *dc) { MsnDirectConnPacket *p; p = msn_dc_new_packet(DC_PACKET_HEADER_SIZE); dc->header.id = dc->slpcall->slplink->slp_seq_id++; dc->header.length = 0; msn_dc_send_handshake_with_nonce(dc, p); } static gboolean msn_dc_verify_handshake(MsnDirectConn *dc, guint32 packet_length) { guchar nonce[16]; gchar nonce_hash[37]; if (packet_length != DC_PACKET_HEADER_SIZE) return FALSE; memcpy(nonce, dc->in_buffer + 4 + offsetof(MsnDcContext, ack_id), 16); if (dc->nonce_type == DC_NONCE_PLAIN) { if (memcmp(dc->nonce, nonce, 16) == 0) { purple_debug_info("msn", "Nonce from buddy request and nonce from DC attempt match, " "allowing direct connection\n"); return TRUE; } else { purple_debug_warning("msn", "Nonce from buddy request and nonce from DC attempt " "don't match, ignoring direct connection\n"); return FALSE; } } else if (dc->nonce_type == DC_NONCE_SHA1) { msn_dc_calculate_nonce_hash(dc->nonce_type, nonce, nonce_hash); if (g_str_equal(dc->remote_nonce, nonce_hash)) { purple_debug_info("msn", "Received nonce %s from buddy request " "and calculated nonce %s from DC attempt. " "Nonces match, allowing direct connection\n", dc->remote_nonce, nonce_hash); return TRUE; } else { purple_debug_warning("msn", "Received nonce %s from buddy request " "and calculated nonce %s from DC attempt. " "Nonces don't match, ignoring direct connection\n", dc->remote_nonce, nonce_hash); return FALSE; } } else return FALSE; } 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_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg) { MsnDirectConnPacket *p; guint32 length; length = msg->body_len + DC_PACKET_HEADER_SIZE; p = msn_dc_new_packet(length); memcpy(p->data, &msg->msnslp_header, DC_PACKET_HEADER_SIZE); memcpy(p->data + DC_PACKET_HEADER_SIZE, msg->body, msg->body_len); p->sent_cb = msn_dc_send_packet_cb; p->msg = msn_message_ref(msg); msn_dc_enqueue_packet(dc, p); } 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 (!msn_dc_verify_handshake(dc, packet_length)) return DC_PROCESS_FALLBACK; msn_dc_send_handshake_reply(dc); dc->state = DC_STATE_ESTABLISHED; msn_slpcall_session_init(dc->slpcall); dc->slpcall = NULL; break; case DC_STATE_HANDSHAKE_REPLY: if (!msn_dc_verify_handshake(dc, packet_length)) return DC_PROCESS_FALLBACK; dc->state = DC_STATE_ESTABLISHED; msn_slpcall_session_init(dc->slpcall); dc->slpcall = NULL; break; case DC_STATE_ESTABLISHED: 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; } return DC_PROCESS_OK; } 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_ESTABLISHED) msn_dc_fallback_to_sb(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_ESTABLISHED) msn_dc_fallback_to_sb(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_FROM_LE(*((guint32*)dc->in_buffer)); 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 SB\n"); msn_dc_fallback_to_sb(dc); return; } if (dc->in_pos > packet_length + 4) { g_memmove(dc->in_buffer, dc->in_buffer + 4 + packet_length, dc->in_pos - packet_length - 4); } dc->in_pos -= packet_length + 4; } } static gboolean msn_dc_timeout(gpointer data) { MsnDirectConn *dc = data; g_return_val_if_fail(dc != NULL, FALSE); if (dc->progress) { dc->progress = FALSE; return TRUE; } else { dc->timeout_handle = 0; msn_dc_destroy(dc); return FALSE; } } 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_dc_connected_to_peer_cb(gpointer data, gint fd, const gchar *error_msg) { MsnDirectConn *dc = data; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_connected_to_peer_cb %p\n", dc); g_return_if_fail(dc != NULL); 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_INCOMING_TIMEOUT seconds */ static gboolean msn_dc_incoming_connection_timeout_cb(gpointer data) { MsnDirectConn *dc = data; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_incoming_connection_timeout_cb %p\n", dc); g_return_val_if_fail(dc != NULL, FALSE); if (dc->listen_data != NULL) { purple_network_listen_cancel(dc->listen_data); dc->listen_data = NULL; } if (dc->listenfd_handle != 0) { purple_input_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; } dc->connect_timeout_handle = 0; msn_dc_fallback_to_sb(dc); return FALSE; } /* * This callback will be called when we're unable to connect to * the remote host in DC_OUTGOING_TIMEOUT seconds. */ gboolean msn_dc_outgoing_connection_timeout_cb(gpointer data) { MsnDirectConn *dc = data; purple_debug_info("msn", "msn_dc_outgoing_connection_timeout_cb %p\n", dc); g_return_val_if_fail(dc != NULL, FALSE); 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_OUTGOING_TIMEOUT, msn_dc_outgoing_connection_timeout_cb, dc ); } else { /* * Connection failed * Fall back to SB transfer */ msn_dc_outgoing_connection_timeout_cb(dc); } } else { /* * Both internal and external connection attempts failed. * Fall back to SB transfer. */ msn_dc_fallback_to_sb(dc); } return FALSE; } /* * This callback will be called when we're the server * and somebody has connected to us in DC_INCOMING_TIMEOUT seconds. */ static void msn_dc_incoming_connection_cb(gpointer data, gint listenfd, PurpleInputCondition cond) { MsnDirectConn *dc = data; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_incoming_connection_cb %p\n", dc); 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; if (purple_debug_is_verbose()) purple_debug_info("msn", "msn_dc_listen_socket_created_cb %p\n", dc); g_return_if_fail(dc != NULL); dc->listen_data = NULL; if (listenfd != -1) { const char *ext_ip; const char *int_ip; int port; 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_INCOMING_TIMEOUT, 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" "%sNonce: {%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_type != DC_NONCE_PLAIN ? "Hashed-" : "", dc->nonce_hash, ext_ip, port, int_ip, port ); } else { dc->msg_body = g_strdup_printf( "Bridge: TCPv1\r\n" "Listening: true\r\n" "%sNonce: {%s}\r\n" "IPv4External-Addrs: %s\r\n" "IPv4External-Port: %d\r\n" "\r\n", dc->nonce_type != DC_NONCE_PLAIN ? "Hashed-" : "", 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; } } }