--- a/libpurple/protocols/jabber/auth_cyrus.c Sun Nov 03 09:22:21 2019 +0000
+++ b/libpurple/protocols/jabber/auth_cyrus.c Mon Nov 04 01:18:24 2019 +0000
@@ -402,11 +402,22 @@
jabber_cyrus_start(JabberStream *js, PurpleXmlNode *mechanisms,
PurpleXmlNode **reply, char **error)
- PurpleXmlNode *mechnode;
+ PurpleXmlNode *mechnode, *hostname; js->sasl_mechs = g_string_new("");
js->sasl_password = g_strdup(purple_connection_get_password(js->gc));
+ /* XEP-0233 says we should grab the hostname for Kerberos v5, but there + * is no claim about other SASL mechanisms. Fortunately, most don't + * care what we use, so just use the domainpart. */ + hostname = purple_xmlnode_get_child_with_namespace( + mechanisms, "hostname", NS_XMPP_SERVER_REGISTRATION); + js->serverFQDN = purple_xmlnode_get_data(hostname); + if (js->serverFQDN == NULL) { + js->serverFQDN = g_strdup(js->user->domain); for(mechnode = purple_xmlnode_get_child(mechanisms, "mechanism"); mechnode;
mechnode = purple_xmlnode_get_next_twin(mechnode))
--- a/libpurple/protocols/jabber/jabber.c Sun Nov 03 09:22:21 2019 +0000
+++ b/libpurple/protocols/jabber/jabber.c Mon Nov 04 01:18:24 2019 +0000
@@ -35,6 +35,7 @@
#include "purpleaccountoption.h"
@@ -222,12 +223,13 @@
PurpleAccount *account = NULL;
PurpleXmlNode *starttls = NULL;
- /* It's a secure BOSH connection, just return FALSE and skip, without doing anything extra.
- * XEP-0206 (XMPP Over BOSH): The client SHOULD ignore any Transport Layer Security (TLS)
- * feature since BOSH channel encryption SHOULD be negotiated at the HTTP layer.
+ /* It's a secure BOSH connection, just return FALSE and skip, without doing + * anything extra. XEP-0206 (XMPP Over BOSH): The client SHOULD ignore any + * Transport Layer Security (TLS) feature since BOSH channel encryption + * SHOULD be negotiated at the HTTP layer. - * Note: we are already receiving STARTTLS at this point from a SSL/TLS BOSH connection,
- * so it is not necessary to check if purple_ssl_is_supported().
+ * Note: we are already receiving STARTTLS at this point from a SSL/TLS BOSH + * connection, so it is not necessary to check if SSL is supported. if (js->bosh && jabber_bosh_connection_is_ssl(js->bosh)) {
@@ -366,9 +368,10 @@
jabber_auth_handle_failure(js, *packet);
} else if (purple_strequal(xmlns, NS_XMPP_TLS)) {
- if (js->state != JABBER_STREAM_INITIALIZING_ENCRYPTION || js->gsc)
+ if (js->state != JABBER_STREAM_INITIALIZING_ENCRYPTION || + G_IS_TLS_CONNECTION(js->stream)) { purple_debug_warning("jabber", "Ignoring spurious %s\n", name);
if (purple_strequal(name, "proceed"))
/* TODO: Handle <failure/>, I guess? */
@@ -378,52 +381,27 @@
-static int jabber_do_send(JabberStream *js, const char *data, int len)
+jabber_push_bytes_cb(GObject *source, GAsyncResult *res, gpointer data)
- 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)
+ PurpleQueuedOutputStream *stream = PURPLE_QUEUED_OUTPUT_STREAM(source); - const gchar *output = NULL;
- writelen = purple_circular_buffer_get_max_read(js->write_buffer);
- output = purple_circular_buffer_get_output(js->write_buffer);
- purple_input_remove(js->writeh);
+ 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);
- ret = jabber_do_send(js, output, writelen);
- if (ret < 0 && errno == EAGAIN)
- gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
- purple_connection_error(js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- purple_circular_buffer_mark_read(js->write_buffer, ret);
static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
g_return_val_if_fail(len > 0, FALSE);
@@ -431,39 +409,11 @@
if (js->state == JABBER_STREAM_CONNECTED)
jabber_stream_restart_inactivity_timer(js);
- ret = jabber_do_send(js, data, len);
- if (ret < 0 && errno != EAGAIN) {
- PurpleAccount *account = purple_connection_get_account(js->gc);
- * The server may have closed the socket (on a stream error), so if
- * we're disconnecting, don't generate (possibly another) error that
- * (for some UIs) would mask the first.
- if (!purple_account_is_disconnecting(account)) {
- gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
- purple_connection_error(js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- } else if (ret < len) {
- js->writeh = purple_input_add(
- js->gsc ? js->gsc->fd : js->fd,
- PURPLE_INPUT_WRITE, jabber_send_cb, js);
- purple_circular_buffer_append(js->write_buffer,
- data + ret, len - ret);
+ 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); @@ -531,8 +481,7 @@
- if (!js->gsc && js->fd<0)
+ g_return_if_fail(js->input != NULL); @@ -647,51 +596,21 @@
-jabber_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc,
- PurpleInputCondition cond)
+jabber_recv_cb(GObject *stream, gpointer data) PurpleConnection *gc = data;
JabberStream *js = purple_connection_get_protocol_data(gc);
PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
- while((len = purple_ssl_read(gsc, buf, sizeof(buf) - 1)) > 0) {
- purple_connection_update_last_received(gc);
- purple_debug_misc("jabber", "Recv (ssl)(%d): %s", len, buf);
- jabber_parser_process(js, buf, len);
- jabber_stream_init(js);
- 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);
-jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition)
- 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) {
@@ -720,43 +639,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);
-jabber_login_callback_ssl(gpointer data, PurpleSslConnection *gsc,
- PurpleInputCondition cond)
- PurpleConnection *gc = data;
- PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
- js = purple_connection_get_protocol_data(gc);
- if(js->state == JABBER_STREAM_CONNECTING)
- jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
- jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING);
- purple_ssl_input_add(gsc, jabber_recv_cb_ssl, gc);
- /* Tell the app that we're doing encryption */
- jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION);
@@ -823,130 +719,184 @@
-jabber_login_callback(gpointer data, gint source, const gchar *error)
+jabber_stream_connect_finish(JabberStream *js, GIOStream *stream) + 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); + 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); +jabber_login_callback(GObject *source_object, GAsyncResult *res, gpointer data) - PurpleConnection *gc = data;
- JabberStream *js = purple_connection_get_protocol_data(gc);
- GResolver *resolver = g_resolver_get_default();
- gchar *name = g_strdup_printf("_xmppconnect.%s", js->user->domain);
- purple_debug_info("jabber", "Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
- g_resolver_lookup_records_async(resolver,
+ GSocketClient *client = G_SOCKET_CLIENT(source_object); + JabberStream *js = data; + GSocketConnection *conn; + gboolean is_old_ssl = g_socket_client_get_tls(client); + conn = g_socket_client_connect_to_host_finish(client, res, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + } else if (is_old_ssl) { + /* Old-style SSL only makes a direct connection, or fails. */ + purple_connection_take_error(js->gc, error); + name = g_strdup_printf("_xmppconnect.%s", js->user->domain); + purple_debug_info("jabber", + "Couldn't connect directly to %s. Trying to find " + "alternative connection methods, like BOSH.\n", + resolver = g_resolver_get_default(); + g_resolver_lookup_records_async(resolver, name, G_RESOLVER_RECORD_TXT, + js->cancellable, txt_resolved_cb, js); g_object_unref(resolver);
- 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, gc);
+ stream = G_IO_STREAM(g_tcp_wrapper_connection_get_base_io_stream( + G_TCP_WRAPPER_CONNECTION(conn))); + stream = G_IO_STREAM(conn); + jabber_stream_connect_finish(js, stream); + /* Tell the app that we're doing encryption */ + jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); -jabber_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
+tls_handshake_cb(GObject *source_object, GAsyncResult *res, gpointer data) - PurpleConnection *gc = data;
- PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
- js = purple_connection_get_protocol_data(gc);
- purple_connection_ssl_error (gc, error);
+ JabberStream *js = data; + if (!g_tls_connection_handshake_finish(G_TLS_CONNECTION(source_object), res, + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + /* Connection already closed/freed. Escape. */ + } else if (g_error_matches(error, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE)) { + /* In Gio, a handshake error is because of the cert */ + purple_connection_ssl_error(js->gc, PURPLE_SSL_CERTIFICATE_INVALID); + /* Report any other errors as handshake failing */ + purple_connection_ssl_error(js->gc, PURPLE_SSL_HANDSHAKE_FAILED); + jabber_stream_connect_finish(js, js->stream); + /* Tell the app that we're doing encryption */ + jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); static void tls_init(JabberStream *js)
- purple_input_remove(js->inpa);
+ GSocketConnectable *identity; + g_source_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 */
-static gboolean jabber_login_connect(JabberStream *js, const char *domain, const char *host, int port,
- gboolean fatal_failure)
- /* host should be used in preference to domain to
- * allow SASL authentication to work with FQDN of the server,
- * but we use domain as fallback for when users enter IP address
- g_free(js->serverFQDN);
- if (g_hostname_is_ip_address(host)) {
- js->serverFQDN = g_strdup(domain);
- js->serverFQDN = g_strdup(host);
+ g_filter_output_stream_set_close_base_stream( + G_FILTER_OUTPUT_STREAM(js->output), FALSE); + g_output_stream_close(G_OUTPUT_STREAM(js->output), js->cancellable, NULL); + identity = g_network_address_new(js->certificate_CN, 0); + tls_conn = g_tls_client_connection_new(js->stream, identity, &error); + g_object_unref(identity); + if (tls_conn == NULL) { + purple_debug_warning("jabber", + "Error creating TLS client connection: %s", + purple_connection_ssl_error(js->gc, PURPLE_SSL_CONNECT_FAILED); - if (purple_proxy_connect(js->gc, purple_connection_get_account(js->gc),
- host, port, jabber_login_callback, js->gc) == NULL) {
- purple_connection_error(js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to connect"));
+ g_clear_object(&js->stream); + js->stream = G_IO_STREAM(tls_conn); + g_tls_connection_handshake_async(G_TLS_CONNECTION(tls_conn), + G_PRIORITY_DEFAULT, js->cancellable, -srv_resolved_cb(GObject *sender, GAsyncResult *result, gpointer data)
+srv_resolved_cb(GObject *source_object, GAsyncResult *result, gpointer data)
- GList *targets = NULL, *l = NULL;
+ GSocketClient *client = G_SOCKET_CLIENT(source_object);
- targets = g_resolver_lookup_service_finish(G_RESOLVER(sender),
- purple_debug_warning("jabber",
- "SRV lookup failed, proceeding with normal connection : %s",
+ GSocketConnection *conn; + conn = g_socket_client_connect_to_service_finish(client, result, &error); + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + /* Do nothing; cancelled. */ + } else if (g_error_matches(error, G_RESOLVER_ERROR, + G_RESOLVER_ERROR_NOT_FOUND)) { + /* If there was no response, then attempt fallback behaviour of XMPP + "SRV lookup failed, proceeding with normal connection : %s", + g_socket_client_connect_to_host_async( + js->client, js->user->domain, + purple_account_get_int( + purple_connection_get_account(js->gc), "port", + js->cancellable, jabber_login_callback, js); + /* If resolving failed or connecting failed, then just error out, as + * in XMPP Core 3.2.1 step 8. */ + purple_connection_g_error(js->gc, error);
- jabber_login_connect(js, js->user->domain, js->user->domain,
- purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222),
- for(l = targets; l; l = l->next) {
- GSrvTarget *target = (GSrvTarget *)l->data;
- const gchar *hostname = g_srv_target_get_hostname(target);
- guint port = g_srv_target_get_port(target);
- if(jabber_login_connect(js, hostname, hostname, port, FALSE)) {
- g_resolver_free_targets(targets);
- g_resolver_free_targets(targets);
- jabber_login_connect(js, js->user->domain, js->user->domain,
- purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222),
+ jabber_stream_connect_finish(js, G_IO_STREAM(conn)); @@ -971,7 +921,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);
@@ -1039,7 +988,6 @@
js->chats = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, (GDestroyNotify)jabber_chat_free);
js->next_id = g_random_int();
- js->write_buffer = purple_circular_buffer_new(512);
js->keepalive_timeout = 0;
js->max_inactivity = DEFAULT_INACTIVITY_TIME;
@@ -1071,6 +1019,7 @@
const char *bosh_url = purple_account_get_string(account,
jabber_stream_set_state(js, JABBER_STREAM_CONNECTING);
@@ -1088,37 +1037,35 @@
+ js->client = purple_gio_socket_client_new(account, &error); + if (js->client == NULL) { + purple_connection_take_error(gc, error); js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain);
/* if they've got old-ssl mode going, we probably want to ignore SRV lookups */
if (purple_strequal("old_ssl", purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS))) {
- js->gsc = purple_ssl_connect(account, js->certificate_CN,
- purple_account_get_int(account, "port", 5223),
- jabber_login_callback_ssl, jabber_ssl_connect_failure, gc);
- purple_connection_error(gc,
- PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
- _("Unable to establish SSL connection"));
+ g_socket_client_set_tls(js->client, TRUE); + g_socket_client_connect_to_host_async( + js->client, js->certificate_CN, + purple_account_get_int(account, "port", 5223), js->cancellable, + jabber_login_callback, js); /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll
* invoke the magic of SRV lookups, to figure out host and port */
- jabber_login_connect(js, js->user->domain, connect_server,
- purple_account_get_int(account, "port", 5222), TRUE);
+ g_socket_client_connect_to_host_async( + js->client, connect_server, + purple_account_get_int(account, "port", 5222), js->cancellable, + jabber_login_callback, js); - GResolver *resolver = g_resolver_get_default();
- g_resolver_lookup_service_async(resolver,
- g_object_unref(resolver);
+ g_socket_client_connect_to_service_async(js->client, js->user->domain, + "xmpp-client", js->cancellable, @@ -1649,19 +1596,21 @@
jabber_bosh_connection_destroy(js->bosh);
- } else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0)
+ } else if (js->output != NULL) { jabber_send_raw(js, "</stream:stream>", -1);
- purple_ssl_close(js->gsc);
- } else if (js->fd > 0) {
- 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);
@@ -1697,10 +1646,6 @@
- g_object_unref(G_OBJECT(js->write_buffer));
- purple_input_remove(js->writeh);
if (js->auth_mech && js->auth_mech->dispose)
js->auth_mech->dispose(js);
@@ -1760,7 +1705,11 @@
void jabber_stream_set_state(JabberStream *js, JabberStreamState state)
-#define JABBER_CONNECT_STEPS ((js->gsc || js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION) ? 9 : 5)
+#define JABBER_CONNECT_STEPS \ + ((G_IS_TLS_CONNECTION(js->stream) || \ + js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION) \ @@ -1771,8 +1720,10 @@
case JABBER_STREAM_INITIALIZING:
- purple_connection_update_progress(js->gc, _("Initializing Stream"),
- js->gsc ? 5 : 2, JABBER_CONNECT_STEPS);
+ purple_connection_update_progress( + js->gc, _("Initializing Stream"), + G_IS_TLS_CONNECTION(js->stream) ? 5 : 2, case JABBER_STREAM_INITIALIZING_ENCRYPTION:
@@ -1780,12 +1731,16 @@
6, JABBER_CONNECT_STEPS);
case JABBER_STREAM_AUTHENTICATING:
- purple_connection_update_progress(js->gc, _("Authenticating"),
- js->gsc ? 7 : 3, JABBER_CONNECT_STEPS);
+ purple_connection_update_progress( + js->gc, _("Authenticating"), + G_IS_TLS_CONNECTION(js->stream) ? 7 : 3, case JABBER_STREAM_POST_AUTH:
- purple_connection_update_progress(js->gc, _("Re-initializing Stream"),
- (js->gsc ? 8 : 4), JABBER_CONNECT_STEPS);
+ purple_connection_update_progress( + js->gc, _("Re-initializing Stream"), + (G_IS_TLS_CONNECTION(js->stream) ? 8 : 4), case JABBER_STREAM_CONNECTED:
@@ -2109,7 +2064,7 @@
gboolean jabber_stream_is_ssl(JabberStream *js)
return (js->bosh && jabber_bosh_connection_is_ssl(js->bosh)) ||
- (!js->bosh && js->gsc);
+ (!js->bosh && G_IS_TLS_CONNECTION(js->stream)); --- a/libpurple/protocols/jabber/si.c Sun Nov 03 09:22:21 2019 +0000
+++ b/libpurple/protocols/jabber/si.c Mon Nov 04 01:18:24 2019 +0000
@@ -429,7 +429,7 @@
char buffer[42]; /* 40 for DST.ADDR + 2 bytes for port number*/
purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n");
@@ -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);
@@ -527,6 +528,8 @@
jabber_si_xfer_bytestreams_send_read_again_resp_cb, xfer));
jabber_si_xfer_bytestreams_send_read_again_resp_cb(xfer, source,
@@ -857,7 +860,7 @@
purple_network_get_all_local_system_ips();
gboolean has_public_ip = FALSE;
jsx->local_streamhost_fd = sock;
@@ -867,7 +870,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) */
@@ -893,6 +897,7 @@
/* The listener for the local proxy */
purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ,