i18n: update bug tracker URL in script
[siplcs.git] / src / purple / purple-transport.c
blobdafb545c802f9494641e0aeb64ac78825344b044
1 /**
2 * @file purple-transport.c
4 * pidgin-sipe
6 * Copyright (C) 2010-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 <errno.h>
28 #include <string.h>
29 #include <time.h>
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
34 #include <glib.h>
36 #include "sipe-common.h"
38 #include "circbuffer.h"
39 #include "connection.h"
40 #include "eventloop.h"
41 #include "network.h"
42 #include "proxy.h"
43 #include "sslconn.h"
45 #ifdef _WIN32
46 /* wrappers for write() & friends for socket handling */
47 #include "win32/win32dep.h"
48 #endif
50 #include "purple-private.h"
52 #include "sipe-backend.h"
53 #include "sipe-core.h"
54 #include "sipe-nls.h"
56 struct sipe_transport_purple {
57 /* public part shared with core */
58 struct sipe_transport_connection public;
60 /* purple private part */
61 transport_connected_cb *connected;
62 transport_input_cb *input;
63 transport_error_cb *error;
64 PurpleConnection *gc;
65 PurpleSslConnection *gsc;
66 PurpleCircBuffer *transmit_buffer;
67 guint transmit_handler;
68 guint receive_handler;
69 int socket;
72 #define PURPLE_TRANSPORT ((struct sipe_transport_purple *) conn)
73 #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport)
75 #define BUFFER_SIZE_INCREMENT 4096
79 /*****************************************************************************
81 * Common transport handling
83 *****************************************************************************/
84 static void transport_common_input(struct sipe_transport_purple *transport)
86 struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION;
87 gssize readlen, len;
88 gboolean firstread = TRUE;
90 /* Read all available data from the connection */
91 do {
92 /* Increase input buffer size as needed */
93 if (conn->buffer_length < conn->buffer_used + BUFFER_SIZE_INCREMENT) {
94 conn->buffer_length += BUFFER_SIZE_INCREMENT;
95 conn->buffer = g_realloc(conn->buffer, conn->buffer_length);
96 SIPE_DEBUG_INFO("transport_input_common: new buffer length %" G_GSIZE_FORMAT,
97 conn->buffer_length);
100 /* Try to read as much as there is space left in the buffer */
101 /* minus 1 for the string terminator */
102 readlen = conn->buffer_length - conn->buffer_used - 1;
103 len = transport->gsc ?
104 (gssize) purple_ssl_read(transport->gsc,
105 conn->buffer + conn->buffer_used,
106 readlen) :
107 read(transport->socket,
108 conn->buffer + conn->buffer_used,
109 readlen);
111 if (len < 0 && errno == EAGAIN) {
112 /* Try again later */
113 return;
114 } else if (len < 0) {
115 SIPE_DEBUG_ERROR("Read error: %s (%d)", strerror(errno), errno);
116 transport->error(SIPE_TRANSPORT_CONNECTION, _("Read error"));
117 return;
118 } else if (firstread && (len == 0)) {
119 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
120 transport->error(SIPE_TRANSPORT_CONNECTION, _("Server has disconnected"));
121 return;
124 conn->buffer_used += len;
125 firstread = FALSE;
127 /* Equivalence indicates that there is possibly more data to read */
128 } while (len == readlen);
130 conn->buffer[conn->buffer_used] = '\0';
131 transport->input(conn);
134 static void transport_ssl_input(gpointer data,
135 PurpleSslConnection *gsc,
136 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
138 struct sipe_transport_purple *transport = data;
140 /* NOTE: This check *IS* necessary */
141 if (!PURPLE_CONNECTION_IS_VALID(transport->gc)) {
142 purple_ssl_close(gsc);
143 transport->gsc = NULL;
144 return;
146 transport_common_input(transport);
149 static void transport_tcp_input(gpointer data,
150 gint source,
151 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
153 struct sipe_transport_purple *transport = data;
155 /* NOTE: This check *IS* necessary */
156 if (!PURPLE_CONNECTION_IS_VALID(transport->gc)) {
157 close(source);
158 transport->socket = -1;
159 return;
161 transport_common_input(transport);
164 static void transport_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection *gsc,
165 PurpleSslErrorType error,
166 gpointer data)
168 struct sipe_transport_purple *transport = data;
170 /* If the connection is already disconnected
171 then we don't need to do anything else */
172 if (!PURPLE_CONNECTION_IS_VALID(transport->gc))
173 return;
175 transport->socket = -1;
176 transport->gsc = NULL;
177 transport->error(SIPE_TRANSPORT_CONNECTION,
178 purple_ssl_strerror(error));
181 static void transport_common_connected(struct sipe_transport_purple *transport,
182 PurpleSslConnection *gsc,
183 int fd)
185 if (!PURPLE_CONNECTION_IS_VALID(transport->gc))
187 if (gsc) {
188 purple_ssl_close(gsc);
189 } else if (fd >= 0) {
190 close(fd);
192 return;
195 if (fd < 0) {
196 transport->error(SIPE_TRANSPORT_CONNECTION,
197 _("Could not connect"));
198 return;
201 transport->socket = fd;
202 transport->public.client_port = purple_network_get_port_from_fd(fd);
204 if (gsc) {
205 transport->gsc = gsc;
206 purple_ssl_input_add(gsc, transport_ssl_input, transport);
207 } else {
208 transport->receive_handler = purple_input_add(fd,
209 PURPLE_INPUT_READ,
210 transport_tcp_input,
211 transport);
214 transport->connected(SIPE_TRANSPORT_CONNECTION);
217 static void transport_ssl_connected(gpointer data,
218 PurpleSslConnection *gsc,
219 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
221 transport_common_connected(data, gsc, gsc ? gsc->fd : -1);
224 static void transport_tcp_connected(gpointer data,
225 gint source,
226 SIPE_UNUSED_PARAMETER const gchar *error_message)
228 transport_common_connected(data, NULL, source);
231 struct sipe_transport_connection *
232 sipe_backend_transport_connect(struct sipe_core_public *sipe_public,
233 const sipe_connect_setup *setup)
235 struct sipe_transport_purple *transport = g_new0(struct sipe_transport_purple, 1);
236 struct sipe_backend_private *purple_private = sipe_public->backend_private;
237 PurpleConnection *gc = purple_private->gc;
238 PurpleAccount *account = purple_connection_get_account(gc);
240 SIPE_DEBUG_INFO("transport_connect - hostname: %s port: %d",
241 setup->server_name, setup->server_port);
243 transport->public.type = setup->type;
244 transport->public.user_data = setup->user_data;
245 transport->connected = setup->connected;
246 transport->input = setup->input;
247 transport->error = setup->error;
248 transport->gc = gc;
249 transport->transmit_buffer = purple_circ_buffer_new(0);
251 if (setup->type == SIPE_TRANSPORT_TLS) {
252 /* SSL case */
253 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
255 if (purple_ssl_connect(account,
256 setup->server_name,
257 setup->server_port,
258 transport_ssl_connected,
259 transport_ssl_connect_failure,
260 transport) == NULL) {
261 setup->error(SIPE_TRANSPORT_CONNECTION,
262 _("Could not create SSL context"));
263 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
264 return(NULL);
266 } else if (setup->type == SIPE_TRANSPORT_TCP) {
267 /* TCP case */
268 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
270 if (purple_proxy_connect(gc, account,
271 setup->server_name,
272 setup->server_port,
273 transport_tcp_connected,
274 transport) == NULL) {
275 setup->error(SIPE_TRANSPORT_CONNECTION,
276 _("Could not create socket"));
277 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
278 return(NULL);
280 } else {
281 setup->error(SIPE_TRANSPORT_CONNECTION,
282 "This should not happen...");
283 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
284 return(NULL);
287 return(SIPE_TRANSPORT_CONNECTION);
290 void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn)
292 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
294 if (!transport) return;
296 if (transport->gsc) {
297 purple_ssl_close(transport->gsc);
298 } else if (transport->socket > 0) {
299 close(transport->socket);
302 if (transport->transmit_handler)
303 purple_input_remove(transport->transmit_handler);
304 if (transport->receive_handler)
305 purple_input_remove(transport->receive_handler);
307 if (transport->transmit_buffer)
308 purple_circ_buffer_destroy(transport->transmit_buffer);
309 g_free(transport->public.buffer);
311 g_free(transport);
314 /* returns FALSE on write error */
315 static gboolean transport_write(struct sipe_transport_purple *transport)
317 gsize max_write;
319 max_write = purple_circ_buffer_get_max_read(transport->transmit_buffer);
320 if (max_write > 0) {
321 gssize written = transport->gsc ?
322 (gssize) purple_ssl_write(transport->gsc,
323 transport->transmit_buffer->outptr,
324 max_write) :
325 write(transport->socket,
326 transport->transmit_buffer->outptr,
327 max_write);
329 if (written < 0 && errno == EAGAIN) {
330 return TRUE;
331 } else if (written <= 0) {
332 SIPE_DEBUG_ERROR("Write error: %s (%d)", strerror(errno), errno);
333 transport->error(SIPE_TRANSPORT_CONNECTION,
334 _("Write error"));
335 return FALSE;
338 purple_circ_buffer_mark_read(transport->transmit_buffer,
339 written);
341 } else {
342 /* buffer is empty -> stop sending */
343 purple_input_remove(transport->transmit_handler);
344 transport->transmit_handler = 0;
347 return TRUE;
350 static void transport_canwrite_cb(gpointer data,
351 SIPE_UNUSED_PARAMETER gint source,
352 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
354 struct sipe_transport_purple *transport = data;
355 PurpleConnection *gc = transport->gc;
357 /* Ignore spurious "can write" events during closing */
358 if (PURPLE_CONNECTION_IS_VALID(gc) &&
359 !gc->account->disconnecting)
360 transport_write(data);
363 void sipe_backend_transport_message(struct sipe_transport_connection *conn,
364 const gchar *buffer)
366 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
368 /* add packet to circular buffer */
369 purple_circ_buffer_append(transport->transmit_buffer,
370 buffer, strlen(buffer));
372 /* initiate transmission */
373 if (!transport->transmit_handler) {
374 transport->transmit_handler = purple_input_add(transport->socket,
375 PURPLE_INPUT_WRITE,
376 transport_canwrite_cb,
377 transport);
381 void sipe_backend_transport_flush(struct sipe_transport_connection *conn)
383 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
385 while ( purple_circ_buffer_get_max_read(transport->transmit_buffer)
386 && transport_write(transport));
390 Local Variables:
391 mode: c
392 c-file-style: "bsd"
393 indent-tabs-mode: t
394 tab-width: 8
395 End: