--- 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 @@
+purple_network_get_local_system_ip_from_gio(GSocketConnection *sockconn) + GInetSocketAddress *inetsockaddr; + 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); purple_network_get_all_local_system_ips(void)
@@ -286,6 +311,42 @@
return purple_network_get_local_system_ip(fd);
+purple_network_get_my_ip_from_gio(GSocketConnection *sockconn) + 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))) { + /* Check if STUN discovery was already done */ + stun = purple_stun_discover(NULL); + if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) { + /* Attempt to get the IP from a NAT device using UPnP */ + ip = purple_upnp_get_public_ip(); + /* Attempt to get the IP from a NAT device using NAT-PMP */ + ip = purple_pmp_get_public_ip(); + /* Just fetch the IP of the local system */ + return purple_network_get_local_system_ip_from_gio(sockconn); purple_network_set_upnp_port_mapping_cb(gboolean success, gpointer data)
--- 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)
- ret = purple_ssl_write(js->gsc, data, len);
- ret = write(js->fd, data, len);
static void jabber_send_cb(gpointer data, gint source, PurpleInputCondition cond)
@@ -406,7 +394,7 @@
- ret = jabber_do_send(js, output, writelen);
+ ret = purple_ssl_write(js->gsc, output, writelen); if (ret < 0 && errno == EAGAIN)
@@ -422,6 +410,24 @@
purple_circular_buffer_mark_read(js->write_buffer, ret);
+jabber_push_bytes_cb(GObject *source, GAsyncResult *res, gpointer data) + PurpleQueuedOutputStream *stream = PURPLE_QUEUED_OUTPUT_STREAM(source); + JabberStream *js = data; + result = purple_queued_output_stream_push_bytes_finish(stream, res, &error); + 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)
@@ -432,9 +438,18 @@
if (js->state == JABBER_STREAM_CONNECTED)
jabber_stream_restart_inactivity_timer(js);
- ret = jabber_do_send(js, data, len);
+ 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); + ret = purple_ssl_write(js->gsc, data, len); @@ -458,10 +473,10 @@
- js->writeh = purple_input_add(
- js->gsc ? js->gsc->fd : js->fd,
- PURPLE_INPUT_WRITE, jabber_send_cb, js);
+ js->writeh = purple_input_add(js->gsc->fd, PURPLE_INPUT_WRITE, purple_circular_buffer_append(js->write_buffer,
@@ -532,8 +547,9 @@
- if (!js->gsc && js->fd<0)
+ if (!js->gsc && js->input == NULL) { @@ -683,16 +699,21 @@
-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);
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); purple_connection_update_last_received(gc);
if (js->sasl_maxbuf > 0) {
@@ -721,23 +742,20 @@
- purple_debug_misc("jabber", "Recv (%d): %s", len, buf);
+ purple_debug_misc("jabber", "Recv (%" G_GSSIZE_FORMAT "): %s", len, jabber_parser_process(js, buf, len);
- } else if(len < 0 && errno == EAGAIN) {
- tmp = g_strdup(_("Server closed the connection"));
- tmp = g_strdup_printf(_("Lost connection with server: %s"),
- purple_connection_error(js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+ 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); @@ -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.
- if (!DuplicateHandle(GetCurrentProcess(), (HANDLE)fd, GetCurrentProcess(),
- &newfd, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
-/* End function grabbed from GLib */
jabber_login_callback(GObject *source_object, GAsyncResult *res, gpointer data)
GSocketClient *client = G_SOCKET_CLIENT(source_object);
conn = g_socket_client_connect_to_host_finish(client, res, &error);
@@ -879,26 +876,20 @@
- 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));
+ 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);
- 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); @@ -918,22 +909,30 @@
static void tls_init(JabberStream *js)
+ 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->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->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); srv_resolved_cb(GObject *source_object, GAsyncResult *result, gpointer data)
GSocketClient *client = G_SOCKET_CLIENT(source_object);
+ JabberStream *js = data;
- JabberStream *js = data;
conn = g_socket_client_connect_to_service_finish(client, result, &error);
@@ -966,26 +965,20 @@
- 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));
+ 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);
- 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); @@ -1010,7 +1003,6 @@
js = g_new0(JabberStream, 1);
purple_connection_set_protocol_data(gc, js);
js->http_conns = soup_session_new_with_options(SOUP_SESSION_PROXY_RESOLVER,
g_object_unref(resolver);
@@ -1691,19 +1683,24 @@
jabber_bosh_connection_destroy(js->bosh);
- } 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);
purple_ssl_close(js->gsc);
- } else if (js->fd > 0) {
+ } else if (js->output != NULL) { - purple_input_remove(js->inpa);
+ g_source_remove(js->inpa);
+ 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);
@@ -1742,7 +1739,7 @@
g_object_unref(G_OBJECT(js->write_buffer));
- purple_input_remove(js->writeh);
+ g_source_remove(js->writeh); if (js->auth_mech && js->auth_mech->dispose)
js->auth_mech->dispose(js);
--- 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 @@
- 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) */