--- a/libpurple/purple-socket.c Wed Oct 26 10:17:10 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,410 +0,0 @@ -/* 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" - -#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); - } -}