purple: take renamed API 3.x.x headers in use
[siplcs.git] / src / purple / purple-transport.c
blobb93b873f1913ec0c3d0b62361d4dc38f0a5a0001
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 #endif
51 #ifdef _WIN32
52 /* wrappers for write() & friends for socket handling */
53 #include "win32/win32dep.h"
54 #endif
56 #include "purple-private.h"
58 #include "sipe-backend.h"
59 #include "sipe-core.h"
60 #include "sipe-nls.h"
62 struct sipe_transport_purple {
63 /* public part shared with core */
64 struct sipe_transport_connection public;
66 /* purple private part */
67 struct sipe_backend_private *purple_private;
68 transport_connected_cb *connected;
69 transport_input_cb *input;
70 transport_error_cb *error;
71 PurpleSslConnection *gsc;
72 PurpleProxyConnectData *proxy;
73 PurpleCircBuffer *transmit_buffer;
74 guint transmit_handler;
75 guint receive_handler;
76 int socket;
78 gboolean is_valid;
81 #define PURPLE_TRANSPORT ((struct sipe_transport_purple *) conn)
82 #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport)
84 #define BUFFER_SIZE_INCREMENT 4096
88 /*****************************************************************************
90 * Common transport handling
92 *****************************************************************************/
93 static void transport_common_input(struct sipe_transport_purple *transport)
95 struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION;
96 gssize readlen, len;
97 gboolean firstread = TRUE;
99 /* Read all available data from the connection */
100 do {
101 /* Increase input buffer size as needed */
102 if (conn->buffer_length < conn->buffer_used + BUFFER_SIZE_INCREMENT) {
103 conn->buffer_length += BUFFER_SIZE_INCREMENT;
104 conn->buffer = g_realloc(conn->buffer, conn->buffer_length);
105 SIPE_DEBUG_INFO("transport_input_common: new buffer length %" G_GSIZE_FORMAT,
106 conn->buffer_length);
109 /* Try to read as much as there is space left in the buffer */
110 /* minus 1 for the string terminator */
111 readlen = conn->buffer_length - conn->buffer_used - 1;
112 len = transport->gsc ?
113 (gssize) purple_ssl_read(transport->gsc,
114 conn->buffer + conn->buffer_used,
115 readlen) :
116 read(transport->socket,
117 conn->buffer + conn->buffer_used,
118 readlen);
120 if (len < 0 && errno == EAGAIN) {
121 /* Try again later */
122 return;
123 } else if (len < 0) {
124 SIPE_DEBUG_ERROR("Read error: %s (%d)", strerror(errno), errno);
125 transport->error(SIPE_TRANSPORT_CONNECTION, _("Read error"));
126 return;
127 } else if (firstread && (len == 0)) {
128 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
129 transport->error(SIPE_TRANSPORT_CONNECTION, _("Server has disconnected"));
130 return;
133 conn->buffer_used += len;
134 firstread = FALSE;
136 /* Equivalence indicates that there is possibly more data to read */
137 } while (len == readlen);
139 conn->buffer[conn->buffer_used] = '\0';
140 transport->input(conn);
143 static void transport_ssl_input(gpointer data,
144 SIPE_UNUSED_PARAMETER PurpleSslConnection *gsc,
145 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
147 struct sipe_transport_purple *transport = data;
149 /* Ignore spurious "SSL input" events after disconnect */
150 if (transport->is_valid)
151 transport_common_input(transport);
154 static void transport_tcp_input(gpointer data,
155 SIPE_UNUSED_PARAMETER gint source,
156 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
158 struct sipe_transport_purple *transport = data;
160 /* Ignore spurious "TCP input" events after disconnect */
161 if (transport->is_valid)
162 transport_common_input(transport);
165 static void transport_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection *gsc,
166 PurpleSslErrorType error,
167 gpointer data)
169 struct sipe_transport_purple *transport = data;
171 /* Ignore spurious "SSL connect failure" events after disconnect */
172 if (transport->is_valid) {
173 transport->socket = -1;
174 transport->gsc = NULL;
175 transport->error(SIPE_TRANSPORT_CONNECTION,
176 purple_ssl_strerror(error));
177 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
181 static void transport_common_connected(struct sipe_transport_purple *transport,
182 int fd)
184 /* Ignore spurious "connected" events after disconnect */
185 if (transport->is_valid) {
187 transport->proxy = NULL;
189 if (fd < 0) {
190 transport->error(SIPE_TRANSPORT_CONNECTION,
191 _("Could not connect"));
192 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
193 return;
196 transport->socket = fd;
197 transport->public.client_port = purple_network_get_port_from_fd(fd);
199 if (transport->gsc) {
200 purple_ssl_input_add(transport->gsc, transport_ssl_input, transport);
201 } else {
202 transport->receive_handler = purple_input_add(fd,
203 PURPLE_INPUT_READ,
204 transport_tcp_input,
205 transport);
208 transport->connected(SIPE_TRANSPORT_CONNECTION);
212 static void transport_ssl_connected(gpointer data,
213 PurpleSslConnection *gsc,
214 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
216 transport_common_connected(data, gsc->fd);
219 static void transport_tcp_connected(gpointer data,
220 gint source,
221 SIPE_UNUSED_PARAMETER const gchar *error_message)
223 transport_common_connected(data, source);
226 struct sipe_transport_connection *
227 sipe_backend_transport_connect(struct sipe_core_public *sipe_public,
228 const sipe_connect_setup *setup)
230 struct sipe_transport_purple *transport = g_new0(struct sipe_transport_purple, 1);
231 struct sipe_backend_private *purple_private = sipe_public->backend_private;
232 PurpleConnection *gc = purple_private->gc;
233 PurpleAccount *account = purple_connection_get_account(gc);
235 SIPE_DEBUG_INFO("transport_connect - hostname: %s port: %d",
236 setup->server_name, setup->server_port);
238 transport->public.type = setup->type;
239 transport->public.user_data = setup->user_data;
240 transport->purple_private = purple_private;
241 transport->connected = setup->connected;
242 transport->input = setup->input;
243 transport->error = setup->error;
244 transport->transmit_buffer = purple_circ_buffer_new(0);
245 transport->is_valid = TRUE;
247 purple_private->transports = g_slist_prepend(purple_private->transports,
248 transport);
250 if (setup->type == SIPE_TRANSPORT_TLS) {
251 /* SSL case */
252 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
254 if ((transport->gsc = purple_ssl_connect(account,
255 setup->server_name,
256 setup->server_port,
257 transport_ssl_connected,
258 transport_ssl_connect_failure,
259 transport)) == NULL) {
260 setup->error(SIPE_TRANSPORT_CONNECTION,
261 _("Could not create SSL context"));
262 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
263 return(NULL);
265 } else if (setup->type == SIPE_TRANSPORT_TCP) {
266 /* TCP case */
267 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
269 if ((transport->proxy = purple_proxy_connect(gc, account,
270 setup->server_name,
271 setup->server_port,
272 transport_tcp_connected,
273 transport)) == NULL) {
274 setup->error(SIPE_TRANSPORT_CONNECTION,
275 _("Could not create socket"));
276 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
277 return(NULL);
279 } else {
280 setup->error(SIPE_TRANSPORT_CONNECTION,
281 "This should not happen...");
282 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
283 return(NULL);
286 return(SIPE_TRANSPORT_CONNECTION);
289 static gboolean transport_deferred_destroy(gpointer user_data)
292 * All pending events on transport have been processed.
293 * Now it is safe to destroy the data structure.
295 SIPE_DEBUG_INFO("transport_deferred_destroy: %p", user_data);
296 g_free(user_data);
297 return(FALSE);
300 void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn)
302 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
303 struct sipe_backend_private *purple_private;
305 if (!transport || !transport->is_valid) return;
307 purple_private = transport->purple_private;
308 purple_private->transports = g_slist_remove(purple_private->transports,
309 transport);
311 if (transport->gsc) {
312 purple_ssl_close(transport->gsc);
313 } else if (transport->socket > 0) {
314 close(transport->socket);
317 if (transport->proxy)
318 purple_proxy_connect_cancel(transport->proxy);
320 if (transport->transmit_handler)
321 purple_input_remove(transport->transmit_handler);
322 if (transport->receive_handler)
323 purple_input_remove(transport->receive_handler);
325 if (transport->transmit_buffer)
326 purple_circ_buffer_destroy(transport->transmit_buffer);
327 g_free(transport->public.buffer);
329 /* defer deletion of transport data structure to idle callback */
330 transport->is_valid = FALSE;
331 g_idle_add(transport_deferred_destroy, transport);
334 void sipe_purple_transport_close_all(struct sipe_backend_private *purple_private)
336 GSList *entry;
337 SIPE_DEBUG_INFO_NOFORMAT("sipe_purple_transport_close_all: entered");
338 while ((entry = purple_private->transports) != NULL)
339 sipe_backend_transport_disconnect(entry->data);
342 /* returns FALSE on write error */
343 static gboolean transport_write(struct sipe_transport_purple *transport)
345 gsize max_write;
347 max_write = purple_circ_buffer_get_max_read(transport->transmit_buffer);
348 if (max_write > 0) {
349 gssize written = transport->gsc ?
350 (gssize) purple_ssl_write(transport->gsc,
351 transport->transmit_buffer->outptr,
352 max_write) :
353 write(transport->socket,
354 transport->transmit_buffer->outptr,
355 max_write);
357 if (written < 0 && errno == EAGAIN) {
358 return TRUE;
359 } else if (written <= 0) {
360 SIPE_DEBUG_ERROR("Write error: %s (%d)", strerror(errno), errno);
361 transport->error(SIPE_TRANSPORT_CONNECTION,
362 _("Write error"));
363 return FALSE;
366 purple_circ_buffer_mark_read(transport->transmit_buffer,
367 written);
369 } else {
370 /* buffer is empty -> stop sending */
371 purple_input_remove(transport->transmit_handler);
372 transport->transmit_handler = 0;
375 return TRUE;
378 static void transport_canwrite_cb(gpointer data,
379 SIPE_UNUSED_PARAMETER gint source,
380 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
382 struct sipe_transport_purple *transport = data;
384 /* Ignore spurious "can write" events after disconnect */
385 if (transport->is_valid)
386 transport_write(data);
389 void sipe_backend_transport_message(struct sipe_transport_connection *conn,
390 const gchar *buffer)
392 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
394 /* add packet to circular buffer */
395 purple_circ_buffer_append(transport->transmit_buffer,
396 buffer, strlen(buffer));
398 /* initiate transmission */
399 if (!transport->transmit_handler) {
400 transport->transmit_handler = purple_input_add(transport->socket,
401 PURPLE_INPUT_WRITE,
402 transport_canwrite_cb,
403 transport);
407 void sipe_backend_transport_flush(struct sipe_transport_connection *conn)
409 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
411 while ( purple_circ_buffer_get_max_read(transport->transmit_buffer)
412 && transport_write(transport));
416 Local Variables:
417 mode: c
418 c-file-style: "bsd"
419 indent-tabs-mode: t
420 tab-width: 8
421 End: