2 * @file purple-transport.c
6 * Copyright (C) 2010 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
34 #include "sipe-common.h"
36 #include "circbuffer.h"
37 #include "connection.h"
38 #include "eventloop.h"
43 #include "purple-private.h"
45 #include "sipe-backend.h"
46 #include "sipe-core.h"
49 struct transport_hooks
;
50 struct sipe_transport_purple
{
51 /* public part shared with core */
52 struct sipe_transport_connection
public;
54 /* purple private part */
55 const struct transport_hooks
*hooks
;
57 PurpleSslConnection
*gsc
;
58 PurpleCircBuffer
*transmit_buffer
;
59 guint transmit_handler
;
60 guint receive_handler
;
64 struct transport_hooks
66 void (*input_error
)(struct sipe_transport_purple
*transport
,
68 void (*input_message
)(struct sipe_transport_connection
*conn
);
70 gboolean (*ssl_check
)(struct sipe_transport_purple
*transport
,
72 gboolean (*connected_check
)(struct sipe_transport_purple
*transport
,
73 PurpleSslConnection
*gsc
,
75 PurpleSslInputFunction input_ssl
;
76 PurpleInputFunction input_tcp
;
77 void (*connected
)(struct sipe_transport_connection
*conn
);
79 void (*write_error
)(struct sipe_transport_purple
*transport
,
83 #define PURPLE_TRANSPORT ((struct sipe_transport_purple *) conn)
84 #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport)
86 #define BUFFER_SIZE_INCREMENT 4096
90 /*****************************************************************************
92 * Common transport handling
94 *****************************************************************************/
95 static void transport_input_common(struct sipe_transport_purple
*transport
)
97 struct sipe_transport_connection
*conn
= SIPE_TRANSPORT_CONNECTION
;
99 gboolean firstread
= TRUE
;
101 /* Read all available data from the connection */
103 /* Increase input buffer size as needed */
104 if (conn
->buffer_length
< conn
->buffer_used
+ BUFFER_SIZE_INCREMENT
) {
105 conn
->buffer_length
+= BUFFER_SIZE_INCREMENT
;
106 conn
->buffer
= g_realloc(conn
->buffer
, conn
->buffer_length
);
107 SIPE_DEBUG_INFO("transport_input_common: new buffer length %" G_GSIZE_FORMAT
,
108 conn
->buffer_length
);
111 /* Try to read as much as there is space left in the buffer */
112 /* minus 1 for the string terminator */
113 readlen
= conn
->buffer_length
- conn
->buffer_used
- 1;
114 len
= transport
->gsc
?
115 (gssize
) purple_ssl_read(transport
->gsc
,
116 conn
->buffer
+ conn
->buffer_used
,
118 read(transport
->socket
,
119 conn
->buffer
+ conn
->buffer_used
,
122 if (len
< 0 && errno
== EAGAIN
) {
123 /* Try again later */
125 } else if (len
< 0) {
126 SIPE_DEBUG_ERROR_NOFORMAT("Read error");
127 transport
->hooks
->input_error(transport
, _("Read error"));
129 } else if (firstread
&& (len
== 0)) {
130 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
131 transport
->hooks
->input_error(transport
, _("Server has disconnected"));
135 conn
->buffer_used
+= len
;
138 /* Equivalence indicates that there is possibly more data to read */
139 } while (len
== readlen
);
141 conn
->buffer
[conn
->buffer_used
] = '\0';
142 transport
->hooks
->input_message(conn
);
145 static void transport_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
146 PurpleSslErrorType error
,
149 struct sipe_transport_purple
*transport
= data
;
150 const gchar
*msg
= purple_ssl_strerror(error
);
152 if (transport
->hooks
->ssl_check
&&
153 transport
->hooks
->ssl_check(transport
, msg
))
156 transport
->socket
= -1;
157 transport
->gsc
= NULL
;
160 static void transport_connected_common(struct sipe_transport_purple
*transport
,
161 PurpleSslConnection
*gsc
,
164 if (transport
->hooks
->connected_check(transport
, gsc
, fd
))
167 transport
->socket
= fd
;
168 transport
->public.client_port
= purple_network_get_port_from_fd(fd
);
171 transport
->gsc
= gsc
;
172 purple_ssl_input_add(gsc
, transport
->hooks
->input_ssl
, transport
);
174 transport
->receive_handler
= purple_input_add(fd
,
176 transport
->hooks
->input_tcp
,
180 transport
->hooks
->connected(SIPE_TRANSPORT_CONNECTION
);
183 static void transport_connected_ssl_cb(gpointer data
,
184 PurpleSslConnection
*gsc
,
185 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
187 transport_connected_common(data
, gsc
, gsc
? gsc
->fd
: -1);
190 static void transport_connected_tcp_cb(gpointer data
,
192 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
194 transport_connected_common(data
, NULL
, source
);
197 static const gchar
*transport_connect(struct sipe_core_public
*sipe_public
,
198 struct sipe_transport_purple
*transport
,
200 const gchar
*server_name
,
203 struct sipe_backend_private
*purple_private
= sipe_public
->backend_private
;
204 PurpleConnection
*gc
= purple_private
->gc
;
205 PurpleAccount
*account
= purple_connection_get_account(gc
);
207 SIPE_DEBUG_INFO("transport_connect - hostname: %s port: %d",
208 server_name
, server_port
);
210 transport
->public.type
= type
;
212 transport
->transmit_buffer
= purple_circ_buffer_new(0);
214 if (type
== SIPE_TRANSPORT_TLS
) {
216 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
218 if (purple_ssl_connect(account
,
221 transport_connected_ssl_cb
,
222 transport_ssl_connect_failure
,
223 transport
) == NULL
) {
225 return(_("Could not create SSL context"));
227 } else if (type
== SIPE_TRANSPORT_TCP
) {
229 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
231 if (purple_proxy_connect(gc
, account
,
234 transport_connected_tcp_cb
,
235 transport
) == NULL
) {
237 return(_("Could not create socket"));
241 return("This should not happen...");
247 static void transport_disconnect(struct sipe_transport_purple
*transport
)
249 if (!transport
) return;
251 if (transport
->gsc
) {
252 purple_ssl_close(transport
->gsc
);
253 } else if (transport
->socket
> 0) {
254 close(transport
->socket
);
257 if (transport
->transmit_handler
)
258 purple_input_remove(transport
->transmit_handler
);
259 if (transport
->receive_handler
)
260 purple_input_remove(transport
->receive_handler
);
262 if (transport
->transmit_buffer
)
263 purple_circ_buffer_destroy(transport
->transmit_buffer
);
264 g_free(transport
->public.buffer
);
269 static void transport_canwrite_cb(gpointer data
,
270 SIPE_UNUSED_PARAMETER gint source
,
271 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
273 struct sipe_transport_purple
*transport
= data
;
276 max_write
= purple_circ_buffer_get_max_read(transport
->transmit_buffer
);
278 gssize written
= transport
->gsc
?
279 (gssize
) purple_ssl_write(transport
->gsc
,
280 transport
->transmit_buffer
->outptr
,
282 write(transport
->socket
,
283 transport
->transmit_buffer
->outptr
,
286 if (written
< 0 && errno
== EAGAIN
) {
288 } else if (written
<= 0) {
289 SIPE_DEBUG_INFO_NOFORMAT("transport_canwrite_cb: written <= 0, exiting");
290 if (transport
->hooks
->write_error
)
291 transport
->hooks
->write_error(transport
,
296 purple_circ_buffer_mark_read(transport
->transmit_buffer
,
300 /* buffer is empty -> stop sending */
301 purple_input_remove(transport
->transmit_handler
);
302 transport
->transmit_handler
= 0;
306 static void transport_message(struct sipe_transport_purple
*transport
,
309 time_t currtime
= time(NULL
);
312 SIPE_DEBUG_INFO("sending - %s######\n%s######",
313 ctime(&currtime
), tmp
= fix_newlines(buffer
));
316 /* add packet to circular buffer */
317 purple_circ_buffer_append(transport
->transmit_buffer
,
318 buffer
, strlen(buffer
));
320 /* initiate transmission */
321 if (!transport
->transmit_handler
) {
322 transport
->transmit_handler
= purple_input_add(transport
->socket
,
324 transport_canwrite_cb
,
330 /*****************************************************************************
332 * SIP transport handling
334 *****************************************************************************/
335 static void transport_sip_input_error(struct sipe_transport_purple
*transport
,
338 purple_connection_error_reason(transport
->gc
,
339 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
341 sipe_backend_transport_sip_disconnect(SIPE_TRANSPORT_CONNECTION
);
344 static void transport_sip_input_ssl(gpointer data
,
345 PurpleSslConnection
*gsc
,
346 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
348 struct sipe_transport_purple
*transport
= data
;
350 /* NOTE: This check *IS* necessary */
351 if (!PURPLE_CONNECTION_IS_VALID(transport
->gc
)) {
352 purple_ssl_close(gsc
);
353 transport
->gsc
= NULL
;
356 transport_input_common(transport
);
359 static void transport_sip_input_tcp(gpointer data
,
361 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
363 struct sipe_transport_purple
*transport
= data
;
365 /* NOTE: This check *IS* necessary */
366 if (!PURPLE_CONNECTION_IS_VALID(transport
->gc
)) {
368 transport
->socket
= -1;
371 transport_input_common(transport
);
374 static gboolean
transport_sip_ssl_check(struct sipe_transport_purple
*transport
,
377 /* If the connection is already disconnected
378 then we don't need to do anything else */
379 if (!PURPLE_CONNECTION_IS_VALID(transport
->gc
))
382 purple_connection_error_reason(transport
->gc
,
383 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
388 static gboolean
transport_sip_connected_check(struct sipe_transport_purple
*transport
,
389 PurpleSslConnection
*gsc
,
392 PurpleConnection
*gc
= transport
->gc
;
394 if (!PURPLE_CONNECTION_IS_VALID(gc
))
397 purple_ssl_close(gsc
);
398 } else if (fd
>= 0) {
405 purple_connection_error_reason(gc
,
406 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
407 _("Could not connect"));
411 PURPLE_GC_TO_SIPE_CORE_PUBLIC
->backend_private
->last_keepalive
= time(NULL
);
415 static void transport_sip_write_error(struct sipe_transport_purple
*transport
,
418 purple_connection_error_reason(transport
->gc
,
419 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
423 static const struct transport_hooks transport_sip_hooks
= {
424 transport_sip_input_error
,
425 sipe_core_transport_sip_message
,
426 transport_sip_ssl_check
,
427 transport_sip_connected_check
,
428 transport_sip_input_ssl
,
429 transport_sip_input_tcp
,
430 sipe_core_transport_sip_connected
,
431 transport_sip_write_error
434 struct sipe_transport_connection
*sipe_backend_transport_sip_connect(struct sipe_core_public
*sipe_public
,
436 const gchar
*server_name
,
439 struct sipe_transport_purple
*transport
= g_new0(struct sipe_transport_purple
, 1);
442 transport
->hooks
= &transport_sip_hooks
;
444 msg
= transport_connect(sipe_public
, transport
,
445 type
, server_name
, server_port
);
448 purple_connection_error_reason(sipe_public
->backend_private
->gc
,
449 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
453 return(SIPE_TRANSPORT_CONNECTION
);
457 void sipe_backend_transport_sip_disconnect(struct sipe_transport_connection
*conn
)
459 struct sipe_transport_purple
*transport
= PURPLE_TRANSPORT
;
462 struct sipe_core_public
*sipe_public
= transport
->gc
->proto_data
;
463 transport_disconnect(transport
);
464 sipe_public
->transport
= NULL
;
468 void sipe_backend_transport_sip_message(struct sipe_transport_connection
*conn
,
471 transport_message(PURPLE_TRANSPORT
, buffer
);
475 /*****************************************************************************
477 * HTTP transport handling
479 *****************************************************************************/
480 static void transport_http_input_error(struct sipe_transport_purple
*transport
,
483 sipe_core_transport_http_input_error(SIPE_TRANSPORT_CONNECTION
, msg
);
486 static void transport_http_input_ssl(gpointer data
,
487 SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
488 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
490 transport_input_common(data
);
493 static void transport_http_input_tcp(gpointer data
,
494 SIPE_UNUSED_PARAMETER gint source
,
495 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
497 transport_input_common(data
);
500 static gboolean
transport_http_connected_check(SIPE_UNUSED_PARAMETER
struct sipe_transport_purple
*transport
,
501 SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
507 static const struct transport_hooks transport_http_hooks
= {
508 transport_http_input_error
,
509 sipe_core_transport_http_message
,
511 transport_http_connected_check
,
512 transport_http_input_ssl
,
513 transport_http_input_tcp
,
514 sipe_core_transport_http_connected
,
518 struct sipe_transport_connection
*sipe_backend_transport_http_connect(struct sipe_core_public
*sipe_public
,
520 const gchar
*server_name
,
523 struct sipe_transport_purple
*transport
= g_new0(struct sipe_transport_purple
, 1);
525 transport
->hooks
= &transport_http_hooks
;
527 return(transport_connect(sipe_public
, transport
,
528 type
, server_name
, server_port
) ?
529 NULL
: SIPE_TRANSPORT_CONNECTION
);
532 void sipe_backend_transport_http_disconnect(struct sipe_transport_connection
*conn
)
534 transport_disconnect(PURPLE_TRANSPORT
);
537 void sipe_backend_transport_http_message(struct sipe_transport_connection
*conn
,
540 transport_message(PURPLE_TRANSPORT
, buffer
);