media: fix relay-info with Farstream 0.2
[siplcs.git] / src / purple / purple-transport.c
bloba5c8046617d2efd54fecab39a221f992a6e8c50c
1 /**
2 * @file purple-transport.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2013 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <errno.h>
28 #include <string.h>
29 #include <time.h>
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
34 #include <glib.h>
36 #include "sipe-common.h"
38 #include "connection.h"
39 #include "eventloop.h"
40 #include "network.h"
41 #include "proxy.h"
42 #include "sslconn.h"
44 #include "version.h"
45 #if PURPLE_VERSION_CHECK(3,0,0)
46 #include "circularbuffer.h"
47 #else
48 #include "circbuffer.h"
49 #define PurpleCircularBuffer PurpleCircBuffer
50 #define purple_circular_buffer_append(b, s, n) purple_circ_buffer_append(b, s, n)
51 #define purple_circular_buffer_get_max_read(b) purple_circ_buffer_get_max_read(b)
52 #define purple_circular_buffer_get_output(b) b->outptr
53 #define purple_circular_buffer_mark_read(b, s) purple_circ_buffer_mark_read(b, s)
54 #define purple_circular_buffer_new(s) purple_circ_buffer_new(s)
55 #endif
57 #ifdef _WIN32
58 /* wrappers for write() & friends for socket handling */
59 #include "win32/win32dep.h"
60 #endif
62 #include "purple-private.h"
64 #include "sipe-backend.h"
65 #include "sipe-core.h"
66 #include "sipe-nls.h"
68 struct sipe_transport_purple {
69 /* public part shared with core */
70 struct sipe_transport_connection public;
72 /* purple private part */
73 struct sipe_backend_private *purple_private;
74 transport_connected_cb *connected;
75 transport_input_cb *input;
76 transport_error_cb *error;
77 PurpleSslConnection *gsc;
78 PurpleProxyConnectData *proxy;
79 PurpleCircularBuffer *transmit_buffer;
80 guint transmit_handler;
81 guint receive_handler;
82 int socket;
84 gboolean is_valid;
87 #define PURPLE_TRANSPORT ((struct sipe_transport_purple *) conn)
88 #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport)
90 #define BUFFER_SIZE_INCREMENT 4096
94 /*****************************************************************************
96 * Common transport handling
98 *****************************************************************************/
99 static void transport_common_input(struct sipe_transport_purple *transport)
101 struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION;
102 gssize readlen, len;
103 gboolean firstread = TRUE;
105 /* Read all available data from the connection */
106 do {
107 /* Increase input buffer size as needed */
108 if (conn->buffer_length < conn->buffer_used + BUFFER_SIZE_INCREMENT) {
109 conn->buffer_length += BUFFER_SIZE_INCREMENT;
110 conn->buffer = g_realloc(conn->buffer, conn->buffer_length);
111 SIPE_DEBUG_INFO("transport_input_common: new buffer length %" G_GSIZE_FORMAT,
112 conn->buffer_length);
115 /* Try to read as much as there is space left in the buffer */
116 /* minus 1 for the string terminator */
117 readlen = conn->buffer_length - conn->buffer_used - 1;
118 len = transport->gsc ?
119 (gssize) purple_ssl_read(transport->gsc,
120 conn->buffer + conn->buffer_used,
121 readlen) :
122 read(transport->socket,
123 conn->buffer + conn->buffer_used,
124 readlen);
126 if (len < 0 && errno == EAGAIN) {
127 /* Try again later */
128 return;
129 } else if (len < 0) {
130 SIPE_DEBUG_ERROR("Read error: %s (%d)", strerror(errno), errno);
131 transport->error(SIPE_TRANSPORT_CONNECTION, _("Read error"));
132 return;
133 } else if (firstread && (len == 0)) {
134 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
135 transport->error(SIPE_TRANSPORT_CONNECTION, _("Server has disconnected"));
136 return;
139 conn->buffer_used += len;
140 firstread = FALSE;
142 /* Equivalence indicates that there is possibly more data to read */
143 } while (len == readlen);
145 conn->buffer[conn->buffer_used] = '\0';
146 transport->input(conn);
149 static void transport_ssl_input(gpointer data,
150 SIPE_UNUSED_PARAMETER PurpleSslConnection *gsc,
151 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
153 struct sipe_transport_purple *transport = data;
155 /* Ignore spurious "SSL input" events after disconnect */
156 if (transport->is_valid)
157 transport_common_input(transport);
160 static void transport_tcp_input(gpointer data,
161 SIPE_UNUSED_PARAMETER gint source,
162 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
164 struct sipe_transport_purple *transport = data;
166 /* Ignore spurious "TCP input" events after disconnect */
167 if (transport->is_valid)
168 transport_common_input(transport);
171 static void transport_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection *gsc,
172 PurpleSslErrorType error,
173 gpointer data)
175 struct sipe_transport_purple *transport = data;
177 /* Ignore spurious "SSL connect failure" events after disconnect */
178 if (transport->is_valid) {
179 transport->socket = -1;
180 transport->gsc = NULL;
181 transport->error(SIPE_TRANSPORT_CONNECTION,
182 purple_ssl_strerror(error));
183 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
187 static void transport_common_connected(struct sipe_transport_purple *transport,
188 int fd)
190 /* Ignore spurious "connected" events after disconnect */
191 if (transport->is_valid) {
193 transport->proxy = NULL;
195 if (fd < 0) {
196 transport->error(SIPE_TRANSPORT_CONNECTION,
197 _("Could not connect"));
198 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
199 return;
202 transport->socket = fd;
203 transport->public.client_port = purple_network_get_port_from_fd(fd);
205 if (transport->gsc) {
206 purple_ssl_input_add(transport->gsc, transport_ssl_input, transport);
207 } else {
208 transport->receive_handler = purple_input_add(fd,
209 PURPLE_INPUT_READ,
210 transport_tcp_input,
211 transport);
214 transport->connected(SIPE_TRANSPORT_CONNECTION);
218 static void transport_ssl_connected(gpointer data,
219 PurpleSslConnection *gsc,
220 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
222 transport_common_connected(data, gsc->fd);
225 static void transport_tcp_connected(gpointer data,
226 gint source,
227 SIPE_UNUSED_PARAMETER const gchar *error_message)
229 transport_common_connected(data, source);
232 struct sipe_transport_connection *
233 sipe_backend_transport_connect(struct sipe_core_public *sipe_public,
234 const sipe_connect_setup *setup)
236 struct sipe_transport_purple *transport = g_new0(struct sipe_transport_purple, 1);
237 struct sipe_backend_private *purple_private = sipe_public->backend_private;
238 PurpleConnection *gc = purple_private->gc;
239 PurpleAccount *account = purple_connection_get_account(gc);
241 SIPE_DEBUG_INFO("transport_connect - hostname: %s port: %d",
242 setup->server_name, setup->server_port);
244 transport->public.type = setup->type;
245 transport->public.user_data = setup->user_data;
246 transport->purple_private = purple_private;
247 transport->connected = setup->connected;
248 transport->input = setup->input;
249 transport->error = setup->error;
250 transport->transmit_buffer = purple_circular_buffer_new(0);
251 transport->is_valid = TRUE;
253 purple_private->transports = g_slist_prepend(purple_private->transports,
254 transport);
256 if (setup->type == SIPE_TRANSPORT_TLS) {
257 /* SSL case */
258 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
260 if ((transport->gsc = purple_ssl_connect(account,
261 setup->server_name,
262 setup->server_port,
263 transport_ssl_connected,
264 transport_ssl_connect_failure,
265 transport)) == NULL) {
266 setup->error(SIPE_TRANSPORT_CONNECTION,
267 _("Could not create SSL context"));
268 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
269 return(NULL);
271 } else if (setup->type == SIPE_TRANSPORT_TCP) {
272 /* TCP case */
273 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
275 if ((transport->proxy = purple_proxy_connect(gc, account,
276 setup->server_name,
277 setup->server_port,
278 transport_tcp_connected,
279 transport)) == NULL) {
280 setup->error(SIPE_TRANSPORT_CONNECTION,
281 _("Could not create socket"));
282 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
283 return(NULL);
285 } else {
286 setup->error(SIPE_TRANSPORT_CONNECTION,
287 "This should not happen...");
288 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
289 return(NULL);
292 return(SIPE_TRANSPORT_CONNECTION);
295 static gboolean transport_deferred_destroy(gpointer user_data)
298 * All pending events on transport have been processed.
299 * Now it is safe to destroy the data structure.
301 SIPE_DEBUG_INFO("transport_deferred_destroy: %p", user_data);
302 g_free(user_data);
303 return(FALSE);
306 void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn)
308 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
309 struct sipe_backend_private *purple_private;
311 if (!transport || !transport->is_valid) return;
313 purple_private = transport->purple_private;
314 purple_private->transports = g_slist_remove(purple_private->transports,
315 transport);
317 if (transport->gsc) {
318 purple_ssl_close(transport->gsc);
319 } else if (transport->socket > 0) {
320 close(transport->socket);
323 if (transport->proxy)
324 purple_proxy_connect_cancel(transport->proxy);
326 if (transport->transmit_handler)
327 purple_input_remove(transport->transmit_handler);
328 if (transport->receive_handler)
329 purple_input_remove(transport->receive_handler);
331 if (transport->transmit_buffer)
332 #if PURPLE_VERSION_CHECK(3,0,0)
333 g_object_unref(transport->transmit_buffer);
334 #else
335 purple_circ_buffer_destroy(transport->transmit_buffer);
336 #endif
337 g_free(transport->public.buffer);
339 /* defer deletion of transport data structure to idle callback */
340 transport->is_valid = FALSE;
341 g_idle_add(transport_deferred_destroy, transport);
344 void sipe_purple_transport_close_all(struct sipe_backend_private *purple_private)
346 GSList *entry;
347 SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_transport_close_all: entered");
348 while ((entry = purple_private->transports) != NULL)
349 sipe_backend_transport_disconnect(entry->data);
352 /* returns FALSE on write error */
353 static gboolean transport_write(struct sipe_transport_purple *transport)
355 gsize max_write;
357 max_write = purple_circular_buffer_get_max_read(transport->transmit_buffer);
358 if (max_write > 0) {
359 gssize written = transport->gsc ?
360 (gssize) purple_ssl_write(transport->gsc,
361 purple_circular_buffer_get_output(transport->transmit_buffer),
362 max_write) :
363 write(transport->socket,
364 purple_circular_buffer_get_output(transport->transmit_buffer),
365 max_write);
367 if (written < 0 && errno == EAGAIN) {
368 return TRUE;
369 } else if (written <= 0) {
370 SIPE_DEBUG_ERROR("Write error: %s (%d)", strerror(errno), errno);
371 transport->error(SIPE_TRANSPORT_CONNECTION,
372 _("Write error"));
373 return FALSE;
376 purple_circular_buffer_mark_read(transport->transmit_buffer,
377 written);
379 } else {
380 /* buffer is empty -> stop sending */
381 purple_input_remove(transport->transmit_handler);
382 transport->transmit_handler = 0;
385 return TRUE;
388 static void transport_canwrite_cb(gpointer data,
389 SIPE_UNUSED_PARAMETER gint source,
390 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
392 struct sipe_transport_purple *transport = data;
394 /* Ignore spurious "can write" events after disconnect */
395 if (transport->is_valid)
396 transport_write(data);
399 void sipe_backend_transport_message(struct sipe_transport_connection *conn,
400 const gchar *buffer)
402 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
404 /* add packet to circular buffer */
405 purple_circular_buffer_append(transport->transmit_buffer,
406 buffer, strlen(buffer));
408 /* initiate transmission */
409 if (!transport->transmit_handler) {
410 transport->transmit_handler = purple_input_add(transport->socket,
411 PURPLE_INPUT_WRITE,
412 transport_canwrite_cb,
413 transport);
417 void sipe_backend_transport_flush(struct sipe_transport_connection *conn)
419 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
421 while ( purple_circular_buffer_get_max_read(transport->transmit_buffer)
422 && transport_write(transport));
426 Local Variables:
427 mode: c
428 c-file-style: "bsd"
429 indent-tabs-mode: t
430 tab-width: 8
431 End: