123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- /* purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
- #include "purple-socket.h"
- #ifndef _WIN32
- #include <errno.h>
- #include <unistd.h>
- #endif
- #include "internal.h"
- #include "debug.h"
- #include "proxy.h"
- #include "sslconn.h"
- typedef enum {
- PURPLE_SOCKET_STATE_DISCONNECTED = 0,
- PURPLE_SOCKET_STATE_CONNECTING,
- PURPLE_SOCKET_STATE_CONNECTED,
- PURPLE_SOCKET_STATE_ERROR
- } PurpleSocketState;
- struct _PurpleSocket
- {
- PurpleConnection *gc;
- gchar *host;
- int port;
- gboolean is_tls;
- GHashTable *data;
- PurpleSocketState state;
- PurpleSslConnection *tls_connection;
- PurpleProxyConnectData *raw_connection;
- int fd;
- guint inpa;
- PurpleSocketConnectCb cb;
- gpointer cb_data;
- };
- static GHashTable *handles = NULL;
- static void
- handle_add(PurpleSocket *ps)
- {
- PurpleConnection *gc = ps->gc;
- GSList *l;
- l = g_hash_table_lookup(handles, gc);
- l = g_slist_prepend(l, ps);
- g_hash_table_insert(handles, gc, l);
- }
- static void
- handle_remove(PurpleSocket *ps)
- {
- PurpleConnection *gc = ps->gc;
- GSList *l;
- l = g_hash_table_lookup(handles, gc);
- l = g_slist_remove(l, ps);
- g_hash_table_insert(handles, gc, l);
- }
- void
- _purple_socket_init(void)
- {
- handles = g_hash_table_new(g_direct_hash, g_direct_equal);
- }
- void
- _purple_socket_uninit(void)
- {
- g_hash_table_destroy(handles);
- handles = NULL;
- }
- PurpleSocket *
- purple_socket_new(PurpleConnection *gc)
- {
- PurpleSocket *ps = g_new0(PurpleSocket, 1);
- ps->gc = gc;
- ps->fd = -1;
- ps->port = -1;
- ps->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- handle_add(ps);
- return ps;
- }
- PurpleConnection *
- purple_socket_get_connection(PurpleSocket *ps)
- {
- g_return_val_if_fail(ps != NULL, NULL);
- return ps->gc;
- }
- static gboolean
- purple_socket_check_state(PurpleSocket *ps, PurpleSocketState wanted_state)
- {
- g_return_val_if_fail(ps != NULL, FALSE);
- if (ps->state == wanted_state)
- return TRUE;
- purple_debug_error("socket", "invalid state: %d (should be: %d)",
- ps->state, wanted_state);
- ps->state = PURPLE_SOCKET_STATE_ERROR;
- return FALSE;
- }
- void
- purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls)
- {
- g_return_if_fail(ps != NULL);
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
- return;
- ps->is_tls = is_tls;
- }
- void
- purple_socket_set_host(PurpleSocket *ps, const gchar *host)
- {
- g_return_if_fail(ps != NULL);
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
- return;
- g_free(ps->host);
- ps->host = g_strdup(host);
- }
- void
- purple_socket_set_port(PurpleSocket *ps, int port)
- {
- g_return_if_fail(ps != NULL);
- g_return_if_fail(port >= 0);
- g_return_if_fail(port <= 65535);
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
- return;
- ps->port = port;
- }
- static void
- _purple_socket_connected_raw(gpointer _ps, gint fd, const gchar *error_message)
- {
- PurpleSocket *ps = _ps;
- ps->raw_connection = NULL;
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
- if (fd > 0)
- close(fd);
- ps->cb(ps, _("Invalid socket state"), ps->cb_data);
- return;
- }
- if (fd <= 0 || error_message != NULL) {
- if (error_message == NULL)
- error_message = _("Unknown error");
- ps->fd = -1;
- ps->state = PURPLE_SOCKET_STATE_ERROR;
- ps->cb(ps, error_message, ps->cb_data);
- return;
- }
- ps->state = PURPLE_SOCKET_STATE_CONNECTED;
- ps->fd = fd;
- ps->cb(ps, NULL, ps->cb_data);
- }
- static void
- _purple_socket_connected_tls(gpointer _ps, PurpleSslConnection *tls_connection,
- PurpleInputCondition cond)
- {
- PurpleSocket *ps = _ps;
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
- purple_ssl_close(tls_connection);
- ps->tls_connection = NULL;
- ps->cb(ps, _("Invalid socket state"), ps->cb_data);
- return;
- }
- if (ps->tls_connection->fd <= 0) {
- ps->state = PURPLE_SOCKET_STATE_ERROR;
- purple_ssl_close(tls_connection);
- ps->tls_connection = NULL;
- ps->cb(ps, _("Invalid file descriptor"), ps->cb_data);
- return;
- }
- ps->state = PURPLE_SOCKET_STATE_CONNECTED;
- ps->fd = ps->tls_connection->fd;
- ps->cb(ps, NULL, ps->cb_data);
- }
- static void
- _purple_socket_connected_tls_error(PurpleSslConnection *ssl_connection,
- PurpleSslErrorType error, gpointer _ps)
- {
- PurpleSocket *ps = _ps;
- ps->state = PURPLE_SOCKET_STATE_ERROR;
- ps->tls_connection = NULL;
- ps->cb(ps, purple_ssl_strerror(error), ps->cb_data);
- }
- gboolean
- purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
- gpointer user_data)
- {
- PurpleAccount *account = NULL;
- g_return_val_if_fail(ps != NULL, FALSE);
- if (ps->gc && purple_connection_is_disconnecting(ps->gc)) {
- purple_debug_error("socket", "connection is being destroyed");
- ps->state = PURPLE_SOCKET_STATE_ERROR;
- return FALSE;
- }
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
- return FALSE;
- ps->state = PURPLE_SOCKET_STATE_CONNECTING;
- if (ps->host == NULL || ps->port < 0) {
- purple_debug_error("socket", "Host or port is not specified");
- ps->state = PURPLE_SOCKET_STATE_ERROR;
- return FALSE;
- }
- if (ps->gc != NULL)
- account = purple_connection_get_account(ps->gc);
- ps->cb = cb;
- ps->cb_data = user_data;
- if (ps->is_tls) {
- ps->tls_connection = purple_ssl_connect(account, ps->host,
- ps->port, _purple_socket_connected_tls,
- _purple_socket_connected_tls_error, ps);
- } else {
- ps->raw_connection = purple_proxy_connect(ps->gc, account,
- ps->host, ps->port, _purple_socket_connected_raw, ps);
- }
- if (ps->tls_connection == NULL &&
- ps->raw_connection == NULL)
- {
- ps->state = PURPLE_SOCKET_STATE_ERROR;
- return FALSE;
- }
- return TRUE;
- }
- gssize
- purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len)
- {
- g_return_val_if_fail(ps != NULL, -1);
- g_return_val_if_fail(buf != NULL, -1);
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
- return -1;
- if (ps->is_tls)
- return purple_ssl_read(ps->tls_connection, buf, len);
- else
- return read(ps->fd, buf, len);
- }
- gssize
- purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len)
- {
- g_return_val_if_fail(ps != NULL, -1);
- g_return_val_if_fail(buf != NULL, -1);
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
- return -1;
- if (ps->is_tls)
- return purple_ssl_write(ps->tls_connection, buf, len);
- else
- return write(ps->fd, buf, len);
- }
- void
- purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond,
- PurpleInputFunction func, gpointer user_data)
- {
- g_return_if_fail(ps != NULL);
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
- return;
- if (ps->inpa > 0)
- purple_input_remove(ps->inpa);
- ps->inpa = 0;
- g_return_if_fail(ps->fd > 0);
- if (func != NULL)
- ps->inpa = purple_input_add(ps->fd, cond, func, user_data);
- }
- int
- purple_socket_get_fd(PurpleSocket *ps)
- {
- g_return_val_if_fail(ps != NULL, -1);
- if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
- return -1;
- g_return_val_if_fail(ps->fd > 0, -1);
- return ps->fd;
- }
- void
- purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data)
- {
- g_return_if_fail(ps != NULL);
- g_return_if_fail(key != NULL);
- if (data == NULL)
- g_hash_table_remove(ps->data, key);
- else
- g_hash_table_insert(ps->data, g_strdup(key), data);
- }
- gpointer
- purple_socket_get_data(PurpleSocket *ps, const gchar *key)
- {
- g_return_val_if_fail(ps != NULL, NULL);
- g_return_val_if_fail(key != NULL, NULL);
- return g_hash_table_lookup(ps->data, key);
- }
- static void
- purple_socket_cancel(PurpleSocket *ps)
- {
- if (ps->inpa > 0)
- purple_input_remove(ps->inpa);
- ps->inpa = 0;
- if (ps->tls_connection != NULL) {
- purple_ssl_close(ps->tls_connection);
- ps->fd = -1;
- }
- ps->tls_connection = NULL;
- if (ps->raw_connection != NULL)
- purple_proxy_connect_cancel(ps->raw_connection);
- ps->raw_connection = NULL;
- if (ps->fd > 0)
- close(ps->fd);
- ps->fd = 0;
- }
- void
- purple_socket_destroy(PurpleSocket *ps)
- {
- if (ps == NULL)
- return;
- handle_remove(ps);
- purple_socket_cancel(ps);
- g_free(ps->host);
- g_hash_table_destroy(ps->data);
- g_free(ps);
- }
- void
- _purple_socket_cancel_with_connection(PurpleConnection *gc)
- {
- GSList *it;
- it = g_hash_table_lookup(handles, gc);
- for (; it; it = g_slist_next(it)) {
- PurpleSocket *ps = it->data;
- purple_socket_cancel(ps);
- }
- }
|