Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/EionRobb/skype4pidgin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEion Robb <eion@robbmob.com>2017-04-11 12:23:57 +0300
committerEion Robb <eion@robbmob.com>2017-04-11 12:23:57 +0300
commit1513ebdf47bd6b3e4e27dffbf2261aa0fab4082a (patch)
tree05e2ad1a5a2f67ad108dfc8663d7381586bf2382 /skypeweb/purple2compat/purple-socket.c
parent35624c3430ce37696eb760dd7c8503f184c8e9f2 (diff)
libpurple 3.0 compatibility, see issue #538
Diffstat (limited to 'skypeweb/purple2compat/purple-socket.c')
-rw-r--r--skypeweb/purple2compat/purple-socket.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/skypeweb/purple2compat/purple-socket.c b/skypeweb/purple2compat/purple-socket.c
new file mode 100644
index 0000000..bf429ed
--- /dev/null
+++ b/skypeweb/purple2compat/purple-socket.c
@@ -0,0 +1,415 @@
+/* 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);
+ }
+}