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

github.com/dequis/purple-facebook.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordequis <dx@dxzone.com.ar>2016-11-21 16:16:25 +0300
committerdequis <dx@dxzone.com.ar>2016-11-21 16:24:02 +0300
commit4bff1463869934e1b2ea83d166746bea08045714 (patch)
tree221c08d9cce473ab3e0258f3a7f7aa54f04d8c3a
parent067af3febef217fd164f9ef26a05457768de65d0 (diff)
VERSION: update to c9b74a765767
Relevant changes: https://bitbucket.org/pidgin/main/pull-requests/167 * fb_api_cb_contacts: Make all the other fields optional too * fb_http_urlcmp: more loose comparison, to avoid showing urls twice * Prevent disconnections on 509 errors, "Invalid attachment id" * Make $.hugePictureUrl.uri optional * Store sent message id in lastmid, to deduplicate echoed messages
-rw-r--r--MANIFEST_PIDGIN1
-rw-r--r--VERSION2
-rw-r--r--patches/02-glibcompat.patch28
-rw-r--r--patches/05-revert-http-callbacks.patch42
-rw-r--r--patches/06-revert-purple-socket.patch637
-rw-r--r--patches/07-revert-http-gio.patch430
6 files changed, 1122 insertions, 18 deletions
diff --git a/MANIFEST_PIDGIN b/MANIFEST_PIDGIN
index 391033b..dd29b52 100644
--- a/MANIFEST_PIDGIN
+++ b/MANIFEST_PIDGIN
@@ -26,4 +26,3 @@ libpurple/protocols/facebook/util.c
libpurple/protocols/facebook/util.h
libpurple/purple-socket.c
libpurple/purple-socket.h
-share/ca-certs/GTE_CyberTrust_Global_Root.pem
diff --git a/VERSION b/VERSION
index 714caa5..a76e224 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-56d191003b34
+c9b74a765767
diff --git a/patches/02-glibcompat.patch b/patches/02-glibcompat.patch
index 4669b77..663ab0c 100644
--- a/patches/02-glibcompat.patch
+++ b/patches/02-glibcompat.patch
@@ -1,12 +1,11 @@
-diff -r 1852eb9052a0 libpurple/glibcompat.h
---- a/libpurple/glibcompat.h Fri Dec 25 15:20:41 2015 -0500
-+++ b/libpurple/glibcompat.h Fri Dec 25 15:49:07 2015 -0500
-@@ -61,6 +61,33 @@
- return FALSE;
- }
-
-+#if !GLIB_CHECK_VERSION(2, 32, 0)
+--- a/libpurple/glibcompat.h 2016-11-21 09:38:39.250858177 -0300
++++ b/libpurple/glibcompat.h 2016-11-21 09:39:44.789845560 -0300
+@@ -66,6 +66,30 @@
+ /******************************************************************************
+ * g_assert_* macros
+ *****************************************************************************/
+
++#if !GLIB_CHECK_VERSION(2, 32, 0)
+static inline GByteArray * g_byte_array_new_take(guint8 *data, gsize len)
+{
+ GByteArray *array;
@@ -23,15 +22,12 @@ diff -r 1852eb9052a0 libpurple/glibcompat.h
+ g_queue_foreach(queue, (GFunc)free_func, NULL);
+ g_queue_free(queue);
+}
++#endif
+
+#if !GLIB_CHECK_VERSION(2, 30, 0)
-+
+#define G_VALUE_INIT {0, {{0}}}
++#endif
+
-+#endif /* < 2.30.0 */
-+
-+#endif /* < 2.32.0 */
-+
- #endif /* < 2.36.0 */
-
-
+ #if !GLIB_CHECK_VERSION(2, 38, 0)
+ #define g_assert_true(expr) G_STMT_START { \
+ if G_LIKELY (expr) ; else \
diff --git a/patches/05-revert-http-callbacks.patch b/patches/05-revert-http-callbacks.patch
new file mode 100644
index 0000000..36d1d17
--- /dev/null
+++ b/patches/05-revert-http-callbacks.patch
@@ -0,0 +1,42 @@
+diff --git a/libpurple/http.c b/libpurple/http.c
+--- a/libpurple/http.c
++++ b/libpurple/http.c
+@@ -486,11 +486,7 @@ purple_http_socket_connect_new_cb(GObjec
+ cb_data = g_object_steal_data(source, "cb_data");
+
+ if (conn == NULL) {
+- if (!g_error_matches(error,
+- G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+- cb(hs, error->message, cb_data);
+- }
+-
++ cb(hs, error->message, cb_data);
+ g_clear_error(&error);
+ return;
+ }
+@@ -1162,10 +1158,8 @@ static gboolean _purple_http_recv_loopbo
+ &error);
+ got_anything = (len > 0);
+
+- if (len < 0 && (g_error_matches(error,
+- G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
+- g_error_matches(error,
+- G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
++ if (len < 0 && g_error_matches(error,
++ G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ g_clear_error(&error);
+ return FALSE;
+ }
+@@ -1450,10 +1444,8 @@ static gboolean _purple_http_send(GObjec
+ &error);
+ }
+
+- if (written < 0 && (g_error_matches(error,
+- G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
+- g_error_matches(error,
+- G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
++ if (written < 0 && g_error_matches(error,
++ G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+ g_clear_error(&error);
+ return G_SOURCE_CONTINUE;
+ }
diff --git a/patches/06-revert-purple-socket.patch b/patches/06-revert-purple-socket.patch
new file mode 100644
index 0000000..230ccd0
--- /dev/null
+++ b/patches/06-revert-purple-socket.patch
@@ -0,0 +1,637 @@
+diff --git a/libpurple/purple-socket.c b/libpurple/purple-socket.c
+new file mode 100644
+--- /dev/null
++++ b/libpurple/purple-socket.c
+@@ -0,0 +1,410 @@
++/* 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);
++ }
++}
+diff --git a/libpurple/purple-socket.h b/libpurple/purple-socket.h
+new file mode 100644
+--- /dev/null
++++ b/libpurple/purple-socket.h
+@@ -0,0 +1,217 @@
++/* 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
++ */
++
++#ifndef _PURPLE_SOCKET_H_
++#define _PURPLE_SOCKET_H_
++/**
++ * SECTION:purple-socket
++ * @section_id: libpurple-purple-socket
++ * @short_description: <filename>purple-socket.h</filename>
++ * @title: Generic Sockets
++ */
++
++#include "connection.h"
++
++/**
++ * PurpleSocket:
++ *
++ * A structure holding all resources needed for the TCP connection.
++ */
++typedef struct _PurpleSocket PurpleSocket;
++
++/**
++ * PurpleSocketConnectCb:
++ * @ps: The socket.
++ * @error: Error message, or NULL if connection was successful.
++ * @user_data: The user data passed with callback function.
++ *
++ * A callback fired after (successfully or not) establishing a connection.
++ */
++typedef void (*PurpleSocketConnectCb)(PurpleSocket *ps, const gchar *error,
++ gpointer user_data);
++
++/**
++ * purple_socket_new:
++ * @gc: The connection for which the socket is needed, or NULL.
++ *
++ * Creates new, disconnected socket.
++ *
++ * Passing a PurpleConnection allows for proper proxy handling.
++ *
++ * Returns: The new socket struct.
++ */
++PurpleSocket *
++purple_socket_new(PurpleConnection *gc);
++
++/**
++ * purple_socket_get_connection:
++ * @ps: The socket.
++ *
++ * Gets PurpleConnection tied with specified socket.
++ *
++ * Returns: The PurpleConnection object.
++ */
++PurpleConnection *
++purple_socket_get_connection(PurpleSocket *ps);
++
++/**
++ * purple_socket_set_tls:
++ * @ps: The socket.
++ * @is_tls: TRUE, if TLS should be handled transparently, FALSE otherwise.
++ *
++ * Determines, if socket should handle TLS.
++ */
++void
++purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls);
++
++/**
++ * purple_socket_set_host:
++ * @ps: The socket.
++ * @host: The connection host.
++ *
++ * Sets connection host.
++ */
++void
++purple_socket_set_host(PurpleSocket *ps, const gchar *host);
++
++/**
++ * purple_socket_set_port:
++ * @ps: The socket.
++ * @port: The connection port.
++ *
++ * Sets connection port.
++ */
++void
++purple_socket_set_port(PurpleSocket *ps, int port);
++
++/**
++ * purple_socket_connect:
++ * @ps: The socket.
++ * @cb: The function to call after establishing a connection, or on
++ * error.
++ * @user_data: The user data to be passed to callback function.
++ *
++ * Establishes a connection.
++ *
++ * Returns: TRUE on success (this doesn't mean it's connected yet), FALSE
++ * otherwise.
++ */
++gboolean
++purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
++ gpointer user_data);
++
++/**
++ * purple_socket_read:
++ * @ps: The socket.
++ * @buf: The buffer to write data to.
++ * @len: The buffer size.
++ *
++ * Reads incoming data from socket.
++ *
++ * This function deals with TLS, if the socket is configured to do it.
++ *
++ * Returns: Amount of data written, or -1 on error (errno will be also be set).
++ */
++gssize
++purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len);
++
++/**
++ * purple_socket_write:
++ * @ps: The socket.
++ * @buf: The buffer to read data from.
++ * @len: The amount of data to read and send.
++ *
++ * Sends data through socket.
++ *
++ * This function deals with TLS, if the socket is configured to do it.
++ *
++ * Returns: Amount of data sent, or -1 on error (errno will albo be set).
++ */
++gssize
++purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len);
++
++/**
++ * purple_socket_watch:
++ * @ps: The socket.
++ * @cond: The condition type.
++ * @func: The callback function for data, or NULL to remove any
++ * existing callbacks.
++ * @user_data: The user data to be passed to callback function.
++ *
++ * Adds an input handler for the socket.
++ *
++ * If the specified socket had input handler already registered, it will be
++ * removed. To remove any input handlers, pass an NULL handler function.
++ */
++void
++purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond,
++ PurpleInputFunction func, gpointer user_data);
++
++/**
++ * purple_socket_get_fd:
++ * @ps: The socket
++ *
++ * Gets underlying file descriptor for socket.
++ *
++ * It's not meant to read/write data (use purple_socket_read/
++ * purple_socket_write), rather for watching for changes with select().
++ *
++ * Returns: The file descriptor, or -1 on error.
++ */
++int
++purple_socket_get_fd(PurpleSocket *ps);
++
++/**
++ * purple_socket_set_data:
++ * @ps: The socket.
++ * @key: The unique key.
++ * @data: The data to assign, or NULL to remove.
++ *
++ * Sets extra data for a socket.
++ */
++void
++purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data);
++
++/**
++ * purple_socket_get_data:
++ * @ps: The socket.
++ * @key: The unqiue key.
++ *
++ * Returns extra data in a socket.
++ *
++ * Returns: The data associated with the key.
++ */
++gpointer
++purple_socket_get_data(PurpleSocket *ps, const gchar *key);
++
++/**
++ * purple_socket_destroy:
++ * @ps: The socket.
++ *
++ * Destroys the socket, closes connection and frees all resources.
++ *
++ * If file descriptor for the socket was extracted with purple_socket_get_fd and
++ * added to event loop, it have to be removed prior this.
++ */
++void
++purple_socket_destroy(PurpleSocket *ps);
++
++#endif /* _PURPLE_SOCKET_H_ */
diff --git a/patches/07-revert-http-gio.patch b/patches/07-revert-http-gio.patch
new file mode 100644
index 0000000..9bfb0d8
--- /dev/null
+++ b/patches/07-revert-http-gio.patch
@@ -0,0 +1,430 @@
+diff --git a/libpurple/http.c b/libpurple/http.c
+--- a/libpurple/http.c
++++ b/libpurple/http.c
+@@ -28,7 +28,7 @@
+ #include "debug.h"
+ #include "ntlm.h"
+ #include "proxy.h"
+-#include "purple-gio.h"
++#include "purple-socket.h"
+
+ #include <zlib.h>
+ #ifndef z_const
+@@ -57,15 +57,9 @@ typedef struct _PurpleHttpKeepaliveReque
+
+ typedef struct _PurpleHttpGzStream PurpleHttpGzStream;
+
+-typedef void (*PurpleHttpSocketConnectCb)(PurpleHttpSocket *hs,
+- const gchar *error, gpointer _hc);
+-
+ struct _PurpleHttpSocket
+ {
+- GSocketConnection *conn;
+- GCancellable *cancellable;
+- guint input_source;
+- guint output_source;
++ PurpleSocket *ps;
+
+ gboolean is_busy;
+ guint use_count;
+@@ -181,7 +175,7 @@ struct _PurpleHttpCookieJar
+ struct _PurpleHttpKeepaliveRequest
+ {
+ PurpleConnection *gc;
+- PurpleHttpSocketConnectCb cb;
++ PurpleSocketConnectCb cb;
+ gpointer user_data;
+
+ PurpleHttpKeepaliveHost *host;
+@@ -253,7 +247,7 @@ gchar * purple_http_cookie_jar_dump(Purp
+ static PurpleHttpKeepaliveRequest *
+ purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool,
+ PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl,
+- PurpleHttpSocketConnectCb cb, gpointer user_data);
++ PurpleSocketConnectCb cb, gpointer user_data);
+ static void
+ purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest *req);
+ static void
+@@ -469,65 +463,23 @@ purple_http_socket_hash(const gchar *hos
+ return g_strdup_printf("%c:%s:%d", (is_ssl ? 'S' : 'R'), host, port);
+ }
+
+-static void
+-purple_http_socket_connect_new_cb(GObject *source, GAsyncResult *res,
+- gpointer user_data)
+-{
+- PurpleHttpSocket *hs = user_data;
+- GSocketConnection *conn;
+- PurpleHttpSocketConnectCb cb;
+- gpointer cb_data;
+- GError *error = NULL;
+-
+- conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
+- res, &error);
+-
+- cb = g_object_steal_data(source, "cb");
+- cb_data = g_object_steal_data(source, "cb_data");
+-
+- if (conn == NULL) {
+- cb(hs, error->message, cb_data);
+- g_clear_error(&error);
+- return;
+- }
+-
+- hs->conn = conn;
+-
+- cb(hs, NULL, cb_data);
+-}
+-
+ static PurpleHttpSocket *
+ purple_http_socket_connect_new(PurpleConnection *gc, const gchar *host,
+- int port, gboolean is_ssl,
+- PurpleHttpSocketConnectCb cb, gpointer user_data)
++ int port, gboolean is_ssl, PurpleSocketConnectCb cb, gpointer user_data)
+ {
+- PurpleHttpSocket *hs;
+- GSocketClient *client;
+- GError *error = NULL;
+-
+- client = purple_gio_socket_client_new(
+- purple_connection_get_account(gc), &error);
+-
+- if (client == NULL) {
+- purple_debug_error("http", "Error connecting to '%s:%d': %s",
+- host, port, error->message);
+- g_clear_error(&error);
++ PurpleHttpSocket *hs = g_new0(PurpleHttpSocket, 1);
++
++ hs->ps = purple_socket_new(gc);
++ purple_socket_set_data(hs->ps, "hs", hs);
++ purple_socket_set_tls(hs->ps, is_ssl);
++ purple_socket_set_host(hs->ps, host);
++ purple_socket_set_port(hs->ps, port);
++ if (!purple_socket_connect(hs->ps, cb, user_data)) {
++ purple_socket_destroy(hs->ps);
++ g_free(hs);
+ return NULL;
+ }
+
+- hs = g_new0(PurpleHttpSocket, 1);
+- hs->cancellable = g_cancellable_new();
+-
+- g_socket_client_set_tls(client, is_ssl);
+- g_object_set_data(G_OBJECT(client), "cb", cb);
+- g_object_set_data(G_OBJECT(client), "cb_data", user_data);
+-
+- g_socket_client_connect_to_host_async(client,
+- host, port, hs->cancellable,
+- purple_http_socket_connect_new_cb, hs);
+-
+- g_object_unref(client);
+-
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "new socket created: %p\n", hs);
+
+@@ -543,26 +495,7 @@ purple_http_socket_close_free(PurpleHttp
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "destroying socket: %p\n", hs);
+
+- if (hs->input_source > 0) {
+- g_source_remove(hs->input_source);
+- hs->input_source = 0;
+- }
+-
+- if (hs->output_source > 0) {
+- g_source_remove(hs->output_source);
+- hs->output_source = 0;
+- }
+-
+- if (hs->cancellable != NULL) {
+- g_cancellable_cancel(hs->cancellable);
+- g_clear_object(&hs->cancellable);
+- }
+-
+- if (hs->conn != NULL) {
+- purple_gio_graceful_close(G_IO_STREAM(hs->conn), NULL, NULL);
+- g_clear_object(&hs->conn);
+- }
+-
++ purple_socket_destroy(hs->ps);
+ g_free(hs);
+ }
+
+@@ -749,9 +682,10 @@ static void _purple_http_disconnect(Purp
+ gboolean is_graceful);
+
+ static void _purple_http_gen_headers(PurpleHttpConnection *hc);
+-static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc);
+-static gboolean _purple_http_recv(GObject *source, gpointer _hc);
+-static gboolean _purple_http_send(GObject *source, gpointer _hc);
++static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd);
++static void _purple_http_recv(gpointer _hc, gint fd,
++ PurpleInputCondition cond);
++static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond);
+
+ /* closes current connection (if exists), estabilishes one and proceeds with
+ * request */
+@@ -1143,31 +1077,21 @@ static gboolean _purple_http_recv_body(P
+ return _purple_http_recv_body_data(hc, buf, len);
+ }
+
+-static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc)
++static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd)
+ {
+ int len;
+ gchar buf[4096];
+ gboolean got_anything;
+- GError *error = NULL;
+-
+- len = g_pollable_input_stream_read_nonblocking(
+- G_POLLABLE_INPUT_STREAM(
+- g_io_stream_get_input_stream(
+- G_IO_STREAM(hc->socket->conn))),
+- buf, sizeof(buf), hc->socket->cancellable,
+- &error);
++
++ len = purple_socket_read(hc->socket->ps, (guchar*)buf, sizeof(buf));
+ got_anything = (len > 0);
+
+- if (len < 0 && g_error_matches(error,
+- G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+- g_clear_error(&error);
++ if (len < 0 && errno == EAGAIN)
+ return FALSE;
+- }
+
+ if (len < 0) {
+ _purple_http_error(hc, _("Error reading from %s: %s"),
+- hc->url->host, error->message);
+- g_clear_error(&error);
++ hc->url->host, g_strerror(errno));
+ return FALSE;
+ }
+
+@@ -1346,13 +1270,11 @@ static gboolean _purple_http_recv_loopbo
+ return got_anything;
+ }
+
+-static gboolean _purple_http_recv(GObject *source, gpointer _hc)
++static void _purple_http_recv(gpointer _hc, gint fd, PurpleInputCondition cond)
+ {
+ PurpleHttpConnection *hc = _hc;
+
+- while (_purple_http_recv_loopbody(hc));
+-
+- return G_SOURCE_CONTINUE;
++ while (_purple_http_recv_loopbody(hc, fd));
+ }
+
+ static void _purple_http_send_got_data(PurpleHttpConnection *hc,
+@@ -1383,19 +1305,17 @@ static void _purple_http_send_got_data(P
+ hc->request->contents_length = estimated_length;
+ }
+
+-static gboolean _purple_http_send(GObject *source, gpointer _hc)
++static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond)
+ {
+ PurpleHttpConnection *hc = _hc;
+ int written, write_len;
+ const gchar *write_from;
+ gboolean writing_headers;
+- GError *error = NULL;
+- GSource *gsource;
+
+ /* Waiting for data. This could be written more efficiently, by removing
+ * (and later, adding) hs->inpa. */
+ if (hc->contents_reader_requested)
+- return G_SOURCE_CONTINUE;
++ return;
+
+ _purple_http_gen_headers(hc);
+
+@@ -1408,7 +1328,7 @@ static gboolean _purple_http_send(GObjec
+ hc->request_header_written;
+ } else if (hc->request->contents_reader) {
+ if (hc->contents_reader_requested)
+- return G_SOURCE_CONTINUE; /* waiting for data */
++ return; /* waiting for data */
+ if (!hc->contents_reader_buffer)
+ hc->contents_reader_buffer = g_string_new("");
+ if (hc->contents_reader_buffer->len == 0) {
+@@ -1421,7 +1341,7 @@ static gboolean _purple_http_send(GObjec
+ PURPLE_HTTP_MAX_READ_BUFFER_LEN,
+ hc->request->contents_reader_data,
+ _purple_http_send_got_data);
+- return G_SOURCE_CONTINUE;
++ return;
+ }
+ write_from = hc->contents_reader_buffer->str;
+ write_len = hc->contents_reader_buffer->len;
+@@ -1436,19 +1356,12 @@ static gboolean _purple_http_send(GObjec
+ purple_debug_warning("http", "Nothing to write\n");
+ written = 0;
+ } else {
+- written = g_pollable_output_stream_write_nonblocking(
+- G_POLLABLE_OUTPUT_STREAM(
+- g_io_stream_get_output_stream(
+- G_IO_STREAM(hc->socket->conn))),
+- write_from, write_len, hc->socket->cancellable,
+- &error);
++ written = purple_socket_write(hc->socket->ps,
++ (const guchar*)write_from, write_len);
+ }
+
+- if (written < 0 && g_error_matches(error,
+- G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
+- g_clear_error(&error);
+- return G_SOURCE_CONTINUE;
+- }
++ if (written < 0 && errno == EAGAIN)
++ return;
+
+ if (written < 0) {
+ if (hc->request_header_written == 0 &&
+@@ -1457,22 +1370,21 @@ static gboolean _purple_http_send(GObjec
+ purple_debug_info("http", "Keep-alive connection "
+ "expired (when writing), retrying...\n");
+ purple_http_conn_retry(hc);
+- } else {
+- _purple_http_error(hc, _("Error writing to %s: %s"),
+- hc->url->host, error->message);
++ return;
+ }
+
+- g_clear_error(&error);
+- return G_SOURCE_CONTINUE;
++ _purple_http_error(hc, _("Error writing to %s: %s"),
++ hc->url->host, g_strerror(errno));
++ return;
+ }
+
+ if (writing_headers) {
+ hc->request_header_written += written;
+ purple_http_conn_notify_progress_watcher(hc);
+ if (hc->request_header_written < hc->request_header->len)
+- return G_SOURCE_CONTINUE;
++ return;
+ if (hc->request->contents_length > 0)
+- return G_SOURCE_CONTINUE;
++ return;
+ } else {
+ hc->request_contents_written += written;
+ purple_http_conn_notify_progress_watcher(hc);
+@@ -1482,24 +1394,14 @@ static gboolean _purple_http_send(GObjec
+ hc->request_contents_written <
+ (guint)hc->request->contents_length)
+ {
+- return G_SOURCE_CONTINUE;
++ return;
+ }
+ }
+
+ /* request is completely written, let's read the response */
+ hc->is_reading = TRUE;
+- gsource = g_pollable_input_stream_create_source(
+- G_POLLABLE_INPUT_STREAM(
+- g_io_stream_get_input_stream(
+- G_IO_STREAM(hc->socket->conn))),
+- NULL);
+- g_source_set_callback(gsource,
+- (GSourceFunc)_purple_http_recv, hc, NULL);
+- hc->socket->input_source = g_source_attach(gsource, NULL);
+- g_source_unref(gsource);
+-
+- hc->socket->output_source = 0;
+- return G_SOURCE_REMOVE;
++ purple_socket_watch(hc->socket->ps, PURPLE_INPUT_READ,
++ _purple_http_recv, hc);
+ }
+
+ static void _purple_http_disconnect(PurpleHttpConnection *hc,
+@@ -1524,10 +1426,13 @@ static void _purple_http_disconnect(Purp
+ }
+
+ static void
+-_purple_http_connected(PurpleHttpSocket *hs, const gchar *error, gpointer _hc)
++_purple_http_connected(PurpleSocket *ps, const gchar *error, gpointer _hc)
+ {
++ PurpleHttpSocket *hs = NULL;
+ PurpleHttpConnection *hc = _hc;
+- GSource *source;
++
++ if (ps != NULL)
++ hs = purple_socket_get_data(ps, "hs");
+
+ hc->socket_request = NULL;
+ hc->socket = hs;
+@@ -1538,14 +1443,7 @@ static void
+ return;
+ }
+
+- source = g_pollable_output_stream_create_source(
+- G_POLLABLE_OUTPUT_STREAM(
+- g_io_stream_get_output_stream(G_IO_STREAM(hs->conn))),
+- NULL);
+- g_source_set_callback(source,
+- (GSourceFunc)_purple_http_send, hc, NULL);
+- hc->socket->output_source = g_source_attach(source, NULL);
+- g_source_unref(source);
++ purple_socket_watch(ps, PURPLE_INPUT_WRITE, _purple_http_send, hc);
+ }
+
+ static gboolean _purple_http_reconnect(PurpleHttpConnection *hc)
+@@ -2257,7 +2155,7 @@ purple_http_keepalive_pool_unref(PurpleH
+ static PurpleHttpKeepaliveRequest *
+ purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool,
+ PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl,
+- PurpleHttpSocketConnectCb cb, gpointer user_data)
++ PurpleSocketConnectCb cb, gpointer user_data)
+ {
+ PurpleHttpKeepaliveRequest *req;
+ PurpleHttpKeepaliveHost *kahost;
+@@ -2300,15 +2198,19 @@ purple_http_keepalive_pool_request(Purpl
+ }
+
+ static void
+-_purple_http_keepalive_socket_connected(PurpleHttpSocket *hs,
++_purple_http_keepalive_socket_connected(PurpleSocket *ps,
+ const gchar *error, gpointer _req)
+ {
++ PurpleHttpSocket *hs = NULL;
+ PurpleHttpKeepaliveRequest *req = _req;
+
++ if (ps != NULL)
++ hs = purple_socket_get_data(ps, "hs");
++
+ if (hs != NULL)
+ hs->use_count++;
+
+- req->cb(hs, error, req->user_data);
++ req->cb(ps, error, req->user_data);
+ g_free(req);
+ }
+
+@@ -2364,7 +2266,7 @@ static gboolean
+
+ purple_http_keepalive_host_process_queue(host);
+
+- req->cb(hs, NULL, req->user_data);
++ req->cb(hs->ps, NULL, req->user_data);
+ g_free(req);
+
+ return FALSE;
+@@ -2435,16 +2337,7 @@ purple_http_keepalive_pool_release(Purpl
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "releasing a socket: %p\n", hs);
+
+- if (hs->input_source > 0) {
+- g_source_remove(hs->input_source);
+- hs->input_source = 0;
+- }
+-
+- if (hs->output_source > 0) {
+- g_source_remove(hs->output_source);
+- hs->output_source = 0;
+- }
+-
++ purple_socket_watch(hs->ps, 0, NULL, NULL);
+ hs->is_busy = FALSE;
+ host = hs->host;
+