im: fix response/timeout for multiparty/conference messages
[siplcs.git] / src / purple / purple-transport.c
blobd853ff1a44ca618050935cae540abc905177183c
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 sipe_transport_purple {
50 /* public part shared with core */
51 struct sipe_transport_connection public;
53 /* purple private part */
54 transport_connected_cb *connected;
55 transport_input_cb *input;
56 transport_error_cb *error;
57 PurpleConnection *gc;
58 PurpleSslConnection *gsc;
59 PurpleCircBuffer *transmit_buffer;
60 guint transmit_handler;
61 guint receive_handler;
62 int socket;
65 #define PURPLE_TRANSPORT ((struct sipe_transport_purple *) conn)
66 #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport)
68 #define BUFFER_SIZE_INCREMENT 4096
72 /*****************************************************************************
74 * Common transport handling
76 *****************************************************************************/
77 static void transport_common_input(struct sipe_transport_purple *transport)
79 struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION;
80 gssize readlen, len;
81 gboolean firstread = TRUE;
83 /* Read all available data from the connection */
84 do {
85 /* Increase input buffer size as needed */
86 if (conn->buffer_length < conn->buffer_used + BUFFER_SIZE_INCREMENT) {
87 conn->buffer_length += BUFFER_SIZE_INCREMENT;
88 conn->buffer = g_realloc(conn->buffer, conn->buffer_length);
89 SIPE_DEBUG_INFO("transport_input_common: new buffer length %" G_GSIZE_FORMAT,
90 conn->buffer_length);
93 /* Try to read as much as there is space left in the buffer */
94 /* minus 1 for the string terminator */
95 readlen = conn->buffer_length - conn->buffer_used - 1;
96 len = transport->gsc ?
97 (gssize) purple_ssl_read(transport->gsc,
98 conn->buffer + conn->buffer_used,
99 readlen) :
100 read(transport->socket,
101 conn->buffer + conn->buffer_used,
102 readlen);
104 if (len < 0 && errno == EAGAIN) {
105 /* Try again later */
106 return;
107 } else if (len < 0) {
108 SIPE_DEBUG_ERROR_NOFORMAT("Read error");
109 transport->error(SIPE_TRANSPORT_CONNECTION, _("Read error"));
110 return;
111 } else if (firstread && (len == 0)) {
112 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
113 transport->error(SIPE_TRANSPORT_CONNECTION, _("Server has disconnected"));
114 return;
117 conn->buffer_used += len;
118 firstread = FALSE;
120 /* Equivalence indicates that there is possibly more data to read */
121 } while (len == readlen);
123 conn->buffer[conn->buffer_used] = '\0';
124 transport->input(conn);
127 static void transport_ssl_input(gpointer data,
128 PurpleSslConnection *gsc,
129 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
131 struct sipe_transport_purple *transport = data;
133 /* NOTE: This check *IS* necessary */
134 if (!PURPLE_CONNECTION_IS_VALID(transport->gc)) {
135 purple_ssl_close(gsc);
136 transport->gsc = NULL;
137 return;
139 transport_common_input(transport);
142 static void transport_tcp_input(gpointer data,
143 gint source,
144 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
146 struct sipe_transport_purple *transport = data;
148 /* NOTE: This check *IS* necessary */
149 if (!PURPLE_CONNECTION_IS_VALID(transport->gc)) {
150 close(source);
151 transport->socket = -1;
152 return;
154 transport_common_input(transport);
157 static void transport_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection *gsc,
158 PurpleSslErrorType error,
159 gpointer data)
161 struct sipe_transport_purple *transport = data;
163 /* If the connection is already disconnected
164 then we don't need to do anything else */
165 if (!PURPLE_CONNECTION_IS_VALID(transport->gc))
166 return;
168 transport->socket = -1;
169 transport->gsc = NULL;
170 transport->error(SIPE_TRANSPORT_CONNECTION,
171 purple_ssl_strerror(error));
174 static void transport_common_connected(struct sipe_transport_purple *transport,
175 PurpleSslConnection *gsc,
176 int fd)
178 if (!PURPLE_CONNECTION_IS_VALID(transport->gc))
180 if (gsc) {
181 purple_ssl_close(gsc);
182 } else if (fd >= 0) {
183 close(fd);
185 return;
188 if (fd < 0) {
189 transport->error(SIPE_TRANSPORT_CONNECTION,
190 _("Could not connect"));
191 return;
194 transport->socket = fd;
195 transport->public.client_port = purple_network_get_port_from_fd(fd);
197 if (gsc) {
198 transport->gsc = gsc;
199 purple_ssl_input_add(gsc, transport_ssl_input, transport);
200 } else {
201 transport->receive_handler = purple_input_add(fd,
202 PURPLE_INPUT_READ,
203 transport_tcp_input,
204 transport);
207 transport->connected(SIPE_TRANSPORT_CONNECTION);
210 static void transport_ssl_connected(gpointer data,
211 PurpleSslConnection *gsc,
212 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
214 transport_common_connected(data, gsc, gsc ? gsc->fd : -1);
217 static void transport_tcp_connected(gpointer data,
218 gint source,
219 SIPE_UNUSED_PARAMETER const gchar *error_message)
221 transport_common_connected(data, NULL, source);
224 struct sipe_transport_connection *
225 sipe_backend_transport_connect(struct sipe_core_public *sipe_public,
226 const sipe_connect_setup *setup)
228 struct sipe_transport_purple *transport = g_new0(struct sipe_transport_purple, 1);
229 struct sipe_backend_private *purple_private = sipe_public->backend_private;
230 PurpleConnection *gc = purple_private->gc;
231 PurpleAccount *account = purple_connection_get_account(gc);
233 SIPE_DEBUG_INFO("transport_connect - hostname: %s port: %d",
234 setup->server_name, setup->server_port);
236 transport->public.type = setup->type;
237 transport->public.user_data = setup->user_data;
238 transport->connected = setup->connected;
239 transport->input = setup->input;
240 transport->error = setup->error;
241 transport->gc = gc;
242 transport->transmit_buffer = purple_circ_buffer_new(0);
244 if (setup->type == SIPE_TRANSPORT_TLS) {
245 /* SSL case */
246 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
248 if (purple_ssl_connect(account,
249 setup->server_name,
250 setup->server_port,
251 transport_ssl_connected,
252 transport_ssl_connect_failure,
253 transport) == NULL) {
254 setup->error(SIPE_TRANSPORT_CONNECTION,
255 _("Could not create SSL context"));
256 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
257 return(NULL);
259 } else if (setup->type == SIPE_TRANSPORT_TCP) {
260 /* TCP case */
261 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
263 if (purple_proxy_connect(gc, account,
264 setup->server_name,
265 setup->server_port,
266 transport_tcp_connected,
267 transport) == NULL) {
268 setup->error(SIPE_TRANSPORT_CONNECTION,
269 _("Could not create socket"));
270 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
271 return(NULL);
273 } else {
274 setup->error(SIPE_TRANSPORT_CONNECTION,
275 "This should not happen...");
276 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
277 return(NULL);
280 return(SIPE_TRANSPORT_CONNECTION);
283 void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn)
285 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
287 if (!transport) return;
289 if (transport->gsc) {
290 purple_ssl_close(transport->gsc);
291 } else if (transport->socket > 0) {
292 close(transport->socket);
295 if (transport->transmit_handler)
296 purple_input_remove(transport->transmit_handler);
297 if (transport->receive_handler)
298 purple_input_remove(transport->receive_handler);
300 if (transport->transmit_buffer)
301 purple_circ_buffer_destroy(transport->transmit_buffer);
302 g_free(transport->public.buffer);
304 g_free(transport);
307 /* returns FALSE on write error */
308 static gboolean transport_write(struct sipe_transport_purple *transport)
310 gsize max_write;
312 max_write = purple_circ_buffer_get_max_read(transport->transmit_buffer);
313 if (max_write > 0) {
314 gssize written = transport->gsc ?
315 (gssize) purple_ssl_write(transport->gsc,
316 transport->transmit_buffer->outptr,
317 max_write) :
318 write(transport->socket,
319 transport->transmit_buffer->outptr,
320 max_write);
322 if (written < 0 && errno == EAGAIN) {
323 return TRUE;
324 } else if (written <= 0) {
325 SIPE_DEBUG_INFO_NOFORMAT("transport_canwrite_cb: written <= 0, exiting");
326 transport->error(SIPE_TRANSPORT_CONNECTION,
327 _("Write error"));
328 return FALSE;
331 purple_circ_buffer_mark_read(transport->transmit_buffer,
332 written);
334 } else {
335 /* buffer is empty -> stop sending */
336 purple_input_remove(transport->transmit_handler);
337 transport->transmit_handler = 0;
340 return TRUE;
343 static void transport_canwrite_cb(gpointer data,
344 SIPE_UNUSED_PARAMETER gint source,
345 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
347 transport_write(data);
350 void sipe_backend_transport_message(struct sipe_transport_connection *conn,
351 const gchar *buffer)
353 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
355 /* add packet to circular buffer */
356 purple_circ_buffer_append(transport->transmit_buffer,
357 buffer, strlen(buffer));
359 /* initiate transmission */
360 if (!transport->transmit_handler) {
361 transport->transmit_handler = purple_input_add(transport->socket,
362 PURPLE_INPUT_WRITE,
363 transport_canwrite_cb,
364 transport);
368 void sipe_backend_transport_flush(struct sipe_transport_connection *conn)
370 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
372 while ( purple_circ_buffer_get_max_read(transport->transmit_buffer)
373 && transport_write(transport));
377 Local Variables:
378 mode: c
379 c-file-style: "bsd"
380 indent-tabs-mode: t
381 tab-width: 8
382 End: