OBS: updates for Ubuntu 16.04
[siplcs.git] / src / purple / purple-transport.c
blob036b13023756c7782367c82bb03cc56d2fa27615
1 /**
2 * @file purple-transport.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2015 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");
276 * NOTE: during shutdown libpurple calls
278 * purple_proxy_connect_cancel_with_handle(gc);
280 * before our cleanup code. Therefore we can't use "gc" as
281 * handle. We are not using it for anything thus NULL is fine.
283 if ((transport->proxy = purple_proxy_connect(NULL, account,
284 setup->server_name,
285 setup->server_port,
286 transport_tcp_connected,
287 transport)) == NULL) {
288 setup->error(SIPE_TRANSPORT_CONNECTION,
289 _("Could not create socket"));
290 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
291 return(NULL);
293 } else {
294 setup->error(SIPE_TRANSPORT_CONNECTION,
295 "This should not happen...");
296 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
297 return(NULL);
300 return(SIPE_TRANSPORT_CONNECTION);
303 static gboolean transport_deferred_destroy(gpointer user_data)
306 * All pending events on transport have been processed.
307 * Now it is safe to destroy the data structure.
309 SIPE_DEBUG_INFO("transport_deferred_destroy: %p", user_data);
310 g_free(user_data);
311 return(FALSE);
314 void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn)
316 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
317 struct sipe_backend_private *purple_private;
319 if (!transport || !transport->is_valid) return;
321 purple_private = transport->purple_private;
322 purple_private->transports = g_slist_remove(purple_private->transports,
323 transport);
325 if (transport->gsc) {
326 purple_ssl_close(transport->gsc);
327 } else if (transport->socket > 0) {
328 close(transport->socket);
331 if (transport->proxy)
332 purple_proxy_connect_cancel(transport->proxy);
334 if (transport->transmit_handler)
335 purple_input_remove(transport->transmit_handler);
336 if (transport->receive_handler)
337 purple_input_remove(transport->receive_handler);
339 if (transport->transmit_buffer)
340 #if PURPLE_VERSION_CHECK(3,0,0)
341 g_object_unref(transport->transmit_buffer);
342 #else
343 purple_circ_buffer_destroy(transport->transmit_buffer);
344 #endif
345 g_free(transport->public.buffer);
347 /* defer deletion of transport data structure to idle callback */
348 transport->is_valid = FALSE;
349 g_idle_add(transport_deferred_destroy, transport);
352 void sipe_purple_transport_close_all(struct sipe_backend_private *purple_private)
354 GSList *entry;
355 SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_transport_close_all: entered");
356 while ((entry = purple_private->transports) != NULL)
357 sipe_backend_transport_disconnect(entry->data);
360 /* returns FALSE on write error */
361 static gboolean transport_write(struct sipe_transport_purple *transport)
363 gsize max_write;
365 max_write = purple_circular_buffer_get_max_read(transport->transmit_buffer);
366 if (max_write > 0) {
367 gssize written = transport->gsc ?
368 (gssize) purple_ssl_write(transport->gsc,
369 purple_circular_buffer_get_output(transport->transmit_buffer),
370 max_write) :
371 write(transport->socket,
372 purple_circular_buffer_get_output(transport->transmit_buffer),
373 max_write);
375 if (written < 0 && errno == EAGAIN) {
376 return TRUE;
377 } else if (written <= 0) {
378 SIPE_DEBUG_ERROR("Write error: %s (%d)", strerror(errno), errno);
379 transport->error(SIPE_TRANSPORT_CONNECTION,
380 _("Write error"));
381 return FALSE;
384 purple_circular_buffer_mark_read(transport->transmit_buffer,
385 written);
387 } else {
388 /* buffer is empty -> stop sending */
389 purple_input_remove(transport->transmit_handler);
390 transport->transmit_handler = 0;
393 return TRUE;
396 static void transport_canwrite_cb(gpointer data,
397 SIPE_UNUSED_PARAMETER gint source,
398 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
400 struct sipe_transport_purple *transport = data;
402 /* Ignore spurious "can write" events after disconnect */
403 if (transport->is_valid)
404 transport_write(data);
407 void sipe_backend_transport_message(struct sipe_transport_connection *conn,
408 const gchar *buffer)
410 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
412 /* add packet to circular buffer */
413 purple_circular_buffer_append(transport->transmit_buffer,
414 buffer, strlen(buffer));
416 /* initiate transmission */
417 if (!transport->transmit_handler) {
418 transport->transmit_handler = purple_input_add(transport->socket,
419 PURPLE_INPUT_WRITE,
420 transport_canwrite_cb,
421 transport);
425 void sipe_backend_transport_flush(struct sipe_transport_connection *conn)
427 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
429 while ( purple_circular_buffer_get_max_read(transport->transmit_buffer)
430 && transport_write(transport));
434 Local Variables:
435 mode: c
436 c-file-style: "bsd"
437 indent-tabs-mode: t
438 tab-width: 8
439 End: