# HG changeset patch
# Node ID 0cbfd6c63e62
# Parent  6707bf9fe6cb
diff --git a/libpurple/protocols/jabber/bosh.c b/libpurple/protocols/jabber/bosh.c
--- a/libpurple/protocols/jabber/bosh.c
+++ b/libpurple/protocols/jabber/bosh.c
@@ -182,13 +182,16 @@ jabber_bosh_connection_is_ssl(const Purp
 
 static PurpleXmlNode *
 jabber_bosh_connection_parse(PurpleJabberBOSHConnection *conn,
-                             SoupMessage *response)
+                             SoupMessage *msg, GBytes *response_body,
+                             GError *error)
 {
+	gconstpointer body = NULL;
+	gsize length = 0;
 	PurpleXmlNode *root;
 	const gchar *type;
 
 	g_return_val_if_fail(conn != NULL, NULL);
-	g_return_val_if_fail(response != NULL, NULL);
+	g_return_val_if_fail(msg != NULL, NULL);
 
 	if (conn->is_terminating || purple_account_is_disconnecting(
 		purple_connection_get_account(conn->js->gc)))
@@ -196,17 +199,19 @@ jabber_bosh_connection_parse(PurpleJabbe
 		return NULL;
 	}
 
-	if (!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(response))) {
-		gchar *tmp = g_strdup_printf(_("Unable to connect: %s"),
-		                             soup_message_get_reason_phrase(response));
+	if(error != NULL ||
+	   !SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(msg))) {
+		gchar *tmp = g_strdup_printf(
+		    _("Unable to connect: %s"),
+		    error ? error->message : soup_message_get_reason_phrase(msg));
 		purple_connection_error(conn->js->gc,
 		                        PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
 		g_free(tmp);
 		return NULL;
 	}
 
-	root = purple_xmlnode_from_str(response->response_body->data,
-	                               response->response_body->length);
+	body = g_bytes_get_data(response_body, &length);
+	root = purple_xmlnode_from_str(body, length);
 
 	type = purple_xmlnode_get_attrib(root, "type");
 	if (purple_strequal(type, "terminate")) {
@@ -221,20 +226,36 @@ jabber_bosh_connection_parse(PurpleJabbe
 }
 
 static void
-jabber_bosh_connection_recv(SoupSession *session, SoupMessage *msg,
-                            gpointer user_data)
+jabber_bosh_connection_recv(GObject *source, GAsyncResult *result,
+                            gpointer data)
 {
-	PurpleJabberBOSHConnection *bosh_conn = user_data;
+	SoupMessage *msg = data;
+	PurpleJabberBOSHConnection *bosh_conn = NULL;
+	GBytes *response_body = NULL;
+	GError *error = NULL;
 	PurpleXmlNode *node, *child;
 
-	if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
-		purple_debug_misc("jabber-bosh", "received: %s\n",
-		                  msg->response_body->data);
+	response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
+	                                                  result, &error);
+	bosh_conn = g_object_get_data(G_OBJECT(msg), "bosh-connection");
+
+	if (response_body != NULL && purple_debug_is_verbose() &&
+	    purple_debug_is_unsafe())
+	{
+		const gchar *body = NULL;
+		gsize length = 0;
+
+		body = g_bytes_get_data(response_body, &length);
+		purple_debug_misc("jabber-bosh", "received: %*s", (int)length, body);
 	}
 
-	node = jabber_bosh_connection_parse(bosh_conn, msg);
-	if (node == NULL)
+	node = jabber_bosh_connection_parse(bosh_conn, msg, response_body, error);
+	g_clear_pointer(&response_body, g_bytes_unref);
+	g_clear_error(&error);
+
+	if (node == NULL) {
 		return;
+	}
 
 	child = node->child;
 	while (child != NULL) {
@@ -313,12 +334,19 @@ jabber_bosh_connection_send_now(PurpleJa
 	g_string_free(data, FALSE);
 
 	if (conn->is_terminating) {
+#if SOUP_MAJOR_VERSION >= 3
+		soup_session_send_async(conn->payload_reqs, req, G_PRIORITY_DEFAULT,
+		                        NULL, NULL, NULL);
+#else
 		soup_session_send_async(conn->payload_reqs, req, NULL, NULL, NULL);
+#endif
 		g_free(conn->sid);
 		conn->sid = NULL;
 	} else {
-		soup_session_queue_message(conn->payload_reqs, req,
-		                           jabber_bosh_connection_recv, conn);
+		g_object_set_data(G_OBJECT(req), "bosh-connection", conn);
+		soup_session_send_and_read_async(conn->payload_reqs, req,
+		                                 G_PRIORITY_DEFAULT, NULL,
+		                                 jabber_bosh_connection_recv, req);
 	}
 }
 
@@ -379,22 +407,39 @@ jabber_bosh_version_check(const gchar *v
 }
 
 static void
-jabber_bosh_connection_session_created(SoupSession *session, SoupMessage *msg,
-                                       gpointer user_data)
+jabber_bosh_connection_session_created(GObject *source, GAsyncResult *result,
+                                       gpointer data)
 {
-	PurpleJabberBOSHConnection *bosh_conn = user_data;
+	SoupMessage *msg = data;
+	PurpleJabberBOSHConnection *bosh_conn = NULL;
+	GBytes *response_body = NULL;
+	GError *error = NULL;
 	PurpleXmlNode *node, *features;
 	const gchar *sid, *ver, *inactivity_str;
 	int inactivity = 0;
 
-	if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
-		purple_debug_misc("jabber-bosh", "received (session creation): %s\n",
-		                  msg->response_body->data);
+	response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
+	                                                  result, &error);
+	bosh_conn = g_object_get_data(G_OBJECT(msg), "bosh-connection");
+
+	if (response_body != NULL && purple_debug_is_verbose() &&
+	    purple_debug_is_unsafe())
+	{
+		const gchar *body = NULL;
+		gsize length = 0;
+
+		body = g_bytes_get_data(response_body, &length);
+		purple_debug_misc("jabber-bosh", "received (session creation): %*s",
+		                  (int)length, body);
 	}
 
-	node = jabber_bosh_connection_parse(bosh_conn, msg);
-	if (node == NULL)
+	node = jabber_bosh_connection_parse(bosh_conn, msg, response_body, error);
+	g_clear_pointer(&response_body, g_bytes_unref);
+	g_clear_error(&error);
+
+	if (node == NULL) {
 		return;
+	}
 
 	sid = purple_xmlnode_get_attrib(node, "sid");
 	ver = purple_xmlnode_get_attrib(node, "ver");
@@ -484,8 +529,11 @@ jabber_bosh_connection_session_create(Pu
 	req = jabber_bosh_connection_http_request_new(conn, data);
 	g_string_free(data, FALSE);
 
-	soup_session_queue_message(conn->payload_reqs, req,
-	                           jabber_bosh_connection_session_created, conn);
+	g_object_set_data(G_OBJECT(req), "bosh-connection", conn);
+	soup_session_send_and_read_async(conn->payload_reqs, req,
+	                                 G_PRIORITY_DEFAULT, NULL,
+	                                 jabber_bosh_connection_session_created,
+	                                 req);
 }
 
 static SoupMessage *
diff --git a/libpurple/protocols/jabber/oob.c b/libpurple/protocols/jabber/oob.c
--- a/libpurple/protocols/jabber/oob.c
+++ b/libpurple/protocols/jabber/oob.c
@@ -28,10 +28,14 @@
 #include "iq.h"
 #include "oob.h"
 
+/* Number of bytes to read asynchronously per chunk. */
+#define JABBER_OOB_READ_SIZE (1024*1024)
+
 struct _JabberOOBXfer {
 	JabberStream *js;
 	gchar *iq_id;
 	gchar *url;
+	GCancellable *cancellable;
 	SoupMessage *msg;
 };
 
@@ -55,6 +59,100 @@ static void jabber_oob_xfer_end(PurpleXf
 }
 
 static void
+jabber_oob_xfer_got_content_length(SoupMessage *msg, gpointer user_data)
+{
+	PurpleXfer *xfer = user_data;
+	SoupMessageHeaders *headers;
+	goffset total;
+
+	headers = soup_message_get_response_headers(msg);
+	total = soup_message_headers_get_content_length(headers);
+
+	purple_xfer_set_size(xfer, total);
+}
+
+#if SOUP_MAJOR_VERSION >= 3
+static void
+jabber_oob_xfer_writer(GObject *source, GAsyncResult *result, gpointer data) {
+	GInputStream *input = G_INPUT_STREAM(source);
+	PurpleXfer *xfer = data;
+	JabberOOBXfer *jox = JABBER_OOB_XFER(xfer);
+	GBytes *bytes = NULL;
+	GError *error = NULL;
+	gpointer buffer = NULL;
+	gsize size = 0;
+
+	bytes = g_input_stream_read_bytes_finish(input, result, &error);
+	if(bytes == NULL || error != NULL) {
+		purple_debug_error("jabber", "Error reading OOB data: %s",
+		                   error ? error->message : "unknown error");
+		purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
+		purple_xfer_end(xfer);
+		g_clear_pointer(&bytes, g_bytes_unref);
+		g_clear_object(&input);
+		jox->msg = NULL;
+		return;
+	}
+
+	buffer = g_bytes_unref_to_data(bytes, &size);
+
+	/* We've reached the end of the file. */
+	if(size == 0) {
+		purple_xfer_set_completed(xfer, TRUE);
+		purple_xfer_end(xfer);
+		g_clear_object(&input);
+		jox->msg = NULL;
+		return;
+	}
+
+	if (!purple_xfer_write_file(xfer, buffer, size)) {
+		purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
+		purple_xfer_end(xfer);
+		g_clear_object(&input);
+		jox->msg = NULL;
+		return;
+	}
+
+	/* Asynchronously read the next chunk of data. */
+	g_input_stream_read_bytes_async(input, JABBER_OOB_READ_SIZE,
+	                                G_PRIORITY_DEFAULT, jox->cancellable,
+	                                jabber_oob_xfer_writer, xfer);
+}
+
+static void
+jabber_oob_xfer_send_cb(GObject *source, GAsyncResult *result, gpointer data) {
+	PurpleXfer *xfer = data;
+	JabberOOBXfer *jox = JABBER_OOB_XFER(xfer);
+	GInputStream *input = NULL;
+	GError *error = NULL;
+
+	if(!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(jox->msg))) {
+		purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
+		purple_xfer_end(xfer);
+		jox->msg = NULL;
+		return;
+	}
+
+	input = soup_session_send_finish(SOUP_SESSION(source), result, &error);
+	if(input == NULL || error != NULL) {
+		purple_debug_error("jabber", "Error sending OOB request: %s",
+		                   error ? error->message : "unknown error");
+		purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
+		purple_xfer_end(xfer);
+		g_clear_object(&input);
+		jox->msg = NULL;
+		return;
+	}
+
+	/* Asynchronously read an initial chunk of data. */
+	g_input_stream_read_bytes_async(input, JABBER_OOB_READ_SIZE,
+	                                G_PRIORITY_DEFAULT, jox->cancellable,
+	                                jabber_oob_xfer_writer, xfer);
+}
+
+#else /* SOUP_MAJOR_VERSION < 3 */
+
+static void
 jabber_oob_xfer_got(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg,
                     gpointer user_data)
 {
@@ -78,19 +176,6 @@ jabber_oob_xfer_got(G_GNUC_UNUSED SoupSe
 }
 
 static void
-jabber_oob_xfer_got_content_length(SoupMessage *msg, gpointer user_data)
-{
-	PurpleXfer *xfer = user_data;
-	SoupMessageHeaders *headers;
-	goffset total;
-
-	headers = soup_message_get_response_headers(msg);
-	total = soup_message_headers_get_content_length(headers);
-
-	purple_xfer_set_size(xfer, total);
-}
-
-static void
 jabber_oob_xfer_writer(SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
 {
 	PurpleXfer *xfer = user_data;
@@ -102,6 +187,7 @@ jabber_oob_xfer_writer(SoupMessage *msg,
 		                            SOUP_STATUS_IO_ERROR);
 	}
 }
+#endif
 
 static void jabber_oob_xfer_start(PurpleXfer *xfer)
 {
@@ -112,11 +198,16 @@ static void jabber_oob_xfer_start(Purple
 	soup_message_add_header_handler(
 	        msg, "got-headers", "Content-Length",
 	        G_CALLBACK(jabber_oob_xfer_got_content_length), xfer);
+#if SOUP_MAJOR_VERSION >= 3
+	soup_session_send_async(jox->js->http_conns, msg, G_PRIORITY_DEFAULT,
+	                        jox->cancellable, jabber_oob_xfer_send_cb, xfer);
+#else
 	soup_message_body_set_accumulate(msg->response_body, FALSE);
 	g_signal_connect(msg, "got-chunk", G_CALLBACK(jabber_oob_xfer_writer),
 	                 xfer);
 	soup_session_queue_message(jox->js->http_conns, msg, jabber_oob_xfer_got,
 	                           xfer);
+#endif
 }
 
 static void jabber_oob_xfer_recv_error(PurpleXfer *xfer, const char *code) {
@@ -148,8 +239,13 @@ static void jabber_oob_xfer_recv_denied(
 static void jabber_oob_xfer_recv_cancelled(PurpleXfer *xfer) {
 	JabberOOBXfer *jox = JABBER_OOB_XFER(xfer);
 
+#if SOUP_MAJOR_VERSION >= 3
+	g_cancellable_cancel(jox->cancellable);
+#else
 	soup_session_cancel_message(jox->js->http_conns, jox->msg,
 	                            SOUP_STATUS_CANCELLED);
+#endif
+
 	jabber_oob_xfer_recv_error(xfer, "404");
 }
 
@@ -201,7 +297,9 @@ void jabber_oob_parse(JabberStream *js, 
 
 static void
 jabber_oob_xfer_init(JabberOOBXfer *xfer) {
-
+#if SOUP_MAJOR_VERSION >= 3
+	xfer->cancellable = g_cancellable_new();
+#endif
 }
 
 static void
@@ -213,6 +311,10 @@ jabber_oob_xfer_finalize(GObject *obj) {
 
 	g_free(jox->iq_id);
 	g_free(jox->url);
+	if(jox->cancellable != NULL) {
+		g_cancellable_cancel(jox->cancellable);
+	}
+	g_clear_object(&jox->cancellable);
 
 	G_OBJECT_CLASS(jabber_oob_xfer_parent_class)->finalize(obj);
 }
diff --git a/libpurple/protocols/jabber/useravatar.c b/libpurple/protocols/jabber/useravatar.c
--- a/libpurple/protocols/jabber/useravatar.c
+++ b/libpurple/protocols/jabber/useravatar.c
@@ -209,31 +209,43 @@ typedef struct {
 	JabberStream *js;
 	char *from;
 	char *id;
+	SoupMessage *msg;
 } JabberBuddyAvatarUpdateURLInfo;
 
 static void
-do_buddy_avatar_update_fromurl(G_GNUC_UNUSED SoupSession *session,
-                               SoupMessage *msg, gpointer data)
+do_buddy_avatar_update_fromurl(GObject *source, GAsyncResult *result,
+                               gpointer data)
 {
 	JabberBuddyAvatarUpdateURLInfo *info = data;
-	gpointer icon_data;
+	GBytes *response_body = NULL;
+	gpointer icon_data = NULL;
+	gsize length = 0;
+	GError *error = NULL;
+	const gchar *error_message = NULL;
 
-	if (!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(msg))) {
+	if(SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(info->msg))) {
+		response_body = soup_session_send_and_read_finish(SOUP_SESSION(source),
+		                                                  result, &error);
+		error_message = error ? error->message : "unknown";
+	} else {
+		error_message = soup_message_get_reason_phrase(info->msg);
+	}
+	if(response_body == NULL) {
 		purple_debug_error("jabber",
 		                   "do_buddy_avatar_update_fromurl got error \"%s\"",
-		                   soup_message_get_reason_phrase(msg));
+		                   error_message);
 		goto out;
 	}
 
-	icon_data = g_memdup2(msg->response_body->data, msg->response_body->length);
+	icon_data = g_bytes_unref_to_data(response_body, &length);
 	purple_buddy_icons_set_for_user(purple_connection_get_account(info->js->gc),
-	                                info->from, icon_data,
-	                                msg->response_body->length, info->id);
+	                                info->from, icon_data, length, info->id);
 
 out:
 	g_free(info->from);
 	g_free(info->id);
 	g_free(info);
+	g_clear_error(&error);
 }
 
 static void
@@ -337,10 +349,11 @@ update_buddy_metadata(JabberStream *js, 
 				info->from = g_strdup(from);
 				info->id = g_strdup(id);
 
-				msg = soup_message_new("GET", url);
-				soup_session_queue_message(js->http_conns, msg,
-				                           do_buddy_avatar_update_fromurl,
-				                           info);
+				info->msg = msg = soup_message_new("GET", url);
+				soup_session_send_and_read_async(js->http_conns, msg,
+				                                 G_PRIORITY_DEFAULT, NULL,
+				                                 do_buddy_avatar_update_fromurl,
+				                                 info);
 			}
 		}
 	}
