core cleanup: remove current_service & ssl_connect_failure callbacks
[siplcs.git] / src / purple / purple-transport.c
bloba157b24d8911d8f1d985e4ec320ce873c3332879
1 /**
2 * @file purple-transport.c
4 * pidgin-sipe
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
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <errno.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
32 #include <glib.h>
34 #include "sipe-common.h"
36 #include "circbuffer.h"
37 #include "connection.h"
38 #include "eventloop.h"
39 #include "network.h"
40 #include "proxy.h"
41 #include "sslconn.h"
43 #include "purple-private.h"
45 #include "sipe-backend.h"
46 #include "sipe-core.h"
47 #include "sipe-nls.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;
56 PurpleConnection *gc;
57 PurpleSslConnection *gsc;
58 PurpleCircBuffer *transmit_buffer;
59 guint transmit_handler;
60 guint receive_handler;
61 int socket;
64 struct transport_hooks
66 void (*input_error)(struct sipe_transport_purple *transport,
67 const char *msg);
68 void (*input_message)(struct sipe_transport_connection *conn);
69 /* can be NULL */
70 gboolean (*ssl_check)(struct sipe_transport_purple *transport,
71 const gchar *msg);
72 gboolean (*connected_check)(struct sipe_transport_purple *transport,
73 PurpleSslConnection *gsc,
74 int fd);
75 PurpleSslInputFunction input_ssl;
76 PurpleInputFunction input_tcp;
77 void (*connected)(struct sipe_transport_connection *conn);
78 /* can be NULL */
79 void (*write_error)(struct sipe_transport_purple *transport,
80 const gchar *msg);
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;
98 gssize readlen, len;
99 gboolean firstread = TRUE;
101 /* Read all available data from the connection */
102 do {
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,
117 readlen) :
118 read(transport->socket,
119 conn->buffer + conn->buffer_used,
120 readlen);
122 if (len < 0 && errno == EAGAIN) {
123 /* Try again later */
124 return;
125 } else if (len < 0) {
126 SIPE_DEBUG_ERROR_NOFORMAT("Read error");
127 transport->hooks->input_error(transport, _("Read error"));
128 return;
129 } else if (firstread && (len == 0)) {
130 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
131 transport->hooks->input_error(transport, _("Server has disconnected"));
132 return;
135 conn->buffer_used += len;
136 firstread = FALSE;
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,
147 gpointer data)
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))
154 return;
156 transport->socket = -1;
157 transport->gsc = NULL;
160 static void transport_connected_common(struct sipe_transport_purple *transport,
161 PurpleSslConnection *gsc,
162 int fd)
164 if (transport->hooks->connected_check(transport, gsc, fd))
165 return;
167 transport->socket = fd;
168 transport->public.client_port = purple_network_get_port_from_fd(fd);
170 if (gsc) {
171 transport->gsc = gsc;
172 purple_ssl_input_add(gsc, transport->hooks->input_ssl, transport);
173 } else {
174 transport->receive_handler = purple_input_add(fd,
175 PURPLE_INPUT_READ,
176 transport->hooks->input_tcp,
177 transport);
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,
191 gint source,
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,
199 guint type,
200 const gchar *server_name,
201 guint server_port)
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;
211 transport->gc = gc;
212 transport->transmit_buffer = purple_circ_buffer_new(0);
214 if (type == SIPE_TRANSPORT_TLS) {
215 /* SSL case */
216 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
218 if (purple_ssl_connect(account,
219 server_name,
220 server_port,
221 transport_connected_ssl_cb,
222 transport_ssl_connect_failure,
223 transport) == NULL) {
224 g_free(transport);
225 return(_("Could not create SSL context"));
227 } else if (type == SIPE_TRANSPORT_TCP) {
228 /* TCP case */
229 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
231 if (purple_proxy_connect(gc, account,
232 server_name,
233 server_port,
234 transport_connected_tcp_cb,
235 transport) == NULL) {
236 g_free(transport);
237 return(_("Could not create socket"));
239 } else {
240 g_free(transport);
241 return("This should not happen...");
244 return(NULL);
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);
266 g_free(transport);
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;
274 gsize max_write;
276 max_write = purple_circ_buffer_get_max_read(transport->transmit_buffer);
277 if (max_write > 0) {
278 gssize written = transport->gsc ?
279 (gssize) purple_ssl_write(transport->gsc,
280 transport->transmit_buffer->outptr,
281 max_write) :
282 write(transport->socket,
283 transport->transmit_buffer->outptr,
284 max_write);
286 if (written < 0 && errno == EAGAIN) {
287 return;
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,
292 _("Write error"));
293 return;
296 purple_circ_buffer_mark_read(transport->transmit_buffer,
297 written);
299 } else {
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,
307 const gchar *buffer)
309 time_t currtime = time(NULL);
310 char *tmp;
312 SIPE_DEBUG_INFO("sending - %s######\n%s######",
313 ctime(&currtime), tmp = fix_newlines(buffer));
314 g_free(tmp);
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,
323 PURPLE_INPUT_WRITE,
324 transport_canwrite_cb,
325 transport);
330 /*****************************************************************************
332 * SIP transport handling
334 *****************************************************************************/
335 static void transport_sip_input_error(struct sipe_transport_purple *transport,
336 const char *msg)
338 purple_connection_error_reason(transport->gc,
339 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
340 msg);
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;
354 return;
356 transport_input_common(transport);
359 static void transport_sip_input_tcp(gpointer data,
360 gint source,
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)) {
367 close(source);
368 transport->socket = -1;
369 return;
371 transport_input_common(transport);
374 static gboolean transport_sip_ssl_check(struct sipe_transport_purple *transport,
375 const gchar *msg)
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))
380 return(TRUE);
382 purple_connection_error_reason(transport->gc,
383 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
384 msg);
385 return(FALSE);
388 static gboolean transport_sip_connected_check(struct sipe_transport_purple *transport,
389 PurpleSslConnection *gsc,
390 int fd)
392 PurpleConnection *gc = transport->gc;
394 if (!PURPLE_CONNECTION_IS_VALID(gc))
396 if (gsc) {
397 purple_ssl_close(gsc);
398 } else if (fd >= 0) {
399 close(fd);
401 return(TRUE);
404 if (fd < 0) {
405 purple_connection_error_reason(gc,
406 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
407 _("Could not connect"));
408 return(TRUE);
411 PURPLE_GC_TO_SIPE_CORE_PUBLIC->backend_private->last_keepalive = time(NULL);
412 return(FALSE);
415 static void transport_sip_write_error(struct sipe_transport_purple *transport,
416 const gchar *msg)
418 purple_connection_error_reason(transport->gc,
419 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
420 msg);
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,
435 guint type,
436 const gchar *server_name,
437 guint server_port)
439 struct sipe_transport_purple *transport = g_new0(struct sipe_transport_purple, 1);
440 const gchar *msg;
442 transport->hooks = &transport_sip_hooks;
444 msg = transport_connect(sipe_public, transport,
445 type, server_name, server_port);
447 if (msg) {
448 purple_connection_error_reason(sipe_public->backend_private->gc,
449 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
450 msg);
451 return(NULL);
452 } else {
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;
461 if (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,
469 const gchar *buffer)
471 transport_message(PURPLE_TRANSPORT, buffer);
475 /*****************************************************************************
477 * HTTP transport handling
479 *****************************************************************************/
480 static void transport_http_input_error(struct sipe_transport_purple *transport,
481 const char *msg)
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,
502 int fd)
504 return(fd < 0);
507 static const struct transport_hooks transport_http_hooks = {
508 transport_http_input_error,
509 sipe_core_transport_http_message,
510 NULL,
511 transport_http_connected_check,
512 transport_http_input_ssl,
513 transport_http_input_tcp,
514 sipe_core_transport_http_connected,
515 NULL
518 struct sipe_transport_connection *sipe_backend_transport_http_connect(struct sipe_core_public *sipe_public,
519 guint type,
520 const gchar *server_name,
521 guint server_port)
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,
538 const gchar *buffer)
540 transport_message(PURPLE_TRANSPORT, buffer);
544 Local Variables:
545 mode: c
546 c-file-style: "bsd"
547 indent-tabs-mode: t
548 tab-width: 8
549 End: