2 * @file purple-transport.c
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
34 #include "sipe-common.h"
36 #include "circbuffer.h"
37 #include "connection.h"
38 #include "eventloop.h"
44 /* wrappers for write() & friends for socket handling */
45 #include "win32/win32dep.h"
48 #include "purple-private.h"
50 #include "sipe-backend.h"
51 #include "sipe-core.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
;
63 PurpleSslConnection
*gsc
;
64 PurpleCircBuffer
*transmit_buffer
;
65 guint transmit_handler
;
66 guint receive_handler
;
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
;
86 gboolean firstread
= TRUE
;
88 /* Read all available data from the connection */
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
,
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
,
105 read(transport
->socket
,
106 conn
->buffer
+ conn
->buffer_used
,
109 if (len
< 0 && errno
== EAGAIN
) {
110 /* Try again later */
112 } else if (len
< 0) {
113 SIPE_DEBUG_ERROR("Read error: %s (%d)", strerror(errno
), errno
);
114 transport
->error(SIPE_TRANSPORT_CONNECTION
, _("Read error"));
116 } else if (firstread
&& (len
== 0)) {
117 SIPE_DEBUG_ERROR_NOFORMAT("Server has disconnected");
118 transport
->error(SIPE_TRANSPORT_CONNECTION
, _("Server has disconnected"));
122 conn
->buffer_used
+= len
;
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
;
144 transport_common_input(transport
);
147 static void transport_tcp_input(gpointer data
,
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
)) {
156 transport
->socket
= -1;
159 transport_common_input(transport
);
162 static void transport_ssl_connect_failure(SIPE_UNUSED_PARAMETER PurpleSslConnection
*gsc
,
163 PurpleSslErrorType error
,
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
))
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
,
183 if (!PURPLE_CONNECTION_IS_VALID(transport
->gc
))
186 purple_ssl_close(gsc
);
187 } else if (fd
>= 0) {
194 transport
->error(SIPE_TRANSPORT_CONNECTION
,
195 _("Could not connect"));
199 transport
->socket
= fd
;
200 transport
->public.client_port
= purple_network_get_port_from_fd(fd
);
203 transport
->gsc
= gsc
;
204 purple_ssl_input_add(gsc
, transport_ssl_input
, transport
);
206 transport
->receive_handler
= purple_input_add(fd
,
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
,
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
;
247 transport
->transmit_buffer
= purple_circ_buffer_new(0);
249 if (setup
->type
== SIPE_TRANSPORT_TLS
) {
251 SIPE_DEBUG_INFO_NOFORMAT("using SSL");
253 if (purple_ssl_connect(account
,
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
);
264 } else if (setup
->type
== SIPE_TRANSPORT_TCP
) {
266 SIPE_DEBUG_INFO_NOFORMAT("using TCP");
268 if (purple_proxy_connect(gc
, account
,
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
);
279 setup
->error(SIPE_TRANSPORT_CONNECTION
,
280 "This should not happen...");
281 sipe_backend_transport_disconnect(SIPE_TRANSPORT_CONNECTION
);
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
);
312 /* returns FALSE on write error */
313 static gboolean
transport_write(struct sipe_transport_purple
*transport
)
317 max_write
= purple_circ_buffer_get_max_read(transport
->transmit_buffer
);
319 gssize written
= transport
->gsc
?
320 (gssize
) purple_ssl_write(transport
->gsc
,
321 transport
->transmit_buffer
->outptr
,
323 write(transport
->socket
,
324 transport
->transmit_buffer
->outptr
,
327 if (written
< 0 && errno
== EAGAIN
) {
329 } else if (written
<= 0) {
330 SIPE_DEBUG_ERROR("Write error: %s (%d)", strerror(errno
), errno
);
331 transport
->error(SIPE_TRANSPORT_CONNECTION
,
336 purple_circ_buffer_mark_read(transport
->transmit_buffer
,
340 /* buffer is empty -> stop sending */
341 purple_input_remove(transport
->transmit_handler
);
342 transport
->transmit_handler
= 0;
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
,
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
,
368 transport_canwrite_cb
,
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
));