2 * @file purple-transport.c
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
36 #include "sipe-common.h"
38 #include "connection.h"
39 #include "eventloop.h"
45 #if PURPLE_VERSION_CHECK(3,0,0)
46 #include "circularbuffer.h"
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)
58 /* wrappers for write() & friends for socket handling */
59 #include "win32/win32dep.h"
62 #include "purple-private.h"
64 #include "sipe-backend.h"
65 #include "sipe-core.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
;
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
;
103 gboolean firstread
= TRUE
;
105 /* Read all available data from the connection */
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
,
122 read(transport
->socket
,
123 conn
->buffer
+ conn
->buffer_used
,
126 if (len
< 0 && errno
== EAGAIN
) {
127 /* Try again later */
129 } else if (len
< 0) {
130 SIPE_DEBUG_ERROR("Read error: %s (%d)", strerror(errno
), errno
);
131 transport
->error(SIPE_TRANSPORT_CONNECTION
, _("Read error"));
133 } else if (firstread
&& (len
== 0)) {
134 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
135 transport
->error(SIPE_TRANSPORT_CONNECTION
, _("Server has disconnected"));
139 conn
->buffer_used
+= len
;
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
,
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
,
190 /* Ignore spurious "connected" events after disconnect */
191 if (transport
->is_valid
) {
193 transport
->proxy
= NULL
;
196 transport
->error(SIPE_TRANSPORT_CONNECTION
,
197 _("Could not connect"));
198 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION
);
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
);
208 transport
->receive_handler
= purple_input_add(fd
,
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
,
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
,
256 if (setup
->type
== SIPE_TRANSPORT_TLS
) {
258 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
260 if ((transport
->gsc
= purple_ssl_connect(account
,
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
);
271 } else if (setup
->type
== SIPE_TRANSPORT_TCP
) {
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
,
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
);
294 setup
->error(SIPE_TRANSPORT_CONNECTION
,
295 "This should not happen...");
296 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION
);
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
);
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
,
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
);
343 purple_circ_buffer_destroy(transport
->transmit_buffer
);
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
)
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
)
365 max_write
= purple_circular_buffer_get_max_read(transport
->transmit_buffer
);
367 gssize written
= transport
->gsc
?
368 (gssize
) purple_ssl_write(transport
->gsc
,
369 purple_circular_buffer_get_output(transport
->transmit_buffer
),
371 write(transport
->socket
,
372 purple_circular_buffer_get_output(transport
->transmit_buffer
),
375 if (written
< 0 && errno
== EAGAIN
) {
377 } else if (written
<= 0) {
378 SIPE_DEBUG_ERROR("Write error: %s (%d)", strerror(errno
), errno
);
379 transport
->error(SIPE_TRANSPORT_CONNECTION
,
384 purple_circular_buffer_mark_read(transport
->transmit_buffer
,
388 /* buffer is empty -> stop sending */
389 purple_input_remove(transport
->transmit_handler
);
390 transport
->transmit_handler
= 0;
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
,
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
,
420 transport_canwrite_cb
,
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
));