Mercurial > pidgin.yaz
view libpurple/protocols/oscar/peer_proxy.c @ 30818:9d386bf63eab
Stop using custom encodings (and LATIN-1, for that matter) for sending
OSCAR messages (ICBM, chat, Direct IM). Now, we use ASCII if a message
contains ASCII characters only, and UTF-16 in all other cases.
That fixes #10833 (offline messages now will be sent as UTF-16)
and also a whole bunch of potential problems we can get
with charset 0x3. Different clients tend to interpret this
charset differently; for instance, the official client
always interprets it as LATIN-1, while alternative
clients may decode it as some other user-specified
8-bit encoding. On the other hand, ASCII messages
(charset 0x0) and UTF-16 messages (charset 0x2) are understood
uniformly by all clients.
I also cleaned-up the code a little (got rid of code paths that were
never executed, flags that were always set, unused struct members, etc.)
author | ivan.komarov@soc.pidgin.im |
---|---|
date | Tue, 27 Jul 2010 21:17:01 +0000 |
parents | 6e1967b0f90b |
children | bbb27d65681f |
line wrap: on
line source
/* * Purple's oscar protocol plugin * This file is the legal property of its developers. * Please see the AUTHORS file distributed alongside this file. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include "oscar.h" #include "peer.h" static void peer_proxy_send(PeerConnection *conn, ProxyFrame *frame) { size_t length; ByteStream bs; purple_debug_info("oscar", "Outgoing peer proxy frame with " "type=0x%04hx, unknown=0x%08x, " "flags=0x%04hx, and payload length=%hd\n", frame->type, frame->unknown, frame->flags, frame->payload.len); length = 12 + frame->payload.len; byte_stream_new(&bs, length); byte_stream_put16(&bs, length - 2); byte_stream_put16(&bs, PEER_PROXY_PACKET_VERSION); byte_stream_put16(&bs, frame->type); byte_stream_put32(&bs, frame->unknown); byte_stream_put16(&bs, frame->flags); byte_stream_putraw(&bs, frame->payload.data, frame->payload.len); peer_connection_send(conn, &bs); byte_stream_destroy(&bs); } /** * Create a rendezvous "init send" packet and send it on its merry way. * This is the first packet sent to the proxy server by the first client * to indicate that this will be a proxied connection * * @param conn The peer connection. */ static void peer_proxy_send_create_new_conn(PeerConnection *conn) { ProxyFrame frame; PurpleAccount *account; const gchar *bn; guint8 bn_length; memset(&frame, 0, sizeof(ProxyFrame)); frame.type = PEER_PROXY_TYPE_CREATE; frame.flags = 0x0000; account = purple_connection_get_account(conn->od->gc); bn = purple_account_get_username(account); bn_length = strlen(bn); byte_stream_new(&frame.payload, 1 + bn_length + 8 + 20); byte_stream_put8(&frame.payload, bn_length); byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length); byte_stream_putraw(&frame.payload, conn->cookie, 8); byte_stream_put16(&frame.payload, 0x0001); /* Type */ byte_stream_put16(&frame.payload, 16); /* Length */ byte_stream_putcaps(&frame.payload, conn->type); /* Value */ peer_proxy_send(conn, &frame); } /** * Create a rendezvous "init recv" packet and send it on its merry way. * This is the first packet sent to the proxy server by the second client * involved in this rendezvous proxy session. * * @param conn The peer connection. * @param pin The 2 byte PIN sent to us by the other user. This acts * as our passcode when establishing the proxy session. */ static void peer_proxy_send_join_existing_conn(PeerConnection *conn, guint16 pin) { ProxyFrame frame; PurpleAccount *account; const gchar *bn; guint8 bn_length; memset(&frame, 0, sizeof(ProxyFrame)); frame.type = PEER_PROXY_TYPE_JOIN; frame.flags = 0x0000; account = purple_connection_get_account(conn->od->gc); bn = purple_account_get_username(account); bn_length = strlen(bn); byte_stream_new(&frame.payload, 1 + bn_length + 2 + 8 + 20); byte_stream_put8(&frame.payload, bn_length); byte_stream_putraw(&frame.payload, (const guint8 *)bn, bn_length); byte_stream_put16(&frame.payload, pin); byte_stream_putraw(&frame.payload, conn->cookie, 8); byte_stream_put16(&frame.payload, 0x0001); /* Type */ byte_stream_put16(&frame.payload, 16); /* Length */ byte_stream_putcaps(&frame.payload, conn->type); /* Value */ peer_proxy_send(conn, &frame); } /** * Handle an incoming peer proxy negotiation frame. */ static void peer_proxy_recv_frame(PeerConnection *conn, ProxyFrame *frame) { purple_debug_info("oscar", "Incoming peer proxy frame with " "type=0x%04hx, unknown=0x%08x, " "flags=0x%04hx, and payload length=%hd\n", frame->type, frame->unknown, frame->flags, frame->payload.len); if (frame->type == PEER_PROXY_TYPE_CREATED) { /* * Read in 2 byte port then 4 byte IP and tell the * remote user to connect to it by sending an ICBM. */ guint16 pin; int i; guint8 ip[4]; pin = byte_stream_get16(&frame->payload); for (i = 0; i < 4; i++) ip[i] = byte_stream_get8(&frame->payload); if (conn->type == OSCAR_CAPABILITY_DIRECTIM) aim_im_sendch2_odc_requestproxy(conn->od, conn->cookie, conn->bn, ip, pin, ++conn->lastrequestnumber); else if (conn->type == OSCAR_CAPABILITY_SENDFILE) { aim_im_sendch2_sendfile_requestproxy(conn->od, conn->cookie, conn->bn, ip, pin, ++conn->lastrequestnumber, (const gchar *)conn->xferdata.name, conn->xferdata.size, conn->xferdata.totfiles); } } else if (frame->type == PEER_PROXY_TYPE_READY) { purple_input_remove(conn->watcher_incoming); conn->watcher_incoming = 0; peer_connection_finalize_connection(conn); } else if (frame->type == PEER_PROXY_TYPE_ERROR) { if (byte_stream_empty(&frame->payload) >= 2) { guint16 error; const char *msg; error = byte_stream_get16(&frame->payload); if (error == 0x000d) msg = "bad request"; else if (error == 0x0010) msg = "initial request timed out"; else if (error == 0x001a) msg ="accept period timed out"; else msg = "unknown reason"; purple_debug_info("oscar", "Proxy negotiation failed with " "error 0x%04hx: %s\n", error, msg); } else { purple_debug_warning("oscar", "Proxy negotiation failed with " "an unknown error\n"); } peer_connection_trynext(conn); } else { purple_debug_warning("oscar", "Unknown peer proxy frame type 0x%04hx.\n", frame->type); } } static void peer_proxy_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond) { PeerConnection *conn; gssize read; ProxyFrame *frame; conn = data; frame = conn->frame; /* Start reading a new proxy frame */ if (frame == NULL) { /* Read the first 12 bytes (frame length and header) */ read = recv(conn->fd, conn->proxy_header + conn->proxy_header_received, 12 - conn->proxy_header_received, 0); /* Check if the proxy server closed the connection */ if (read == 0) { purple_debug_info("oscar", "Peer proxy server closed connection\n"); peer_connection_trynext(conn); return; } /* If there was an error then close the connection */ if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ return; purple_debug_info("oscar", "Lost connection with peer proxy server\n"); peer_connection_trynext(conn); return; } conn->lastactivity = time(NULL); /* If we don't even have the first 12 bytes then do nothing */ conn->proxy_header_received += read; if (conn->proxy_header_received < 12) return; /* We only support a specific version of the proxy protocol */ if (aimutil_get16(&conn->proxy_header[2]) != PEER_PROXY_PACKET_VERSION) { purple_debug_warning("oscar", "Expected peer proxy protocol " "version %u but received version %u. Closing " "connection.\n", PEER_PROXY_PACKET_VERSION, aimutil_get16(&conn->proxy_header[2])); peer_connection_trynext(conn); return; } /* Initialize a new temporary ProxyFrame for incoming data */ frame = g_new0(ProxyFrame, 1); frame->payload.len = aimutil_get16(&conn->proxy_header[0]) - 10; frame->version = aimutil_get16(&conn->proxy_header[2]); frame->type = aimutil_get16(&conn->proxy_header[4]); frame->unknown = aimutil_get16(&conn->proxy_header[6]); frame->flags = aimutil_get16(&conn->proxy_header[10]); if (frame->payload.len > 0) frame->payload.data = g_new(guint8, frame->payload.len); conn->frame = frame; } /* If this frame has a payload then attempt to read it */ if (frame->payload.len - frame->payload.offset > 0) { /* Read data into the temporary buffer until it is complete */ read = recv(conn->fd, &frame->payload.data[frame->payload.offset], frame->payload.len - frame->payload.offset, 0); /* Check if the proxy server closed the connection */ if (read == 0) { purple_debug_info("oscar", "Peer proxy server closed connection\n"); g_free(frame->payload.data); g_free(frame); conn->frame = NULL; peer_connection_trynext(conn); return; } /* If there was an error then close the connection */ if (read < 0) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) /* No worries */ return; purple_debug_info("oscar", "Lost connection with peer proxy server\n"); g_free(frame->payload.data); g_free(frame); conn->frame = NULL; peer_connection_trynext(conn); return; } frame->payload.offset += read; } conn->lastactivity = time(NULL); if (frame->payload.offset < frame->payload.len) /* Waiting for more data to arrive */ return; /* We have a complete proxy frame! Handle it and continue reading */ conn->frame = NULL; byte_stream_rewind(&frame->payload); peer_proxy_recv_frame(conn, frame); g_free(frame->payload.data); g_free(frame); conn->proxy_header_received = 0; } /** * We tried to make an outgoing connection to a proxy server. It * either connected or failed to connect. */ void peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message) { PeerConnection *conn; conn = data; conn->verified_connect_data = NULL; if (source < 0) { peer_connection_trynext(conn); return; } conn->fd = source; conn->watcher_incoming = purple_input_add(conn->fd, PURPLE_INPUT_READ, peer_proxy_connection_recv_cb, conn); if (conn->proxyip != NULL) /* Connect to the session created by the remote user */ peer_proxy_send_join_existing_conn(conn, conn->port); else /* Create a new session */ peer_proxy_send_create_new_conn(conn); }