pidgin/pidgin

xmpp: Convert unencrypted connections to pure GIO.

2019-11-01, Elliott Sales de Andrade
6320c272e8b2
Parents 52dc2d315d73
Children d38ea23785a5
xmpp: Convert unencrypted connections to pure GIO.
--- a/libpurple/network.c Mon Oct 28 23:58:20 2019 -0400
+++ b/libpurple/network.c Fri Nov 01 02:38:50 2019 -0400
@@ -150,6 +150,31 @@
return "0.0.0.0";
}
+static const char *
+purple_network_get_local_system_ip_from_gio(GSocketConnection *sockconn)
+{
+ GSocketAddress *addr;
+ GInetSocketAddress *inetsockaddr;
+ static char ip[16];
+
+ strcpy(ip, "0.0.0.0");
+
+ addr = g_socket_connection_get_local_address(sockconn, NULL);
+ if ((inetsockaddr = G_INET_SOCKET_ADDRESS(addr)) != NULL) {
+ GInetAddress *inetaddr =
+ g_inet_socket_address_get_address(inetsockaddr);
+ if (g_inet_address_get_family(inetaddr) == G_SOCKET_FAMILY_IPV4 &&
+ !g_inet_address_get_is_loopback(inetaddr)) {
+ gchar *tmp = g_inet_address_to_string(inetaddr);
+ g_snprintf(ip, 16, "%s", tmp);
+ g_free(tmp);
+ }
+ }
+ g_object_unref(addr);
+
+ return ip;
+}
+
GList *
purple_network_get_all_local_system_ips(void)
{
@@ -286,6 +311,42 @@
return purple_network_get_local_system_ip(fd);
}
+const char *
+purple_network_get_my_ip_from_gio(GSocketConnection *sockconn)
+{
+ const char *ip = NULL;
+ PurpleStunNatDiscovery *stun;
+
+ /* Check if the user specified an IP manually */
+ if (!purple_prefs_get_bool("/purple/network/auto_ip")) {
+ ip = purple_network_get_public_ip();
+ /* Make sure the IP address entered by the user is valid */
+ if ((ip != NULL) && (purple_network_is_ipv4(ip))) {
+ return ip;
+ }
+ } else {
+ /* Check if STUN discovery was already done */
+ stun = purple_stun_discover(NULL);
+ if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) {
+ return stun->publicip;
+ }
+
+ /* Attempt to get the IP from a NAT device using UPnP */
+ ip = purple_upnp_get_public_ip();
+ if (ip != NULL) {
+ return ip;
+ }
+
+ /* Attempt to get the IP from a NAT device using NAT-PMP */
+ ip = purple_pmp_get_public_ip();
+ if (ip != NULL) {
+ return ip;
+ }
+ }
+
+ /* Just fetch the IP of the local system */
+ return purple_network_get_local_system_ip_from_gio(sockconn);
+}
static void
purple_network_set_upnp_port_mapping_cb(gboolean success, gpointer data)
--- a/libpurple/network.h Mon Oct 28 23:58:20 2019 -0400
+++ b/libpurple/network.h Fri Nov 01 02:38:50 2019 -0400
@@ -29,6 +29,7 @@
*/
#include <glib.h>
+#include <gio/gio.h>
G_BEGIN_DECLS
@@ -113,6 +114,27 @@
const char *purple_network_get_my_ip(int fd);
/**
+ * purple_network_get_my_ip_from_gio:
+ * @sockconn: The socket connection to use to help figure out the IP, or %NULL.
+ *
+ * Returns the IP address that should be used anywhere a
+ * public IP addresses is needed (listening for an incoming
+ * file transfer, etc).
+ *
+ * If the user has manually specified an IP address via
+ * preferences, then this IP is returned. Otherwise the
+ * IP address returned by purple_network_get_local_system_ip_from_gio()
+ * is returned.
+ *
+ * Note: The returned string is a pointer to a static buffer. If this
+ * function is called twice, it may be important to make a copy
+ * of the returned string.
+ *
+ * Returns: The local IP address to be used.
+ */
+const char *purple_network_get_my_ip_from_gio(GSocketConnection *sockconn);
+
+/**
* purple_network_listen:
* @port: The port number to bind to. Must be greater than 0.
* @socket_family: The protocol family of the socket. This should be
--- a/libpurple/protocols/jabber/jabber.c Mon Oct 28 23:58:20 2019 -0400
+++ b/libpurple/protocols/jabber/jabber.c Fri Nov 01 02:38:50 2019 -0400
@@ -379,18 +379,6 @@
}
}
-static int jabber_do_send(JabberStream *js, const char *data, int len)
-{
- int ret;
-
- if (js->gsc)
- ret = purple_ssl_write(js->gsc, data, len);
- else
- ret = write(js->fd, data, len);
-
- return ret;
-}
-
static void jabber_send_cb(gpointer data, gint source, PurpleInputCondition cond)
{
JabberStream *js = data;
@@ -406,7 +394,7 @@
return;
}
- ret = jabber_do_send(js, output, writelen);
+ ret = purple_ssl_write(js->gsc, output, writelen);
if (ret < 0 && errno == EAGAIN)
return;
@@ -422,6 +410,24 @@
purple_circular_buffer_mark_read(js->write_buffer, ret);
}
+static void
+jabber_push_bytes_cb(GObject *source, GAsyncResult *res, gpointer data)
+{
+ PurpleQueuedOutputStream *stream = PURPLE_QUEUED_OUTPUT_STREAM(source);
+ JabberStream *js = data;
+ gboolean result;
+ GError *error = NULL;
+
+ result = purple_queued_output_stream_push_bytes_finish(stream, res, &error);
+
+ if (!result) {
+ purple_queued_output_stream_clear_queue(stream);
+
+ g_prefix_error(&error, "%s", _("Lost connection with server: "));
+ purple_connection_take_error(js->gc, error);
+ }
+}
+
static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
{
int ret;
@@ -432,9 +438,18 @@
if (js->state == JABBER_STREAM_CONNECTED)
jabber_stream_restart_inactivity_timer(js);
- if (js->writeh == 0)
- ret = jabber_do_send(js, data, len);
- else {
+ if (js->gsc == NULL) {
+ GBytes *output = g_bytes_new(data, len);
+ purple_queued_output_stream_push_bytes_async(
+ js->output, output, G_PRIORITY_DEFAULT, js->cancellable,
+ jabber_push_bytes_cb, js);
+ g_bytes_unref(output);
+ return success;
+ }
+
+ if (js->writeh == 0) {
+ ret = purple_ssl_write(js->gsc, data, len);
+ } else {
ret = -1;
errno = EAGAIN;
}
@@ -458,10 +473,10 @@
} else if (ret < len) {
if (ret < 0)
ret = 0;
- if (js->writeh == 0)
- js->writeh = purple_input_add(
- js->gsc ? js->gsc->fd : js->fd,
- PURPLE_INPUT_WRITE, jabber_send_cb, js);
+ if (js->writeh == 0) {
+ js->writeh = purple_input_add(js->gsc->fd, PURPLE_INPUT_WRITE,
+ jabber_send_cb, js);
+ }
purple_circular_buffer_append(js->write_buffer,
data + ret, len - ret);
}
@@ -532,8 +547,9 @@
if (js->sasl_maxbuf>0) {
int pos = 0;
- if (!js->gsc && js->fd<0)
+ if (!js->gsc && js->input == NULL) {
g_return_if_reached();
+ }
while (pos < len) {
int towrite;
@@ -683,16 +699,21 @@
}
static void
-jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition)
+jabber_recv_cb(GObject *stream, gpointer data)
{
PurpleConnection *gc = data;
JabberStream *js = purple_connection_get_protocol_data(gc);
- int len;
+ gssize len;
static char buf[4096];
+ GError *error = NULL;
PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
- if((len = read(js->fd, buf, sizeof(buf) - 1)) > 0) {
+ len = g_pollable_input_stream_read_nonblocking(
+ G_POLLABLE_INPUT_STREAM(stream), buf, sizeof(buf) - 1,
+ js->cancellable, &error);
+
+ if (len > 0) {
purple_connection_update_last_received(gc);
#ifdef HAVE_CYRUS_SASL
if (js->sasl_maxbuf > 0) {
@@ -721,23 +742,20 @@
}
#endif
buf[len] = '\0';
- purple_debug_misc("jabber", "Recv (%d): %s", len, buf);
+ purple_debug_misc("jabber", "Recv (%" G_GSSIZE_FORMAT "): %s", len,
+ buf);
jabber_parser_process(js, buf, len);
if(js->reinit)
jabber_stream_init(js);
- } else if(len < 0 && errno == EAGAIN) {
- return;
- } else {
- gchar *tmp;
- if (len == 0)
- tmp = g_strdup(_("Server closed the connection"));
- else
- tmp = g_strdup_printf(_("Lost connection with server: %s"),
- g_strerror(errno));
- purple_connection_error(js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- g_free(tmp);
+ } else if (len == 0) {
+ purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Server closed the connection"));
+ } else if (error->code != G_IO_ERROR_WOULD_BLOCK &&
+ error->code != G_IO_ERROR_CANCELLED) {
+ g_prefix_error(&error, "%s", _("Lost connection with server: "));
+ purple_connection_g_error(js->gc, error);
}
+ g_clear_error(&error);
}
static void
@@ -823,34 +841,13 @@
}
}
-/* Grabbed duplicate_fd() from GLib's testcases (gio/tests/socket.c).
- * Can be dropped once this prpl has been fully converted to Gio.
- */
-static int
-duplicate_fd(int fd)
-{
-#ifdef G_OS_WIN32
- HANDLE newfd;
-
- if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)fd, GetCurrentProcess(),
- &newfd, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
- return -1;
- }
-
- return (int)newfd;
-#else
- return dup(fd);
-#endif
-}
-/* End function grabbed from GLib */
-
static void
jabber_login_callback(GObject *source_object, GAsyncResult *res, gpointer data)
{
GSocketClient *client = G_SOCKET_CLIENT(source_object);
JabberStream *js = data;
GSocketConnection *conn;
- GSocket *socket;
+ GSource *source;
GError *error = NULL;
conn = g_socket_client_connect_to_host_finish(client, res, &error);
@@ -879,26 +876,20 @@
return;
}
- socket = g_socket_connection_get_socket(conn);
- g_assert(socket != NULL);
-
- /* Duplicate the file descriptor, and then free the connection.
- * libpurple's proxy code doesn't keep an object around for the
- * lifetime of the connection. Therefore, in order to not leak
- * memory, the GSocketConnection must be freed here. In order
- * to avoid the double close/free of the file descriptor, the
- * file descriptor is duplicated.
- */
- js->fd = duplicate_fd(g_socket_get_fd(socket));
- g_object_unref(conn);
+ js->stream = G_IO_STREAM(conn);
+ js->input = g_io_stream_get_input_stream(js->stream);
+ js->output = purple_queued_output_stream_new(
+ g_io_stream_get_output_stream(js->stream));
if (js->state == JABBER_STREAM_CONNECTING) {
jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
}
jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING);
- js->inpa =
- purple_input_add(js->fd, PURPLE_INPUT_READ, jabber_recv_cb, js->gc);
+ source = g_pollable_input_stream_create_source(
+ G_POLLABLE_INPUT_STREAM(js->input), js->cancellable);
+ g_source_set_callback(source, (GSourceFunc)jabber_recv_cb, js->gc, NULL);
+ js->inpa = g_source_attach(source, NULL);
}
static void
@@ -918,22 +909,30 @@
static void tls_init(JabberStream *js)
{
+ GSocket *socket;
+ gint fd;
+
+ socket = g_socket_connection_get_socket(G_SOCKET_CONNECTION(js->stream));
+ g_assert(socket != NULL);
+
+ fd = g_socket_get_fd(socket);
+
purple_input_remove(js->inpa);
js->inpa = 0;
- js->gsc = purple_ssl_connect_with_host_fd(purple_connection_get_account(js->gc), js->fd,
- jabber_login_callback_ssl, jabber_ssl_connect_failure, js->certificate_CN, js->gc);
- /* The fd is no longer our concern */
- js->fd = -1;
+ js->gsc = purple_ssl_connect_with_host_fd(
+ purple_connection_get_account(js->gc), fd,
+ jabber_login_callback_ssl, jabber_ssl_connect_failure,
+ js->certificate_CN, js->gc);
}
static void
srv_resolved_cb(GObject *source_object, GAsyncResult *result, gpointer data)
{
GSocketClient *client = G_SOCKET_CLIENT(source_object);
- GError *error = NULL;
+ JabberStream *js = data;
GSocketConnection *conn;
- GSocket *socket;
- JabberStream *js = data;
+ GSource *source;
+ GError *error = NULL;
conn = g_socket_client_connect_to_service_finish(client, result, &error);
if (error) {
@@ -966,26 +965,20 @@
return;
}
- socket = g_socket_connection_get_socket(conn);
- g_assert(socket != NULL);
-
- /* Duplicate the file descriptor, and then free the connection.
- * libpurple's proxy code doesn't keep an object around for the
- * lifetime of the connection. Therefore, in order to not leak
- * memory, the GSocketConnection must be freed here. In order
- * to avoid the double close/free of the file descriptor, the
- * file descriptor is duplicated.
- */
- js->fd = duplicate_fd(g_socket_get_fd(socket));
- g_object_unref(conn);
+ js->stream = G_IO_STREAM(conn);
+ js->input = g_io_stream_get_input_stream(js->stream);
+ js->output = purple_queued_output_stream_new(
+ g_io_stream_get_output_stream(js->stream));
if (js->state == JABBER_STREAM_CONNECTING) {
jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
}
jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING);
- js->inpa =
- purple_input_add(js->fd, PURPLE_INPUT_READ, jabber_recv_cb, js->gc);
+ source = g_pollable_input_stream_create_source(
+ G_POLLABLE_INPUT_STREAM(js->input), js->cancellable);
+ g_source_set_callback(source, (GSourceFunc)jabber_recv_cb, js->gc, NULL);
+ js->inpa = g_source_attach(source, NULL);
}
static JabberStream *
@@ -1010,7 +1003,6 @@
js = g_new0(JabberStream, 1);
purple_connection_set_protocol_data(gc, js);
js->gc = gc;
- js->fd = -1;
js->http_conns = soup_session_new_with_options(SOUP_SESSION_PROXY_RESOLVER,
resolver, NULL);
g_object_unref(resolver);
@@ -1691,19 +1683,24 @@
if (js->bosh) {
jabber_bosh_connection_destroy(js->bosh);
js->bosh = NULL;
- } else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0)
+ } else if ((js->gsc && js->gsc->fd > 0) || js->output != NULL)
jabber_send_raw(js, "</stream:stream>", -1);
if(js->gsc) {
purple_ssl_close(js->gsc);
- } else if (js->fd > 0) {
+ } else if (js->output != NULL) {
if(js->inpa) {
- purple_input_remove(js->inpa);
+ g_source_remove(js->inpa);
js->inpa = 0;
}
- close(js->fd);
+ purple_gio_graceful_close(js->stream, js->input,
+ G_OUTPUT_STREAM(js->output));
}
+ g_clear_object(&js->output);
+ g_clear_object(&js->input);
+ g_clear_object(&js->stream);
+
jabber_buddy_remove_all_pending_buddy_info_requests(js);
jabber_parser_free(js);
@@ -1742,7 +1739,7 @@
if (js->write_buffer)
g_object_unref(G_OBJECT(js->write_buffer));
if(js->writeh)
- purple_input_remove(js->writeh);
+ g_source_remove(js->writeh);
if (js->auth_mech && js->auth_mech->dispose)
js->auth_mech->dispose(js);
#ifdef HAVE_CYRUS_SASL
--- a/libpurple/protocols/jabber/jabber.h Mon Oct 28 23:58:20 2019 -0400
+++ b/libpurple/protocols/jabber/jabber.h Fri Nov 01 02:38:50 2019 -0400
@@ -67,6 +67,7 @@
#include "media.h"
#include "mediamanager.h"
#include "protocol.h"
+#include "queuedoutputstream.h"
#include "roomlist.h"
#include "sslconn.h"
@@ -119,7 +120,6 @@
struct _JabberStream
{
- int fd;
guint inpa;
GCancellable *cancellable;
@@ -197,6 +197,9 @@
PurpleConnection *gc;
GSocketClient *client;
+ GIOStream *stream;
+ GInputStream *input;
+ PurpleQueuedOutputStream *output;
PurpleSslConnection *gsc;
gboolean registration;
--- a/libpurple/protocols/jabber/si.c Mon Oct 28 23:58:20 2019 -0400
+++ b/libpurple/protocols/jabber/si.c Fri Nov 01 02:38:50 2019 -0400
@@ -508,7 +508,8 @@
g_free(dstaddr);
g_free(jsx->rxqueue);
- host = purple_network_get_my_ip(jsx->js->fd);
+ host = purple_network_get_my_ip_from_gio(
+ G_SOCKET_CONNECTION(jsx->js->stream));
jsx->rxmaxlen = 5 + strlen(host) + 2;
jsx->rxqueue = g_malloc(jsx->rxmaxlen);
@@ -867,7 +868,8 @@
purple_xfer_set_local_port(xfer, purple_network_get_port_from_fd(sock));
g_snprintf(port, sizeof(port), "%hu", purple_xfer_get_local_port(xfer));
- public_ip = purple_network_get_my_ip(jsx->js->fd);
+ public_ip = purple_network_get_my_ip_from_gio(
+ G_SOCKET_CONNECTION(jsx->js->stream));
/* Include the localhost's IPs (for in-network transfers) */
while (local_ips) {