win32: fix broken libc calls
[siplcs.git] / src / purple / purple-transport.c
blob2206e0877f4ce085f83c1376871fd31f90a67adb
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 #ifdef _WIN32
44 /* wrappers for write() & friends for socket handling */
45 #include "win32/win32dep.h"
46 #endif
48 #include "purple-private.h"
50 #include "sipe-backend.h"
51 #include "sipe-core.h"
52 #include "sipe-nls.h"
54 struct sipe_transport_purple {
55 /* public part shared with core */
56 struct sipe_transport_connection public;
58 /* purple private part */
59 transport_connected_cb *connected;
60 transport_input_cb *input;
61 transport_error_cb *error;
62 PurpleConnection *gc;
63 PurpleSslConnection *gsc;
64 PurpleCircBuffer *transmit_buffer;
65 guint transmit_handler;
66 guint receive_handler;
67 int socket;
70 #define PURPLE_TRANSPORT ((struct sipe_transport_purple *) conn)
71 #define SIPE_TRANSPORT_CONNECTION ((struct sipe_transport_connection *) transport)
73 #define BUFFER_SIZE_INCREMENT 4096
77 /*****************************************************************************
79 * Common transport handling
81 *****************************************************************************/
82 static void transport_common_input(struct sipe_transport_purple *transport)
84 struct sipe_transport_connection *conn = SIPE_TRANSPORT_CONNECTION;
85 gssize readlen, len;
86 gboolean firstread = TRUE;
88 /* Read all available data from the connection */
89 do {
90 /* Increase input buffer size as needed */
91 if (conn->buffer_length < conn->buffer_used + BUFFER_SIZE_INCREMENT) {
92 conn->buffer_length += BUFFER_SIZE_INCREMENT;
93 conn->buffer = g_realloc(conn->buffer, conn->buffer_length);
94 SIPE_DEBUG_INFO("transport_input_common: new buffer length %" G_GSIZE_FORMAT,
95 conn->buffer_length);
98 /* Try to read as much as there is space left in the buffer */
99 /* minus 1 for the string terminator */
100 readlen = conn->buffer_length - conn->buffer_used - 1;
101 len = transport->gsc ?
102 (gssize) purple_ssl_read(transport->gsc,
103 conn->buffer + conn->buffer_used,
104 readlen) :
105 read(transport->socket,
106 conn->buffer + conn->buffer_used,
107 readlen);
109 if (len < 0 && errno == EAGAIN) {
110 /* Try again later */
111 return;
112 } else if (len < 0) {
113 SIPE_DEBUG_ERROR("Read error: %s (%d)", strerror(errno), errno);
114 transport->error(SIPE_TRANSPORT_CONNECTION, _("Read error"));
115 return;
116 } else if (firstread && (len == 0)) {
117 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
118 transport->error(SIPE_TRANSPORT_CONNECTION, _("Server has disconnected"));
119 return;
122 conn->buffer_used += len;
123 firstread = FALSE;
125 /* Equivalence indicates that there is possibly more data to read */
126 } while (len == readlen);
128 conn->buffer[conn->buffer_used] = '\0';
129 transport->input(conn);
132 static void transport_ssl_input(gpointer data,
133 PurpleSslConnection *gsc,
134 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
136 struct sipe_transport_purple *transport = data;
138 /* NOTE: This check *IS* necessary */
139 if (!PURPLE_CONNECTION_IS_VALID(transport->gc)) {
140 purple_ssl_close(gsc);
141 transport->gsc = NULL;
142 return;
144 transport_common_input(transport);
147 static void transport_tcp_input(gpointer data,
148 gint source,
149 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
151 struct sipe_transport_purple *transport = data;
153 /* NOTE: This check *IS* necessary */
154 if (!PURPLE_CONNECTION_IS_VALID(transport->gc)) {
155 close(source);
156 transport->socket = -1;
157 return;
159 transport_common_input(transport);
162 static void transport_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection *gsc,
163 PurpleSslErrorType error,
164 gpointer data)
166 struct sipe_transport_purple *transport = data;
168 /* If the connection is already disconnected
169 then we don't need to do anything else */
170 if (!PURPLE_CONNECTION_IS_VALID(transport->gc))
171 return;
173 transport->socket = -1;
174 transport->gsc = NULL;
175 transport->error(SIPE_TRANSPORT_CONNECTION,
176 purple_ssl_strerror(error));
179 static void transport_common_connected(struct sipe_transport_purple *transport,
180 PurpleSslConnection *gsc,
181 int fd)
183 if (!PURPLE_CONNECTION_IS_VALID(transport->gc))
185 if (gsc) {
186 purple_ssl_close(gsc);
187 } else if (fd >= 0) {
188 close(fd);
190 return;
193 if (fd < 0) {
194 transport->error(SIPE_TRANSPORT_CONNECTION,
195 _("Could not connect"));
196 return;
199 transport->socket = fd;
200 transport->public.client_port = purple_network_get_port_from_fd(fd);
202 if (gsc) {
203 transport->gsc = gsc;
204 purple_ssl_input_add(gsc, transport_ssl_input, transport);
205 } else {
206 transport->receive_handler = purple_input_add(fd,
207 PURPLE_INPUT_READ,
208 transport_tcp_input,
209 transport);
212 transport->connected(SIPE_TRANSPORT_CONNECTION);
215 static void transport_ssl_connected(gpointer data,
216 PurpleSslConnection *gsc,
217 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
219 transport_common_connected(data, gsc, gsc ? gsc->fd : -1);
222 static void transport_tcp_connected(gpointer data,
223 gint source,
224 SIPE_UNUSED_PARAMETER const gchar *error_message)
226 transport_common_connected(data, NULL, source);
229 struct sipe_transport_connection *
230 sipe_backend_transport_connect(struct sipe_core_public *sipe_public,
231 const sipe_connect_setup *setup)
233 struct sipe_transport_purple *transport = g_new0(struct sipe_transport_purple, 1);
234 struct sipe_backend_private *purple_private = sipe_public->backend_private;
235 PurpleConnection *gc = purple_private->gc;
236 PurpleAccount *account = purple_connection_get_account(gc);
238 SIPE_DEBUG_INFO("transport_connect - hostname: %s port: %d",
239 setup->server_name, setup->server_port);
241 transport->public.type = setup->type;
242 transport->public.user_data = setup->user_data;
243 transport->connected = setup->connected;
244 transport->input = setup->input;
245 transport->error = setup->error;
246 transport->gc = gc;
247 transport->transmit_buffer = purple_circ_buffer_new(0);
249 if (setup->type == SIPE_TRANSPORT_TLS) {
250 /* SSL case */
251 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
253 if (purple_ssl_connect(account,
254 setup->server_name,
255 setup->server_port,
256 transport_ssl_connected,
257 transport_ssl_connect_failure,
258 transport) == NULL) {
259 setup->error(SIPE_TRANSPORT_CONNECTION,
260 _("Could not create SSL context"));
261 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
262 return(NULL);
264 } else if (setup->type == SIPE_TRANSPORT_TCP) {
265 /* TCP case */
266 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
268 if (purple_proxy_connect(gc, account,
269 setup->server_name,
270 setup->server_port,
271 transport_tcp_connected,
272 transport) == NULL) {
273 setup->error(SIPE_TRANSPORT_CONNECTION,
274 _("Could not create socket"));
275 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
276 return(NULL);
278 } else {
279 setup->error(SIPE_TRANSPORT_CONNECTION,
280 "This should not happen...");
281 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION);
282 return(NULL);
285 return(SIPE_TRANSPORT_CONNECTION);
288 void sipe_backend_transport_disconnect(struct sipe_transport_connection *conn)
290 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
292 if (!transport) return;
294 if (transport->gsc) {
295 purple_ssl_close(transport->gsc);
296 } else if (transport->socket > 0) {
297 close(transport->socket);
300 if (transport->transmit_handler)
301 purple_input_remove(transport->transmit_handler);
302 if (transport->receive_handler)
303 purple_input_remove(transport->receive_handler);
305 if (transport->transmit_buffer)
306 purple_circ_buffer_destroy(transport->transmit_buffer);
307 g_free(transport->public.buffer);
309 g_free(transport);
312 /* returns FALSE on write error */
313 static gboolean transport_write(struct sipe_transport_purple *transport)
315 gsize max_write;
317 max_write = purple_circ_buffer_get_max_read(transport->transmit_buffer);
318 if (max_write > 0) {
319 gssize written = transport->gsc ?
320 (gssize) purple_ssl_write(transport->gsc,
321 transport->transmit_buffer->outptr,
322 max_write) :
323 write(transport->socket,
324 transport->transmit_buffer->outptr,
325 max_write);
327 if (written < 0 && errno == EAGAIN) {
328 return TRUE;
329 } else if (written <= 0) {
330 SIPE_DEBUG_ERROR("Write error: %s (%d)", strerror(errno), errno);
331 transport->error(SIPE_TRANSPORT_CONNECTION,
332 _("Write error"));
333 return FALSE;
336 purple_circ_buffer_mark_read(transport->transmit_buffer,
337 written);
339 } else {
340 /* buffer is empty -> stop sending */
341 purple_input_remove(transport->transmit_handler);
342 transport->transmit_handler = 0;
345 return TRUE;
348 static void transport_canwrite_cb(gpointer data,
349 SIPE_UNUSED_PARAMETER gint source,
350 SIPE_UNUSED_PARAMETER PurpleInputCondition cond)
352 transport_write(data);
355 void sipe_backend_transport_message(struct sipe_transport_connection *conn,
356 const gchar *buffer)
358 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
360 /* add packet to circular buffer */
361 purple_circ_buffer_append(transport->transmit_buffer,
362 buffer, strlen(buffer));
364 /* initiate transmission */
365 if (!transport->transmit_handler) {
366 transport->transmit_handler = purple_input_add(transport->socket,
367 PURPLE_INPUT_WRITE,
368 transport_canwrite_cb,
369 transport);
373 void sipe_backend_transport_flush(struct sipe_transport_connection *conn)
375 struct sipe_transport_purple *transport = PURPLE_TRANSPORT;
377 while ( purple_circ_buffer_get_max_read(transport->transmit_buffer)
378 && transport_write(transport));
382 Local Variables:
383 mode: c
384 c-file-style: "bsd"
385 indent-tabs-mode: t
386 tab-width: 8
387 End: