Adapt migration for files
[pidgin-git.git] / libpurple / purple-socket.c
blob72b368b95075545be35e92ea01023a76b9b82078
1 /* purple
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
5 * source distribution.
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"
24 #include "internal.h"
26 #include "debug.h"
27 #include "proxy.h"
28 #include "sslconn.h"
30 typedef enum {
31 PURPLE_SOCKET_STATE_DISCONNECTED = 0,
32 PURPLE_SOCKET_STATE_CONNECTING,
33 PURPLE_SOCKET_STATE_CONNECTED,
34 PURPLE_SOCKET_STATE_ERROR
35 } PurpleSocketState;
37 struct _PurpleSocket
39 PurpleConnection *gc;
40 gchar *host;
41 int port;
42 gboolean is_tls;
43 GHashTable *data;
45 PurpleSocketState state;
47 PurpleSslConnection *tls_connection;
48 PurpleProxyConnectData *raw_connection;
49 int fd;
50 guint inpa;
52 PurpleSocketConnectCb cb;
53 gpointer cb_data;
56 static GHashTable *handles = NULL;
58 static void
59 handle_add(PurpleSocket *ps)
61 PurpleConnection *gc = ps->gc;
62 GSList *l;
64 l = g_hash_table_lookup(handles, gc);
65 l = g_slist_prepend(l, ps);
66 g_hash_table_insert(handles, gc, l);
69 static void
70 handle_remove(PurpleSocket *ps)
72 PurpleConnection *gc = ps->gc;
73 GSList *l;
75 l = g_hash_table_lookup(handles, gc);
76 l = g_slist_remove(l, ps);
77 g_hash_table_insert(handles, gc, l);
80 void
81 _purple_socket_init(void)
83 handles = g_hash_table_new(g_direct_hash, g_direct_equal);
86 void
87 _purple_socket_uninit(void)
89 g_hash_table_destroy(handles);
90 handles = NULL;
93 PurpleSocket *
94 purple_socket_new(PurpleConnection *gc)
96 PurpleSocket *ps = g_new0(PurpleSocket, 1);
98 ps->gc = gc;
99 ps->fd = -1;
100 ps->port = -1;
101 ps->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
103 handle_add(ps);
105 return ps;
108 PurpleConnection *
109 purple_socket_get_connection(PurpleSocket *ps)
111 g_return_val_if_fail(ps != NULL, NULL);
113 return ps->gc;
116 static gboolean
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)
122 return TRUE;
124 purple_debug_error("socket", "invalid state: %d (should be: %d)",
125 ps->state, wanted_state);
126 ps->state = PURPLE_SOCKET_STATE_ERROR;
127 return FALSE;
130 void
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))
136 return;
138 ps->is_tls = is_tls;
141 void
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))
147 return;
149 g_free(ps->host);
150 ps->host = g_strdup(host);
153 void
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))
161 return;
163 ps->port = port;
166 static void
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)) {
174 if (fd > 0)
175 close(fd);
176 ps->cb(ps, _("Invalid socket state"), ps->cb_data);
177 return;
180 if (fd <= 0 || error_message != NULL) {
181 if (error_message == NULL)
182 error_message = _("Unknown error");
183 ps->fd = -1;
184 ps->state = PURPLE_SOCKET_STATE_ERROR;
185 ps->cb(ps, error_message, ps->cb_data);
186 return;
189 ps->state = PURPLE_SOCKET_STATE_CONNECTED;
190 ps->fd = fd;
191 ps->cb(ps, NULL, ps->cb_data);
194 static void
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);
204 return;
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);
212 return;
215 ps->state = PURPLE_SOCKET_STATE_CONNECTED;
216 ps->fd = ps->tls_connection->fd;
217 ps->cb(ps, NULL, ps->cb_data);
220 static void
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);
231 gboolean
232 purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
233 gpointer user_data)
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;
242 return FALSE;
245 if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
246 return FALSE;
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;
252 return FALSE;
255 if (ps->gc != NULL)
256 account = purple_connection_get_account(ps->gc);
258 ps->cb = cb;
259 ps->cb_data = user_data;
261 if (ps->is_tls) {
262 ps->tls_connection = purple_ssl_connect(account, ps->host,
263 ps->port, _purple_socket_connected_tls,
264 _purple_socket_connected_tls_error, ps);
265 } else {
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;
274 return FALSE;
277 return TRUE;
280 gssize
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))
287 return -1;
289 if (ps->is_tls)
290 return purple_ssl_read(ps->tls_connection, buf, len);
291 else
292 return read(ps->fd, buf, len);
295 gssize
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))
302 return -1;
304 if (ps->is_tls)
305 return purple_ssl_write(ps->tls_connection, buf, len);
306 else
307 return write(ps->fd, buf, len);
310 void
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))
317 return;
319 if (ps->inpa > 0)
320 purple_input_remove(ps->inpa);
321 ps->inpa = 0;
323 g_return_if_fail(ps->fd > 0);
325 if (func != NULL)
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))
335 return -1;
337 g_return_val_if_fail(ps->fd > 0, -1);
339 return ps->fd;
342 void
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);
348 if (data == NULL)
349 g_hash_table_remove(ps->data, key);
350 else
351 g_hash_table_insert(ps->data, g_strdup(key), data);
354 gpointer
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);
363 static void
364 purple_socket_cancel(PurpleSocket *ps)
366 if (ps->inpa > 0)
367 purple_input_remove(ps->inpa);
368 ps->inpa = 0;
370 if (ps->tls_connection != NULL) {
371 purple_ssl_close(ps->tls_connection);
372 ps->fd = -1;
374 ps->tls_connection = NULL;
376 if (ps->raw_connection != NULL)
377 purple_proxy_connect_cancel(ps->raw_connection);
378 ps->raw_connection = NULL;
380 if (ps->fd > 0)
381 close(ps->fd);
382 ps->fd = 0;
385 void
386 purple_socket_destroy(PurpleSocket *ps)
388 if (ps == NULL)
389 return;
391 handle_remove(ps);
393 purple_socket_cancel(ps);
395 g_free(ps->host);
396 g_hash_table_destroy(ps->data);
397 g_free(ps);
400 void
401 _purple_socket_cancel_with_connection(PurpleConnection *gc)
403 GSList *it;
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);