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"
41 #include "purple-private.h"
43 #include "sipe-backend.h"
44 #include "sipe-core.h"
47 struct sipe_transport_purple
{
48 /* public part shared with core */
49 struct sipe_transport_connection
public;
51 /* purple private part */
53 PurpleSslConnection
*gsc
;
54 PurpleCircBuffer
*transmit_buffer
;
55 guint transmit_handler
;
56 guint receive_handler
;
59 #define PURPLE_TRANSPORT ((struct sipe_transport_purple *) conn)
60 #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport)
62 #define BUFFER_SIZE_INCREMENT 4096
65 /*****************************************************************************
67 * SIP transport handling
69 *****************************************************************************/
70 static void transport_sip_input_common(struct sipe_transport_purple
*transport
)
72 struct sipe_transport_connection
*conn
= SIPE_TRANSPORT_CONNECTION
;
74 gboolean firstread
= TRUE
;
76 /* Read all available data from the connection */
78 /* Increase input buffer size as needed */
79 if (conn
->buffer_length
< conn
->buffer_used
+ BUFFER_SIZE_INCREMENT
) {
80 conn
->buffer_length
+= BUFFER_SIZE_INCREMENT
;
81 conn
->buffer
= g_realloc(conn
->buffer
, conn
->buffer_length
);
82 SIPE_DEBUG_INFO("transport_sip_input_common: new buffer length %" G_GSIZE_FORMAT
,
86 /* Try to read as much as there is space left in the buffer */
87 /* minus 1 for the string terminator */
88 readlen
= conn
->buffer_length
- conn
->buffer_used
- 1;
89 len
= transport
->gsc
?
90 (gssize
) purple_ssl_read(transport
->gsc
,
91 conn
->buffer
+ conn
->buffer_used
,
93 read(transport
->socket
,
94 conn
->buffer
+ conn
->buffer_used
,
97 if (len
< 0 && errno
== EAGAIN
) {
100 } else if (len
< 0) {
101 SIPE_DEBUG_ERROR_NOFORMAT("Read error");
102 purple_connection_error_reason(transport
->gc
,
103 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
105 sipe_backend_transport_sip_disconnect(conn
);
107 } else if (firstread
&& (len
== 0)) {
108 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
109 purple_connection_error_reason(transport
->gc
,
110 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
111 _("Server has disconnected"));
112 sipe_backend_transport_sip_disconnect(conn
);
116 conn
->buffer_used
+= len
;
119 /* Equivalence indicates that there is possibly more data to read */
120 } while (len
== readlen
);
122 conn
->buffer
[conn
->buffer_used
] = '\0';
123 sipe_core_transport_sip_message(conn
);
126 static void transport_sip_input_ssl_cb(gpointer data
,
127 PurpleSslConnection
*gsc
,
128 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
130 struct sipe_transport_purple
*transport
= data
;
132 /* NOTE: This check *IS* necessary */
133 if (!PURPLE_CONNECTION_IS_VALID(transport
->gc
)) {
134 purple_ssl_close(gsc
);
137 transport_sip_input_common(transport
);
140 static void transport_sip_input_tcp_cb(gpointer data
,
142 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
144 struct sipe_transport_purple
*transport
= data
;
146 /* NOTE: This check *IS* necessary */
147 if (!PURPLE_CONNECTION_IS_VALID(transport
->gc
)) {
151 transport_sip_input_common(transport
);
154 static void transport_sip_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
155 PurpleSslErrorType error
,
158 struct sipe_transport_purple
*transport
= data
;
160 /* If the connection is already disconnected
161 then we don't need to do anything else */
162 if (!PURPLE_CONNECTION_IS_VALID(transport
->gc
))
165 sipe_core_transport_sip_ssl_connect_failure(data
);
167 transport
->socket
= -1;
168 transport
->gsc
= NULL
;
170 purple_connection_ssl_error(transport
->gc
, error
);
173 static void transport_sip_connected_common(struct sipe_transport_purple
*transport
,
174 PurpleSslConnection
*gsc
,
177 PurpleConnection
*gc
= transport
->gc
;
178 struct sipe_backend_private
*purple_private
;
180 if (!PURPLE_CONNECTION_IS_VALID(gc
))
183 purple_ssl_close(gsc
);
184 } else if (fd
>= 0) {
191 purple_connection_error_reason(gc
,
192 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
193 _("Could not connect"));
197 transport
->socket
= fd
;
198 transport
->public.client_port
= purple_network_get_port_from_fd(fd
);
199 purple_private
= PURPLE_GC_TO_SIPE_CORE_PUBLIC
->backend_private
;
200 purple_private
->last_keepalive
= time(NULL
);
203 transport
->gsc
= gsc
;
204 purple_ssl_input_add(gsc
, transport_sip_input_ssl_cb
, transport
);
206 transport
->receive_handler
= purple_input_add(fd
,
208 transport_sip_input_tcp_cb
,
212 sipe_core_transport_sip_connected(SIPE_TRANSPORT_CONNECTION
);
216 static void transport_sip_connected_ssl_cb(gpointer data
,
217 PurpleSslConnection
*gsc
,
218 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
220 transport_sip_connected_common(data
, gsc
, gsc
? gsc
->fd
: -1);
223 static void transport_sip_connected_tcp_cb(gpointer data
,
225 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
227 transport_sip_connected_common(data
, NULL
, source
);
230 struct sipe_transport_connection
*sipe_backend_transport_sip_connect(struct sipe_core_public
*sipe_public
,
232 const gchar
*server_name
,
235 struct sipe_backend_private
*purple_private
= sipe_public
->backend_private
;
236 PurpleConnection
*gc
= purple_private
->gc
;
237 PurpleAccount
*account
= purple_connection_get_account(gc
);
238 struct sipe_transport_purple
*transport
= g_new0(struct sipe_transport_purple
, 1);
240 SIPE_DEBUG_INFO("sipe_backend_transport_sip_connect - hostname: %s port: %d",
241 server_name
, server_port
);
243 transport
->public.type
= type
;
245 transport
->transmit_buffer
= purple_circ_buffer_new(0);
247 if (type
== SIPE_TRANSPORT_TLS
) {
249 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
251 if (purple_ssl_connect(account
,
254 transport_sip_connected_ssl_cb
,
255 transport_sip_ssl_connect_failure
,
256 transport
) == NULL
) {
257 purple_connection_error_reason(gc
,
258 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
259 _("Could not create SSL context"));
264 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
266 if (purple_proxy_connect(gc
, account
,
269 transport_sip_connected_tcp_cb
,
270 transport
) == NULL
) {
271 purple_connection_error_reason(gc
,
272 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
273 _("Could not create socket"));
278 return(SIPE_TRANSPORT_CONNECTION
);
281 void sipe_backend_transport_sip_disconnect(struct sipe_transport_connection
*conn
)
283 struct sipe_transport_purple
*transport
= PURPLE_TRANSPORT
;
285 if (!transport
) return;
287 if (transport
->gsc
) {
288 purple_ssl_close(transport
->gsc
);
289 } else if (transport
->socket
> 0) {
290 close(transport
->socket
);
293 if (transport
->transmit_handler
)
294 purple_input_remove(transport
->transmit_handler
);
295 if (transport
->receive_handler
)
296 purple_input_remove(transport
->receive_handler
);
298 if (transport
->transmit_buffer
)
299 purple_circ_buffer_destroy(transport
->transmit_buffer
);
300 g_free(transport
->public.buffer
);
304 ((struct sipe_core_public
*)transport
->gc
->proto_data
)->transport
= NULL
;
307 static void transport_sip_canwrite_cb(gpointer data
,
308 SIPE_UNUSED_PARAMETER gint source
,
309 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
311 struct sipe_transport_purple
*transport
= data
;
314 max_write
= purple_circ_buffer_get_max_read(transport
->transmit_buffer
);
316 gssize written
= transport
->gsc
?
317 (gssize
) purple_ssl_write(transport
->gsc
,
318 transport
->transmit_buffer
->outptr
,
320 write(transport
->socket
,
321 transport
->transmit_buffer
->outptr
,
324 if (written
< 0 && errno
== EAGAIN
) {
326 } else if (written
<= 0) {
327 purple_connection_error_reason(transport
->gc
,
328 PURPLE_CONNECTION_ERROR_NETWORK_ERROR
,
333 purple_circ_buffer_mark_read(transport
->transmit_buffer
,
337 /* buffer is empty -> stop sending */
338 purple_input_remove(transport
->transmit_handler
);
339 transport
->transmit_handler
= 0;
343 void sipe_backend_transport_sip_message(struct sipe_transport_connection
*conn
,
346 struct sipe_transport_purple
*transport
= PURPLE_TRANSPORT
;
347 time_t currtime
= time(NULL
);
350 SIPE_DEBUG_INFO("sending - %s######\n%s######",
351 ctime(&currtime
), tmp
= fix_newlines(buffer
));
354 /* add packet to circular buffer */
355 purple_circ_buffer_append(transport
->transmit_buffer
,
356 buffer
, strlen(buffer
));
358 /* initiate transmission */
359 if (!transport
->transmit_handler
) {
360 transport
->transmit_handler
= purple_input_add(transport
->socket
,
362 transport_sip_canwrite_cb
,
367 /*****************************************************************************
369 * SIP transport handling
371 *****************************************************************************/
372 static void transport_http_input_common(struct sipe_transport_purple
*transport
)
374 struct sipe_transport_connection
*conn
= SIPE_TRANSPORT_CONNECTION
;
376 gboolean firstread
= TRUE
;
378 /* Read all available data from the connection */
380 /* Increase input buffer size as needed */
381 if (conn
->buffer_length
< conn
->buffer_used
+ BUFFER_SIZE_INCREMENT
) {
382 conn
->buffer_length
+= BUFFER_SIZE_INCREMENT
;
383 conn
->buffer
= g_realloc(conn
->buffer
, conn
->buffer_length
);
384 SIPE_DEBUG_INFO("transport_sip_input_common: new buffer length %" G_GSIZE_FORMAT
,
385 conn
->buffer_length
);
388 /* Try to read as much as there is space left in the buffer */
389 /* minus 1 for the string terminator */
390 readlen
= conn
->buffer_length
- conn
->buffer_used
- 1;
391 len
= transport
->gsc
?
392 (gssize
) purple_ssl_read(transport
->gsc
,
393 conn
->buffer
+ conn
->buffer_used
,
395 read(transport
->socket
,
396 conn
->buffer
+ conn
->buffer_used
,
399 if (len
< 0 && errno
== EAGAIN
) {
400 /* Try again later */
402 } else if (len
< 0) {
403 sipe_core_transport_http_input_error(conn
,
406 } else if (firstread
&& (len
== 0)) {
407 sipe_core_transport_http_input_error(conn
,
408 _("Server has disconnected"));
412 conn
->buffer_used
+= len
;
415 /* Equivalence indicates that there is possibly more data to read */
416 } while (len
== readlen
);
418 conn
->buffer
[conn
->buffer_used
] = '\0';
419 sipe_core_transport_http_message(conn
);
422 static void transport_http_input_ssl_cb(gpointer data
,
423 SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
424 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
426 transport_http_input_common(data
);
429 static void transport_http_input_tcp_cb(gpointer data
,
430 SIPE_UNUSED_PARAMETER gint source
,
431 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
433 transport_http_input_common(data
);
436 static void transport_http_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
437 PurpleSslErrorType error
,
440 struct sipe_transport_purple
*transport
= data
;
441 const gchar
*message
= NULL
;
444 case PURPLE_SSL_CONNECT_FAILED
:
445 message
= "Connection failed";
447 case PURPLE_SSL_HANDSHAKE_FAILED
:
448 message
= "SSL handshake failed";
450 case PURPLE_SSL_CERTIFICATE_INVALID
:
451 message
= "SSL certificate invalid";
455 sipe_core_transport_http_ssl_connect_failure(data
, message
);
457 transport
->socket
= -1;
458 transport
->gsc
= NULL
;
461 static void transport_http_connected_common(struct sipe_transport_purple
*transport
,
462 PurpleSslConnection
*gsc
,
467 transport
->socket
= fd
;
468 transport
->public.client_port
= purple_network_get_port_from_fd(fd
);
471 transport
->gsc
= gsc
;
472 purple_ssl_input_add(gsc
, transport_http_input_ssl_cb
, transport
);
474 transport
->receive_handler
= purple_input_add(fd
,
476 transport_http_input_tcp_cb
,
480 sipe_core_transport_http_connected(SIPE_TRANSPORT_CONNECTION
);
484 static void transport_http_connected_ssl_cb(gpointer data
,
485 PurpleSslConnection
*gsc
,
486 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
488 transport_http_connected_common(data
, gsc
, gsc
? gsc
->fd
: -1);
491 static void transport_http_connected_tcp_cb(gpointer data
,
493 SIPE_UNUSED_PARAMETER
const gchar
*error_message
)
495 transport_http_connected_common(data
, NULL
, source
);
499 struct sipe_transport_connection
*sipe_backend_transport_http_connect(struct sipe_core_public
*sipe_public
,
501 const gchar
*server_name
,
504 struct sipe_backend_private
*purple_private
= sipe_public
->backend_private
;
505 PurpleConnection
*gc
= purple_private
->gc
;
506 PurpleAccount
*account
= purple_connection_get_account(gc
);
507 struct sipe_transport_purple
*transport
= g_new0(struct sipe_transport_purple
, 1);
509 SIPE_DEBUG_INFO("sipe_backend_transport_http_connect - hostname: %s port: %d",
510 server_name
, server_port
);
512 transport
->public.type
= type
;
513 transport
->transmit_buffer
= purple_circ_buffer_new(0);
515 if (type
== SIPE_TRANSPORT_TLS
) {
517 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
519 if (purple_ssl_connect(account
,
522 transport_http_connected_ssl_cb
,
523 transport_http_ssl_connect_failure
,
524 transport
) == NULL
) {
530 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
532 if (purple_proxy_connect(gc
, account
,
535 transport_http_connected_tcp_cb
,
536 transport
) == NULL
) {
542 return(SIPE_TRANSPORT_CONNECTION
);
545 void sipe_backend_transport_http_disconnect(struct sipe_transport_connection
*conn
)
547 struct sipe_transport_purple
*transport
= PURPLE_TRANSPORT
;
549 if (!transport
) return;
551 if (transport
->gsc
) {
552 purple_ssl_close(transport
->gsc
);
553 } else if (transport
->socket
> 0) {
554 close(transport
->socket
);
557 if (transport
->transmit_handler
)
558 purple_input_remove(transport
->transmit_handler
);
559 if (transport
->receive_handler
)
560 purple_input_remove(transport
->receive_handler
);
562 if (transport
->transmit_buffer
)
563 purple_circ_buffer_destroy(transport
->transmit_buffer
);
564 g_free(transport
->public.buffer
);
569 static void transport_http_canwrite_cb(gpointer data
,
570 SIPE_UNUSED_PARAMETER gint source
,
571 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
573 struct sipe_transport_purple
*transport
= data
;
576 max_write
= purple_circ_buffer_get_max_read(transport
->transmit_buffer
);
578 gssize written
= transport
->gsc
?
579 (gssize
) purple_ssl_write(transport
->gsc
,
580 transport
->transmit_buffer
->outptr
,
582 write(transport
->socket
,
583 transport
->transmit_buffer
->outptr
,
586 if (written
< 0 && errno
== EAGAIN
) {
588 } else if (written
<= 0) {
589 SIPE_DEBUG_INFO_NOFORMAT("transport_http_canwrite_cb: written <= 0, exiting");
593 purple_circ_buffer_mark_read(transport
->transmit_buffer
,
597 /* buffer is empty -> stop sending */
598 purple_input_remove(transport
->transmit_handler
);
599 transport
->transmit_handler
= 0;
603 void sipe_backend_transport_http_message(struct sipe_transport_connection
*conn
,
606 struct sipe_transport_purple
*transport
= PURPLE_TRANSPORT
;
607 time_t currtime
= time(NULL
);
610 SIPE_DEBUG_INFO("sending - %s######\n%s######",
611 ctime(&currtime
), tmp
= fix_newlines(buffer
));
614 /* add packet to circular buffer */
615 purple_circ_buffer_append(transport
->transmit_buffer
,
616 buffer
, strlen(buffer
));
618 /* initiate transmission */
619 if (!transport
->transmit_handler
) {
620 transport
->transmit_handler
= purple_input_add(transport
->socket
,
622 transport_http_canwrite_cb
,