telepathy: introduce TLS info data structure
[siplcs.git] / src / telepathy / telepathy-transport.c
blob2b30a94310ae10b183d781c4fd159d36a1a70786
1 /**
2 * @file telepathy-transport.c
4 * pidgin-sipe
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
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <string.h>
29 #include <glib.h>
30 #include <gio/gio.h>
32 #include "sipe-backend.h"
33 #include "sipe-common.h"
34 #include "sipe-core.h"
35 #include "sipe-nls.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;
47 gchar *hostname;
48 struct sipe_tls_info *tls_info;
49 struct sipe_backend_private *private;
50 GCancellable *cancel;
51 GSocketConnection *socket;
52 GInputStream *istream;
53 GOutputStream *ostream;
54 GSList *buffers;
55 gboolean is_writing;
56 gboolean do_flush;
59 #define TELEPATHY_TRANSPORT ((struct sipe_transport_telepathy *) conn)
60 #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport)
62 #define BUFFER_SIZE_INCREMENT 4096
64 static void read_completed(GObject *stream,
65 GAsyncResult *result,
66 gpointer data)
68 struct sipe_transport_telepathy *transport = data;
69 struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION;
71 do {
72 if (conn->buffer_length < conn->buffer_used + BUFFER_SIZE_INCREMENT) {
73 conn->buffer_length += BUFFER_SIZE_INCREMENT;
74 conn->buffer = g_realloc(conn->buffer, conn->buffer_length);
75 SIPE_DEBUG_INFO("read_completed: new buffer length %" G_GSIZE_FORMAT,
76 conn->buffer_length);
79 /* callback result is valid */
80 if (result) {
81 GError *error = NULL;
82 gssize len = g_input_stream_read_finish(G_INPUT_STREAM(stream),
83 result,
84 &error);
86 if (len < 0) {
87 const gchar *msg = error ? error->message : "UNKNOWN";
88 SIPE_DEBUG_ERROR("read_completed: error: %s", msg);
89 if (transport->error)
90 transport->error(conn, msg);
91 g_error_free(error);
92 return;
93 } else if (len == 0) {
94 SIPE_DEBUG_ERROR_NOFORMAT("read_completed: server has disconnected");
95 transport->error(conn, _("Server has disconnected"));
96 return;
97 } else if (transport->do_flush) {
98 /* read completed while disconnected transport is flushing */
99 SIPE_DEBUG_INFO_NOFORMAT("read_completed: ignored during flushing");
100 return;
101 } else if (g_cancellable_is_cancelled(transport->cancel)) {
102 /* read completed when transport was disconnected */
103 SIPE_DEBUG_INFO_NOFORMAT("read_completed: cancelled");
104 return;
107 /* Forward data to core */
108 conn->buffer_used += len;
109 conn->buffer[conn->buffer_used] = '\0';
110 transport->input(conn);
112 /* we processed the result */
113 result = NULL;
116 /* buffer too short? */
117 } while (conn->buffer_length - conn->buffer_used - 1 == 0);
119 /* setup next read */
120 g_input_stream_read_async(G_INPUT_STREAM(stream),
121 conn->buffer + conn->buffer_used,
122 conn->buffer_length - conn->buffer_used - 1,
123 G_PRIORITY_DEFAULT,
124 transport->cancel,
125 read_completed,
126 transport);
129 static void certificate_result(SIPE_UNUSED_PARAMETER GObject *unused,
130 SIPE_UNUSED_PARAMETER GAsyncResult *res,
131 gpointer data)
133 struct sipe_transport_telepathy *transport = data;
135 SIPE_DEBUG_INFO("certificate_result: %p", transport);
137 /* @TODO: take action based on result */
140 static void socket_connected(GObject *client,
141 GAsyncResult *result,
142 gpointer data)
144 struct sipe_transport_telepathy *transport = data;
145 GError *error = NULL;
147 transport->socket = g_socket_client_connect_finish(G_SOCKET_CLIENT(client),
148 result,
149 &error);
151 if (transport->socket == NULL) {
152 if (transport->tls_info) {
153 SIPE_DEBUG_INFO_NOFORMAT("socket_connected: need to wait for user interaction");
154 sipe_telepathy_tls_verify_async(G_OBJECT(transport->private->connection),
155 transport->tls_info,
156 certificate_result,
157 transport);
158 } else {
159 const gchar *msg = error ? error->message : "UNKNOWN";
160 SIPE_DEBUG_ERROR("socket_connected: failed: %s", msg);
161 if (transport->error)
162 transport->error(SIPE_TRANSPORT_CONNECTION, msg);
163 g_error_free(error);
165 } else if (g_cancellable_is_cancelled(transport->cancel)) {
166 /* connect already succeeded when transport was disconnected */
167 g_object_unref(transport->socket);
168 transport->socket = NULL;
169 SIPE_DEBUG_INFO_NOFORMAT("socket_connected: succeeded, but cancelled");
170 } else {
171 GSocketAddress *saddr = g_socket_connection_get_local_address(transport->socket,
172 &error);
174 if (saddr) {
175 SIPE_DEBUG_INFO_NOFORMAT("socket_connected: success");
177 transport->public.client_port = g_inet_socket_address_get_port(G_INET_SOCKET_ADDRESS(saddr));
178 g_object_unref(saddr);
180 transport->istream = g_io_stream_get_input_stream(G_IO_STREAM(transport->socket));
181 transport->ostream = g_io_stream_get_output_stream(G_IO_STREAM(transport->socket));
183 /* the first connection is always to the server */
184 if (transport->private->transport == NULL)
185 transport->private->transport = transport;
187 /* this sets up the async read handler */
188 read_completed(G_OBJECT(transport->istream), NULL, transport);
189 transport->connected(SIPE_TRANSPORT_CONNECTION);
191 } else {
192 g_object_unref(transport->socket);
193 transport->socket = NULL;
194 SIPE_DEBUG_ERROR("socket_connected: failed: %s", error->message);
195 transport->error(SIPE_TRANSPORT_CONNECTION, error->message);
196 g_error_free(error);
201 static gboolean accept_certificate_signal(SIPE_UNUSED_PARAMETER GTlsConnection *tls,
202 GTlsCertificate *peer_cert,
203 SIPE_UNUSED_PARAMETER GTlsCertificateFlags errors,
204 gpointer user_data)
206 struct sipe_transport_telepathy *transport = user_data;
208 SIPE_DEBUG_INFO("accept_certificate_signal: %p", transport);
210 /* second connection attempt after feedback from user? */
211 if (transport->tls_info) {
212 /* user accepted certificate */
213 sipe_telepathy_tls_info_free(transport->tls_info);
214 transport->tls_info = NULL;
215 return(TRUE);
216 } else {
217 /* retry after user accepted certificate */
218 transport->tls_info = sipe_telepathy_tls_info_new(transport->hostname,
219 peer_cert);
220 return(FALSE);
224 static void tls_handshake_starts(SIPE_UNUSED_PARAMETER GSocketClient *client,
225 GSocketClientEvent event,
226 SIPE_UNUSED_PARAMETER GSocketConnectable *connectable,
227 GIOStream *connection,
228 gpointer user_data)
230 if (event == G_SOCKET_CLIENT_TLS_HANDSHAKING) {
231 SIPE_DEBUG_INFO("tls_handshake_starts: %p", connection);
232 g_signal_connect(connection, /* is a GTlsConnection */
233 "accept-certificate",
234 G_CALLBACK(accept_certificate_signal),
235 user_data);
239 struct sipe_transport_connection *sipe_backend_transport_connect(struct sipe_core_public *sipe_public,
240 const sipe_connect_setup *setup)
242 struct sipe_transport_telepathy *transport = g_new0(struct sipe_transport_telepathy, 1);
244 SIPE_DEBUG_INFO("sipe_backend_transport_connect - hostname: %s port: %d",
245 setup->server_name, setup->server_port);
247 transport->public.type = setup->type;
248 transport->public.user_data = setup->user_data;
249 transport->connected = setup->connected;
250 transport->input = setup->input;
251 transport->error = setup->error;
252 transport->hostname = g_strdup(setup->server_name);
253 transport->tls_info = NULL;
254 transport->private = sipe_public->backend_private;
255 transport->buffers = NULL;
256 transport->is_writing = FALSE;
257 transport->do_flush = FALSE;
259 if ((setup->type == SIPE_TRANSPORT_TLS) ||
260 (setup->type == SIPE_TRANSPORT_TCP)) {
261 GSocketClient *client = g_socket_client_new();
263 /* request TLS connection */
264 if (setup->type == SIPE_TRANSPORT_TLS) {
265 SIPE_DEBUG_INFO_NOFORMAT("using TLS");
266 g_socket_client_set_tls(client, TRUE);
267 g_signal_connect(client,
268 "event",
269 G_CALLBACK(tls_handshake_starts),
270 transport);
271 } else
272 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
274 transport->cancel = g_cancellable_new();
275 g_socket_client_connect_async(client,
276 g_network_address_new(setup->server_name,
277 setup->server_port),
278 transport->cancel,
279 socket_connected,
280 transport);
281 g_object_unref(client);
282 } else {
283 setup->error(SIPE_TRANSPORT_CONNECTION,
284 "This should not happen...");
285 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
286 return(NULL);
289 return(SIPE_TRANSPORT_CONNECTION);
292 static gboolean free_transport(gpointer data)
294 struct sipe_transport_telepathy *transport = data;
295 GSList *entry;
297 SIPE_DEBUG_INFO("free_transport %p", transport);
299 if (transport->tls_info)
300 sipe_telepathy_tls_info_free(transport->tls_info);
301 g_free(transport->hostname);
303 /* free unflushed buffers */
304 for (entry = transport->buffers; entry; entry = entry->next)
305 g_free(entry->data);
306 g_slist_free(transport->buffers);
308 if (transport->cancel)
309 g_object_unref(transport->cancel);
311 g_free(transport);
313 return(FALSE);
316 static void close_completed(GObject *stream,
317 GAsyncResult *result,
318 gpointer data)
320 struct sipe_transport_telepathy *transport = data;
321 SIPE_DEBUG_INFO("close_completed: transport %p", data);
322 g_io_stream_close_finish(G_IO_STREAM(stream), result, NULL);
323 g_idle_add(free_transport, transport);
326 static void do_close(struct sipe_transport_telepathy *transport)
328 SIPE_DEBUG_INFO("do_close: %p", transport);
330 /* cancel outstanding asynchronous operations */
331 transport->do_flush = FALSE;
332 g_cancellable_cancel(transport->cancel);
333 g_io_stream_close_async(G_IO_STREAM(transport->socket),
334 G_PRIORITY_DEFAULT,
335 NULL,
336 close_completed,
337 transport);
340 void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn)
342 struct sipe_transport_telepathy *transport = TELEPATHY_TRANSPORT;
344 if (!transport) return;
346 SIPE_DEBUG_INFO("sipe_backend_transport_disconnect: %p", transport);
348 /* error callback is invalid now, do no longer call! */
349 transport->error = NULL;
351 /* dropping connection to the server? */
352 if (transport->private->transport == transport)
353 transport->private->transport = NULL;
355 /* already connected? */
356 if (transport->socket) {
358 /* flush required? */
359 if (transport->do_flush && transport->is_writing)
360 SIPE_DEBUG_INFO("sipe_backend_transport_disconnect: %p needs flushing",
361 transport);
362 else
363 do_close(transport);
365 } else {
366 /* cancel outstanding connect operation */
367 if (transport->cancel)
368 g_cancellable_cancel(transport->cancel);
370 /* queue transport to be deleted */
371 g_idle_add(free_transport, transport);
375 static void do_write(struct sipe_transport_telepathy *transport,
376 const gchar *buffer);
377 static void write_completed(GObject *stream,
378 GAsyncResult *result,
379 gpointer data)
381 struct sipe_transport_telepathy *transport = data;
382 GError *error = NULL;
383 gssize written = g_output_stream_write_finish(G_OUTPUT_STREAM(stream),
384 result,
385 &error);
387 if ((written < 0) || error) {
388 const gchar *msg = error ? error->message : "UNKNOWN";
389 SIPE_DEBUG_ERROR("write_completed: error: %s", msg);
390 if (transport->error)
391 transport->error(SIPE_TRANSPORT_CONNECTION, msg);
392 g_error_free(error);
394 /* error during flush: give up and close transport */
395 if (transport->do_flush)
396 do_close(transport);
398 } else if (g_cancellable_is_cancelled(transport->cancel)) {
399 /* write completed when transport was disconnected */
400 SIPE_DEBUG_INFO_NOFORMAT("write_completed: cancelled");
401 transport->is_writing = FALSE;
402 } else {
403 /* more to write? */
404 if (transport->buffers) {
405 /* yes */
406 gchar *buffer = transport->buffers->data;
407 transport->buffers = g_slist_remove(transport->buffers,
408 buffer);
409 do_write(transport, buffer);
410 g_free(buffer);
411 } else {
412 /* no, we're done for now... */
413 transport->is_writing = FALSE;
415 /* flush completed */
416 if (transport->do_flush)
417 do_close(transport);
422 static void do_write(struct sipe_transport_telepathy *transport,
423 const gchar *buffer)
425 transport->is_writing = TRUE;
426 g_output_stream_write_async(transport->ostream,
427 buffer,
428 strlen(buffer),
429 G_PRIORITY_DEFAULT,
430 transport->cancel,
431 write_completed,
432 transport);
435 void sipe_backend_transport_message(struct sipe_transport_connection *conn,
436 const gchar *buffer)
438 struct sipe_transport_telepathy *transport = TELEPATHY_TRANSPORT;
440 /* currently writing? */
441 if (transport->is_writing) {
442 /* yes, append copy of buffer to list */
443 transport->buffers = g_slist_append(transport->buffers,
444 g_strdup(buffer));
445 } else
446 /* no, write directly to stream */
447 do_write(transport, buffer);
450 void sipe_backend_transport_flush(struct sipe_transport_connection *conn)
452 struct sipe_transport_telepathy *transport = TELEPATHY_TRANSPORT;
453 transport->do_flush = TRUE;
456 const gchar *sipe_backend_network_ip_address(struct sipe_core_public *sipe_public)
458 struct sipe_backend_private *telepathy_private = sipe_public->backend_private;
459 const gchar *ipstr = telepathy_private->ipaddress;
461 /* address cached? */
462 if (!ipstr) {
463 struct sipe_transport_telepathy *transport = telepathy_private->transport;
465 /* default if everything should fail */
466 ipstr = "127.0.0.1";
468 /* connection to server established - get local IP from socket */
469 if (transport && transport->socket) {
470 GSocketAddress *saddr = g_socket_connection_get_local_address(transport->socket,
471 NULL);
473 if (saddr) {
474 GInetAddress *iaddr = g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(saddr));
476 if (iaddr) {
477 /* cache address string */
478 ipstr = telepathy_private->ipaddress = g_inet_address_to_string(iaddr);
479 SIPE_DEBUG_INFO("sipe_backend_network_ip_address: %s", ipstr);
481 g_object_unref(saddr);
486 return(ipstr);
491 Local Variables:
492 mode: c
493 c-file-style: "bsd"
494 indent-tabs-mode: t
495 tab-width: 8
496 End: