3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "purple-socket.h"
31 PURPLE_SOCKET_STATE_DISCONNECTED
= 0,
32 PURPLE_SOCKET_STATE_CONNECTING
,
33 PURPLE_SOCKET_STATE_CONNECTED
,
34 PURPLE_SOCKET_STATE_ERROR
45 PurpleSocketState state
;
47 PurpleSslConnection
*tls_connection
;
48 PurpleProxyConnectData
*raw_connection
;
52 PurpleSocketConnectCb cb
;
56 static GHashTable
*handles
= NULL
;
59 handle_add(PurpleSocket
*ps
)
61 PurpleConnection
*gc
= ps
->gc
;
64 l
= g_hash_table_lookup(handles
, gc
);
65 l
= g_slist_prepend(l
, ps
);
66 g_hash_table_insert(handles
, gc
, l
);
70 handle_remove(PurpleSocket
*ps
)
72 PurpleConnection
*gc
= ps
->gc
;
75 l
= g_hash_table_lookup(handles
, gc
);
76 l
= g_slist_remove(l
, ps
);
77 g_hash_table_insert(handles
, gc
, l
);
81 _purple_socket_init(void)
83 handles
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
87 _purple_socket_uninit(void)
89 g_hash_table_destroy(handles
);
94 purple_socket_new(PurpleConnection
*gc
)
96 PurpleSocket
*ps
= g_new0(PurpleSocket
, 1);
101 ps
->data
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
109 purple_socket_get_connection(PurpleSocket
*ps
)
111 g_return_val_if_fail(ps
!= NULL
, NULL
);
117 purple_socket_check_state(PurpleSocket
*ps
, PurpleSocketState wanted_state
)
119 g_return_val_if_fail(ps
!= NULL
, FALSE
);
121 if (ps
->state
== wanted_state
)
124 purple_debug_error("socket", "invalid state: %d (should be: %d)",
125 ps
->state
, wanted_state
);
126 ps
->state
= PURPLE_SOCKET_STATE_ERROR
;
131 purple_socket_set_tls(PurpleSocket
*ps
, gboolean is_tls
)
133 g_return_if_fail(ps
!= NULL
);
135 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_DISCONNECTED
))
142 purple_socket_set_host(PurpleSocket
*ps
, const gchar
*host
)
144 g_return_if_fail(ps
!= NULL
);
146 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_DISCONNECTED
))
150 ps
->host
= g_strdup(host
);
154 purple_socket_set_port(PurpleSocket
*ps
, int port
)
156 g_return_if_fail(ps
!= NULL
);
157 g_return_if_fail(port
>= 0);
158 g_return_if_fail(port
<= 65535);
160 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_DISCONNECTED
))
167 _purple_socket_connected_raw(gpointer _ps
, gint fd
, const gchar
*error_message
)
169 PurpleSocket
*ps
= _ps
;
171 ps
->raw_connection
= NULL
;
173 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_CONNECTING
)) {
176 ps
->cb(ps
, _("Invalid socket state"), ps
->cb_data
);
180 if (fd
<= 0 || error_message
!= NULL
) {
181 if (error_message
== NULL
)
182 error_message
= _("Unknown error");
184 ps
->state
= PURPLE_SOCKET_STATE_ERROR
;
185 ps
->cb(ps
, error_message
, ps
->cb_data
);
189 ps
->state
= PURPLE_SOCKET_STATE_CONNECTED
;
191 ps
->cb(ps
, NULL
, ps
->cb_data
);
195 _purple_socket_connected_tls(gpointer _ps
, PurpleSslConnection
*tls_connection
,
196 PurpleInputCondition cond
)
198 PurpleSocket
*ps
= _ps
;
200 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_CONNECTING
)) {
201 purple_ssl_close(tls_connection
);
202 ps
->tls_connection
= NULL
;
203 ps
->cb(ps
, _("Invalid socket state"), ps
->cb_data
);
207 if (ps
->tls_connection
->fd
<= 0) {
208 ps
->state
= PURPLE_SOCKET_STATE_ERROR
;
209 purple_ssl_close(tls_connection
);
210 ps
->tls_connection
= NULL
;
211 ps
->cb(ps
, _("Invalid file descriptor"), ps
->cb_data
);
215 ps
->state
= PURPLE_SOCKET_STATE_CONNECTED
;
216 ps
->fd
= ps
->tls_connection
->fd
;
217 ps
->cb(ps
, NULL
, ps
->cb_data
);
221 _purple_socket_connected_tls_error(PurpleSslConnection
*ssl_connection
,
222 PurpleSslErrorType error
, gpointer _ps
)
224 PurpleSocket
*ps
= _ps
;
226 ps
->state
= PURPLE_SOCKET_STATE_ERROR
;
227 ps
->tls_connection
= NULL
;
228 ps
->cb(ps
, purple_ssl_strerror(error
), ps
->cb_data
);
232 purple_socket_connect(PurpleSocket
*ps
, PurpleSocketConnectCb cb
,
235 PurpleAccount
*account
= NULL
;
237 g_return_val_if_fail(ps
!= NULL
, FALSE
);
239 if (ps
->gc
&& purple_connection_is_disconnecting(ps
->gc
)) {
240 purple_debug_error("socket", "connection is being destroyed");
241 ps
->state
= PURPLE_SOCKET_STATE_ERROR
;
245 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_DISCONNECTED
))
247 ps
->state
= PURPLE_SOCKET_STATE_CONNECTING
;
249 if (ps
->host
== NULL
|| ps
->port
< 0) {
250 purple_debug_error("socket", "Host or port is not specified");
251 ps
->state
= PURPLE_SOCKET_STATE_ERROR
;
256 account
= purple_connection_get_account(ps
->gc
);
259 ps
->cb_data
= user_data
;
262 ps
->tls_connection
= purple_ssl_connect(account
, ps
->host
,
263 ps
->port
, _purple_socket_connected_tls
,
264 _purple_socket_connected_tls_error
, ps
);
266 ps
->raw_connection
= purple_proxy_connect(ps
->gc
, account
,
267 ps
->host
, ps
->port
, _purple_socket_connected_raw
, ps
);
270 if (ps
->tls_connection
== NULL
&&
271 ps
->raw_connection
== NULL
)
273 ps
->state
= PURPLE_SOCKET_STATE_ERROR
;
281 purple_socket_read(PurpleSocket
*ps
, guchar
*buf
, size_t len
)
283 g_return_val_if_fail(ps
!= NULL
, -1);
284 g_return_val_if_fail(buf
!= NULL
, -1);
286 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_CONNECTED
))
290 return purple_ssl_read(ps
->tls_connection
, buf
, len
);
292 return read(ps
->fd
, buf
, len
);
296 purple_socket_write(PurpleSocket
*ps
, const guchar
*buf
, size_t len
)
298 g_return_val_if_fail(ps
!= NULL
, -1);
299 g_return_val_if_fail(buf
!= NULL
, -1);
301 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_CONNECTED
))
305 return purple_ssl_write(ps
->tls_connection
, buf
, len
);
307 return write(ps
->fd
, buf
, len
);
311 purple_socket_watch(PurpleSocket
*ps
, PurpleInputCondition cond
,
312 PurpleInputFunction func
, gpointer user_data
)
314 g_return_if_fail(ps
!= NULL
);
316 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_CONNECTED
))
320 purple_input_remove(ps
->inpa
);
323 g_return_if_fail(ps
->fd
> 0);
326 ps
->inpa
= purple_input_add(ps
->fd
, cond
, func
, user_data
);
330 purple_socket_get_fd(PurpleSocket
*ps
)
332 g_return_val_if_fail(ps
!= NULL
, -1);
334 if (!purple_socket_check_state(ps
, PURPLE_SOCKET_STATE_CONNECTED
))
337 g_return_val_if_fail(ps
->fd
> 0, -1);
343 purple_socket_set_data(PurpleSocket
*ps
, const gchar
*key
, gpointer data
)
345 g_return_if_fail(ps
!= NULL
);
346 g_return_if_fail(key
!= NULL
);
349 g_hash_table_remove(ps
->data
, key
);
351 g_hash_table_insert(ps
->data
, g_strdup(key
), data
);
355 purple_socket_get_data(PurpleSocket
*ps
, const gchar
*key
)
357 g_return_val_if_fail(ps
!= NULL
, NULL
);
358 g_return_val_if_fail(key
!= NULL
, NULL
);
360 return g_hash_table_lookup(ps
->data
, key
);
364 purple_socket_cancel(PurpleSocket
*ps
)
367 purple_input_remove(ps
->inpa
);
370 if (ps
->tls_connection
!= NULL
) {
371 purple_ssl_close(ps
->tls_connection
);
374 ps
->tls_connection
= NULL
;
376 if (ps
->raw_connection
!= NULL
)
377 purple_proxy_connect_cancel(ps
->raw_connection
);
378 ps
->raw_connection
= NULL
;
386 purple_socket_destroy(PurpleSocket
*ps
)
393 purple_socket_cancel(ps
);
396 g_hash_table_destroy(ps
->data
);
401 _purple_socket_cancel_with_connection(PurpleConnection
*gc
)
405 it
= g_hash_table_lookup(handles
, gc
);
406 for (; it
; it
= g_slist_next(it
)) {
407 PurpleSocket
*ps
= it
->data
;
408 purple_socket_cancel(ps
);