2 * @file telepathy-transport.c
6 * Copyright (C) 2012-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
32 #include "sipe-backend.h"
33 #include "sipe-common.h"
34 #include "sipe-core.h"
37 #include "telepathy-private.h"
39 struct sipe_transport_telepathy
{
40 /* public part shared with core */
41 struct sipe_transport_connection
public;
43 /* telepathy private part */
44 transport_connected_cb
*connected
;
45 transport_input_cb
*input
;
46 transport_error_cb
*error
;
48 struct sipe_tls_info
*tls_info
;
49 struct sipe_backend_private
*private;
51 GSocketConnection
*socket
;
52 GInputStream
*istream
;
53 GOutputStream
*ostream
;
60 #define TELEPATHY_TRANSPORT ((struct sipe_transport_telepathy *) conn)
61 #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport)
63 #define BUFFER_SIZE_INCREMENT 4096
65 static void read_completed(GObject
*stream
,
69 struct sipe_transport_telepathy
*transport
= data
;
70 struct sipe_transport_connection
*conn
= SIPE_TRANSPORT_CONNECTION
;
73 if (conn
->buffer_length
< conn
->buffer_used
+ BUFFER_SIZE_INCREMENT
) {
74 conn
->buffer_length
+= BUFFER_SIZE_INCREMENT
;
75 conn
->buffer
= g_realloc(conn
->buffer
, conn
->buffer_length
);
76 SIPE_DEBUG_INFO("read_completed: new buffer length %" G_GSIZE_FORMAT
,
80 /* callback result is valid */
83 gssize len
= g_input_stream_read_finish(G_INPUT_STREAM(stream
),
88 const gchar
*msg
= error
? error
->message
: "UNKNOWN";
89 SIPE_DEBUG_ERROR("read_completed: error: %s", msg
);
91 transport
->error(conn
, msg
);
94 } else if (len
== 0) {
95 SIPE_DEBUG_ERROR_NOFORMAT("read_completed: server has disconnected");
96 transport
->error(conn
, _("Server has disconnected"));
98 } else if (transport
->do_flush
) {
99 /* read completed while disconnected transport is flushing */
100 SIPE_DEBUG_INFO_NOFORMAT("read_completed: ignored during flushing");
102 } else if (g_cancellable_is_cancelled(transport
->cancel
)) {
103 /* read completed when transport was disconnected */
104 SIPE_DEBUG_INFO_NOFORMAT("read_completed: cancelled");
108 /* Forward data to core */
109 conn
->buffer_used
+= len
;
110 conn
->buffer
[conn
->buffer_used
] = '\0';
111 transport
->input(conn
);
113 /* we processed the result */
117 /* buffer too short? */
118 } while (conn
->buffer_length
- conn
->buffer_used
- 1 == 0);
120 /* setup next read */
121 g_input_stream_read_async(G_INPUT_STREAM(stream
),
122 conn
->buffer
+ conn
->buffer_used
,
123 conn
->buffer_length
- conn
->buffer_used
- 1,
130 static gboolean
internal_connect(gpointer data
);
131 static void certificate_result(SIPE_UNUSED_PARAMETER GObject
*unused
,
132 GAsyncResult
*result
,
135 struct sipe_transport_telepathy
*transport
= data
;
136 GError
*error
= NULL
;
138 g_simple_async_result_propagate_error(G_SIMPLE_ASYNC_RESULT(result
),
141 SIPE_DEBUG_INFO("certificate_result: %s", error
->message
);
142 if (transport
->error
)
143 transport
->error(SIPE_TRANSPORT_CONNECTION
,
147 SIPE_DEBUG_INFO("certificate_result: trigger reconnect %p", transport
);
148 g_idle_add(internal_connect
, transport
);
152 static void socket_connected(GObject
*client
,
153 GAsyncResult
*result
,
156 struct sipe_transport_telepathy
*transport
= data
;
157 GError
*error
= NULL
;
159 transport
->socket
= g_socket_client_connect_finish(G_SOCKET_CLIENT(client
),
163 if (transport
->socket
== NULL
) {
164 if (transport
->tls_info
) {
165 SIPE_DEBUG_INFO_NOFORMAT("socket_connected: need to wait for user interaction");
166 sipe_telepathy_tls_verify_async(G_OBJECT(transport
->private->connection
),
171 const gchar
*msg
= error
? error
->message
: "UNKNOWN";
172 SIPE_DEBUG_ERROR("socket_connected: failed: %s", msg
);
173 if (transport
->error
)
174 transport
->error(SIPE_TRANSPORT_CONNECTION
, msg
);
177 } else if (g_cancellable_is_cancelled(transport
->cancel
)) {
178 /* connect already succeeded when transport was disconnected */
179 g_object_unref(transport
->socket
);
180 transport
->socket
= NULL
;
181 SIPE_DEBUG_INFO_NOFORMAT("socket_connected: succeeded, but cancelled");
183 GSocketAddress
*saddr
= g_socket_connection_get_local_address(transport
->socket
,
187 SIPE_DEBUG_INFO_NOFORMAT("socket_connected: success");
189 transport
->public.client_port
= g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(saddr
));
190 g_object_unref(saddr
);
192 transport
->istream
= g_io_stream_get_input_stream(G_IO_STREAM(transport
->socket
));
193 transport
->ostream
= g_io_stream_get_output_stream(G_IO_STREAM(transport
->socket
));
195 /* the first connection is always to the server */
196 if (transport
->private->transport
== NULL
)
197 transport
->private->transport
= transport
;
199 /* this sets up the async read handler */
200 read_completed(G_OBJECT(transport
->istream
), NULL
, transport
);
201 transport
->connected(SIPE_TRANSPORT_CONNECTION
);
204 g_object_unref(transport
->socket
);
205 transport
->socket
= NULL
;
206 SIPE_DEBUG_ERROR("socket_connected: failed: %s", error
->message
);
207 transport
->error(SIPE_TRANSPORT_CONNECTION
, error
->message
);
213 static gboolean
accept_certificate_signal(SIPE_UNUSED_PARAMETER GTlsConnection
*tls
,
214 GTlsCertificate
*peer_cert
,
215 SIPE_UNUSED_PARAMETER GTlsCertificateFlags errors
,
218 struct sipe_transport_telepathy
*transport
= user_data
;
220 SIPE_DEBUG_INFO("accept_certificate_signal: %p", transport
);
222 /* second connection attempt after feedback from user? */
223 if (transport
->tls_info
) {
224 /* user accepted certificate */
225 sipe_telepathy_tls_info_free(transport
->tls_info
);
226 transport
->tls_info
= NULL
;
229 /* retry after user accepted certificate */
230 transport
->tls_info
= sipe_telepathy_tls_info_new(transport
->hostname
,
236 static void tls_handshake_starts(SIPE_UNUSED_PARAMETER GSocketClient
*client
,
237 GSocketClientEvent event
,
238 SIPE_UNUSED_PARAMETER GSocketConnectable
*connectable
,
239 GIOStream
*connection
,
242 if (event
== G_SOCKET_CLIENT_TLS_HANDSHAKING
) {
243 SIPE_DEBUG_INFO("tls_handshake_starts: %p", connection
);
244 g_signal_connect(connection
, /* is a GTlsConnection */
245 "accept-certificate",
246 G_CALLBACK(accept_certificate_signal
),
251 static gboolean
internal_connect(gpointer data
)
253 struct sipe_transport_telepathy
*transport
= data
;
254 GSocketClient
*client
= g_socket_client_new();
256 SIPE_DEBUG_INFO("internal_connect - hostname: %s port: %d",
257 transport
->hostname
, transport
->port
);
259 /* request TLS connection */
260 if (transport
->public.type
== SIPE_TRANSPORT_TLS
) {
261 SIPE_DEBUG_INFO_NOFORMAT("using TLS");
262 g_socket_client_set_tls(client
, TRUE
);
263 g_signal_connect(client
,
265 G_CALLBACK(tls_handshake_starts
),
268 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
270 g_socket_client_connect_async(client
,
271 g_network_address_new(transport
->hostname
,
276 g_object_unref(client
);
281 struct sipe_transport_connection
*sipe_backend_transport_connect(struct sipe_core_public
*sipe_public
,
282 const sipe_connect_setup
*setup
)
284 struct sipe_transport_telepathy
*transport
= g_new0(struct sipe_transport_telepathy
, 1);
286 transport
->public.type
= setup
->type
;
287 transport
->public.user_data
= setup
->user_data
;
288 transport
->connected
= setup
->connected
;
289 transport
->input
= setup
->input
;
290 transport
->error
= setup
->error
;
291 transport
->hostname
= g_strdup(setup
->server_name
);
292 transport
->tls_info
= NULL
;
293 transport
->private = sipe_public
->backend_private
;
294 transport
->cancel
= g_cancellable_new();
295 transport
->buffers
= NULL
;
296 transport
->port
= setup
->server_port
;
297 transport
->is_writing
= FALSE
;
298 transport
->do_flush
= FALSE
;
300 if ((setup
->type
== SIPE_TRANSPORT_TLS
) ||
301 (setup
->type
== SIPE_TRANSPORT_TCP
)) {
303 internal_connect(transport
);
304 return(SIPE_TRANSPORT_CONNECTION
);
307 setup
->error(SIPE_TRANSPORT_CONNECTION
,
308 "This should not happen...");
309 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION
);
314 static gboolean
free_transport(gpointer data
)
316 struct sipe_transport_telepathy
*transport
= data
;
319 SIPE_DEBUG_INFO("free_transport %p", transport
);
321 if (transport
->tls_info
)
322 sipe_telepathy_tls_info_free(transport
->tls_info
);
323 g_free(transport
->hostname
);
325 /* free unflushed buffers */
326 for (entry
= transport
->buffers
; entry
; entry
= entry
->next
)
328 g_slist_free(transport
->buffers
);
330 if (transport
->cancel
)
331 g_object_unref(transport
->cancel
);
338 static void close_completed(GObject
*stream
,
339 GAsyncResult
*result
,
342 struct sipe_transport_telepathy
*transport
= data
;
343 SIPE_DEBUG_INFO("close_completed: transport %p", data
);
344 g_io_stream_close_finish(G_IO_STREAM(stream
), result
, NULL
);
345 g_idle_add(free_transport
, transport
);
348 static void do_close(struct sipe_transport_telepathy
*transport
)
350 SIPE_DEBUG_INFO("do_close: %p", transport
);
352 /* cancel outstanding asynchronous operations */
353 transport
->do_flush
= FALSE
;
354 g_cancellable_cancel(transport
->cancel
);
355 g_io_stream_close_async(G_IO_STREAM(transport
->socket
),
362 void sipe_backend_transport_disconnect(struct sipe_transport_connection
*conn
)
364 struct sipe_transport_telepathy
*transport
= TELEPATHY_TRANSPORT
;
366 if (!transport
) return;
368 SIPE_DEBUG_INFO("sipe_backend_transport_disconnect: %p", transport
);
370 /* error callback is invalid now, do no longer call! */
371 transport
->error
= NULL
;
373 /* dropping connection to the server? */
374 if (transport
->private->transport
== transport
)
375 transport
->private->transport
= NULL
;
377 /* already connected? */
378 if (transport
->socket
) {
380 /* flush required? */
381 if (transport
->do_flush
&& transport
->is_writing
)
382 SIPE_DEBUG_INFO("sipe_backend_transport_disconnect: %p needs flushing",
388 /* cancel outstanding connect operation */
389 if (transport
->cancel
)
390 g_cancellable_cancel(transport
->cancel
);
392 /* queue transport to be deleted */
393 g_idle_add(free_transport
, transport
);
397 static void do_write(struct sipe_transport_telepathy
*transport
,
398 const gchar
*buffer
);
399 static void write_completed(GObject
*stream
,
400 GAsyncResult
*result
,
403 struct sipe_transport_telepathy
*transport
= data
;
404 GError
*error
= NULL
;
405 gssize written
= g_output_stream_write_finish(G_OUTPUT_STREAM(stream
),
409 if ((written
< 0) || error
) {
410 const gchar
*msg
= error
? error
->message
: "UNKNOWN";
411 SIPE_DEBUG_ERROR("write_completed: error: %s", msg
);
412 if (transport
->error
)
413 transport
->error(SIPE_TRANSPORT_CONNECTION
, msg
);
416 /* error during flush: give up and close transport */
417 if (transport
->do_flush
)
420 } else if (g_cancellable_is_cancelled(transport
->cancel
)) {
421 /* write completed when transport was disconnected */
422 SIPE_DEBUG_INFO_NOFORMAT("write_completed: cancelled");
423 transport
->is_writing
= FALSE
;
426 if (transport
->buffers
) {
428 gchar
*buffer
= transport
->buffers
->data
;
429 transport
->buffers
= g_slist_remove(transport
->buffers
,
431 do_write(transport
, buffer
);
434 /* no, we're done for now... */
435 transport
->is_writing
= FALSE
;
437 /* flush completed */
438 if (transport
->do_flush
)
444 static void do_write(struct sipe_transport_telepathy
*transport
,
447 transport
->is_writing
= TRUE
;
448 g_output_stream_write_async(transport
->ostream
,
457 void sipe_backend_transport_message(struct sipe_transport_connection
*conn
,
460 struct sipe_transport_telepathy
*transport
= TELEPATHY_TRANSPORT
;
462 /* currently writing? */
463 if (transport
->is_writing
) {
464 /* yes, append copy of buffer to list */
465 transport
->buffers
= g_slist_append(transport
->buffers
,
468 /* no, write directly to stream */
469 do_write(transport
, buffer
);
472 void sipe_backend_transport_flush(struct sipe_transport_connection
*conn
)
474 struct sipe_transport_telepathy
*transport
= TELEPATHY_TRANSPORT
;
475 transport
->do_flush
= TRUE
;
478 const gchar
*sipe_backend_network_ip_address(struct sipe_core_public
*sipe_public
)
480 struct sipe_backend_private
*telepathy_private
= sipe_public
->backend_private
;
481 const gchar
*ipstr
= telepathy_private
->ipaddress
;
483 /* address cached? */
485 struct sipe_transport_telepathy
*transport
= telepathy_private
->transport
;
487 /* default if everything should fail */
490 /* connection to server established - get local IP from socket */
491 if (transport
&& transport
->socket
) {
492 GSocketAddress
*saddr
= g_socket_connection_get_local_address(transport
->socket
,
496 GInetAddress
*iaddr
= g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(saddr
));
499 /* cache address string */
500 ipstr
= telepathy_private
->ipaddress
= g_inet_address_to_string(iaddr
);
501 SIPE_DEBUG_INFO("sipe_backend_network_ip_address: %s", ipstr
);
503 g_object_unref(saddr
);