libpurple/protocols/jabber/bosh.c

changeset 34268
cf61366236ee
parent 34219
eee308def583
child 34269
26b4c9a54166
child 34869
7bb3023a9997
--- a/libpurple/protocols/jabber/bosh.c	Fri Aug 02 10:51:42 2013 +0200
+++ b/libpurple/protocols/jabber/bosh.c	Sat Aug 03 00:36:57 2013 +0200
@@ -17,1079 +17,503 @@
  *
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
  *
  */
 #include "internal.h"
-#include "circbuffer.h"
 #include "core.h"
-#include "cipher.h"
 #include "debug.h"
 #include "http.h"
-#include "prpl.h"
-#include "util.h"
-#include "xmlnode.h"
 
 #include "bosh.h"
 
-/* The number of HTTP connections to use. This MUST be at least 2. */
-#define NUM_HTTP_CONNECTIONS      2
-/* How many failed connection attempts before it becomes a fatal error */
-#define MAX_FAILED_CONNECTIONS    3
-/* How long in seconds to queue up outgoing messages */
-#define BUFFER_SEND_IN_SECS       1
-
-typedef struct _PurpleHTTPConnection PurpleHTTPConnection;
-
-typedef void (*PurpleBOSHConnectionConnectFunction)(PurpleBOSHConnection *conn);
-typedef void (*PurpleBOSHConnectionReceiveFunction)(PurpleBOSHConnection *conn, xmlnode *node);
+/*
+TODO: test, what happens, if the http server (BOSH server) doesn't support
+keep-alive (sends connection: close).
+*/
 
-static char *bosh_useragent = NULL;
+#define JABBER_BOSH_SEND_DELAY 250
 
-typedef enum {
-	PACKET_NORMAL,
-	PACKET_TERMINATE,
-	PACKET_FLUSH,
-} PurpleBOSHPacketType;
+#define JABBER_BOSH_TIMEOUT 10
 
-struct _PurpleBOSHConnection {
-	JabberStream *js;
-	PurpleHTTPConnection *connections[NUM_HTTP_CONNECTIONS];
+static gchar *jabber_bosh_useragent = NULL;
 
-	PurpleCircBuffer *pending;
-	PurpleBOSHConnectionConnectFunction connect_cb;
-	PurpleBOSHConnectionReceiveFunction receive_cb;
-
-	/* Must be big enough to hold 2^53 - 1 */
-	char *sid;
-	guint64 rid;
-
-	/* decoded URL */
-	char *host;
-	char *path;
-	guint16 port;
+struct _PurpleJabberBOSHConnection {
+	JabberStream *js;
+	PurpleHttpKeepalivePool *kapool;
+	PurpleHttpConnection *sc_req; /* Session Creation Request */
+	PurpleHttpConnectionSet *payload_reqs;
 
-	gboolean pipelining;
-	gboolean ssl;
+	gchar *url;
+	gboolean is_ssl;
+	gboolean is_terminating;
 
-	enum {
-		BOSH_CONN_OFFLINE,
-		BOSH_CONN_BOOTING,
-		BOSH_CONN_ONLINE
-	} state;
-	guint8 failed_connections;
+	gchar *sid;
+	guint64 rid; /* Must be big enough to hold 2^53 - 1 */
 
-	int wait;
-
-	int max_requests;
-	int requests;
-
+	GString *send_buff;
 	guint send_timer;
 };
 
-struct _PurpleHTTPConnection {
-	PurpleBOSHConnection *bosh;
-	PurpleSslConnection *psc;
-
-	PurpleCircBuffer *write_buf;
-	GString *read_buf;
-
-	gsize handled_len;
-	gsize body_len;
-
-	int fd;
-	guint readh;
-	guint writeh;
-
-	enum {
-		HTTP_CONN_OFFLINE,
-		HTTP_CONN_CONNECTING,
-		HTTP_CONN_CONNECTED
-	} state;
-	int requests; /* number of outstanding HTTP requests */
-
-	gboolean headers_done;
-	gboolean close;
-};
+static PurpleHttpRequest *
+jabber_bosh_connection_http_request_new(PurpleJabberBOSHConnection *conn,
+	const GString *data);
+static void
+jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn);
+static void
+jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn);
 
-static void
-debug_dump_http_connections(PurpleBOSHConnection *conn)
-{
-	int i;
-
-	g_return_if_fail(conn != NULL);
-
-	for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
-		PurpleHTTPConnection *httpconn = conn->connections[i];
-		if (httpconn == NULL)
-			purple_debug_misc("jabber", "BOSH %p->connections[%d] = (nil)\n",
-			                  conn, i);
-		else
-			purple_debug_misc("jabber", "BOSH %p->connections[%d] = %p, state = %d"
-			                  ", requests = %d\n", conn, i, httpconn,
-			                  httpconn->state, httpconn->requests);
-	}
-}
-
-static void http_connection_connect(PurpleHTTPConnection *conn);
-static void http_connection_send_request(PurpleHTTPConnection *conn,
-                                         const GString *req);
-static gboolean send_timer_cb(gpointer data);
-
-void jabber_bosh_init(void)
+void
+jabber_bosh_init(void)
 {
 	GHashTable *ui_info = purple_core_get_ui_info();
-	const char *ui_name = NULL;
-	const char *ui_version = NULL;
+	const gchar *ui_name = NULL;
+	const gchar *ui_version = NULL;
 
 	if (ui_info) {
 		ui_name = g_hash_table_lookup(ui_info, "name");
 		ui_version = g_hash_table_lookup(ui_info, "version");
 	}
 
-	if (ui_name)
-		bosh_useragent = g_strdup_printf("%s%s%s (libpurple " VERSION ")",
-		                                 ui_name, ui_version ? " " : "",
-		                                 ui_version ? ui_version : "");
-	else
-		bosh_useragent = g_strdup("libpurple " VERSION);
+	if (ui_name) {
+		jabber_bosh_useragent = g_strdup_printf(
+			"%s%s%s (libpurple " VERSION ")", ui_name,
+			ui_version ? " " : "", ui_version ? ui_version : "");
+	} else
+		jabber_bosh_useragent = g_strdup("libpurple " VERSION);
 }
 
 void jabber_bosh_uninit(void)
 {
-	g_free(bosh_useragent);
-	bosh_useragent = NULL;
-}
-
-static PurpleHTTPConnection*
-jabber_bosh_http_connection_init(PurpleBOSHConnection *bosh)
-{
-	PurpleHTTPConnection *conn = g_new0(PurpleHTTPConnection, 1);
-	conn->bosh = bosh;
-	conn->fd = -1;
-	conn->state = HTTP_CONN_OFFLINE;
-
-	conn->write_buf = purple_circ_buffer_new(0 /* default grow size */);
-
-	return conn;
+	g_free(jabber_bosh_useragent);
+	jabber_bosh_useragent = NULL;
 }
 
-static void
-jabber_bosh_http_connection_destroy(PurpleHTTPConnection *conn)
+PurpleJabberBOSHConnection*
+jabber_bosh_connection_new(JabberStream *js, const gchar *url)
 {
-	if (conn->read_buf)
-		g_string_free(conn->read_buf, TRUE);
-
-	if (conn->write_buf)
-		purple_circ_buffer_destroy(conn->write_buf);
-	if (conn->readh)
-		purple_input_remove(conn->readh);
-	if (conn->writeh)
-		purple_input_remove(conn->writeh);
-	if (conn->psc)
-		purple_ssl_close(conn->psc);
-	if (conn->fd >= 0)
-		close(conn->fd);
-
-	purple_proxy_connect_cancel_with_handle(conn);
-
-	g_free(conn);
-}
-
-PurpleBOSHConnection*
-jabber_bosh_connection_init(JabberStream *js, const char *url)
-{
-	PurpleBOSHConnection *conn;
+	PurpleJabberBOSHConnection *conn;
 	PurpleHttpURL *url_p;
 
 	url_p = purple_http_url_parse(url);
 	if (!url_p) {
-		purple_debug_info("jabber", "Unable to parse given URL.\n");
+		purple_debug_error("jabber-bosh", "Unable to parse given URL.\n");
 		return NULL;
 	}
 
-	conn = g_new0(PurpleBOSHConnection, 1);
-	conn->host = g_strdup(purple_http_url_get_host(url_p));
-	conn->port = purple_http_url_get_port(url_p);
-	conn->path = g_strdup(purple_http_url_get_path(url_p));
-	conn->pipelining = TRUE;
+	conn = g_new0(PurpleJabberBOSHConnection, 1);
+	conn->kapool = purple_http_keepalive_pool_new();
+	conn->payload_reqs = purple_http_connection_set_new();
+	purple_http_keepalive_pool_set_limit_per_host(conn->kapool, 2);
+	conn->url = g_strdup(url);
+	conn->js = js;
+	conn->is_ssl = (g_ascii_strcasecmp("https",
+		purple_http_url_get_protocol(url_p)) == 0);
+	conn->send_buff = g_string_new(NULL);
+
+	/*
+	 * Random 64-bit integer masked off by 2^52 - 1.
+	 *
+	 * This should produce a random integer in the range [0, 2^52). It's
+	 * unlikely we'll send enough packets in one session to overflow the
+	 * rid.
+	 */
+	conn->rid = (((guint64)g_random_int() << 32) | g_random_int());
+	conn->rid &= 0xFFFFFFFFFFFFFLL;
 
 	if (purple_ip_address_is_valid(purple_http_url_get_host(url_p)))
 		js->serverFQDN = g_strdup(js->user->domain);
 	else
 		js->serverFQDN = g_strdup(purple_http_url_get_host(url_p));
 
-	if (purple_http_url_get_username(url_p) ||
-		purple_http_url_get_password(url_p))
-	{
-		purple_debug_info("jabber", "Ignoring unexpected username and password "
-		                            "in BOSH URL.\n");
-	}
-
 	purple_http_url_free(url_p);
 
-	conn->js = js;
-
-	/*
-	 * Random 64-bit integer masked off by 2^52 - 1.
-	 *
-	 * This should produce a random integer in the range [0, 2^52). It's
-	 * unlikely we'll send enough packets in one session to overflow the rid.
-	 */
-	conn->rid = ((guint64)g_random_int() << 32) | g_random_int();
-	conn->rid &= 0xFFFFFFFFFFFFFLL;
-
-	conn->pending = purple_circ_buffer_new(0 /* default grow size */);
-
-	conn->state = BOSH_CONN_OFFLINE;
-	if (purple_strcasestr(url, "https://") != NULL)
-		conn->ssl = TRUE;
-	else
-		conn->ssl = FALSE;
-
-	conn->connections[0] = jabber_bosh_http_connection_init(conn);
+	jabber_bosh_connection_session_create(conn);
 
 	return conn;
 }
 
 void
-jabber_bosh_connection_destroy(PurpleBOSHConnection *conn)
+jabber_bosh_connection_destroy(PurpleJabberBOSHConnection *conn)
 {
-	int i;
+	if (conn == NULL || conn->is_terminating)
+		return;
+	conn->is_terminating = TRUE;
 
-	g_free(conn->host);
-	g_free(conn->path);
+	if (conn->sid != NULL) {
+		purple_debug_info("jabber-bosh",
+			"Terminating a session for %p\n", conn);
+		jabber_bosh_connection_send_now(conn);
+	}
+
+	purple_http_connection_set_destroy(conn->payload_reqs);
+	conn->payload_reqs = NULL;
 
 	if (conn->send_timer)
 		purple_timeout_remove(conn->send_timer);
 
-	purple_circ_buffer_destroy(conn->pending);
+	if (conn->sc_req != NULL) {
+		purple_http_conn_cancel(conn->sc_req);
+		conn->sc_req = NULL;
+	}
 
-	for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
-		if (conn->connections[i])
-			jabber_bosh_http_connection_destroy(conn->connections[i]);
-	}
+	purple_http_keepalive_pool_unref(conn->kapool);
+	conn->kapool = NULL;
+	g_string_free(conn->send_buff, TRUE);
+	conn->send_buff = NULL;
+
+	g_free(conn->sid);
+	conn->sid = NULL;
+	g_free(conn->url);
+	conn->url = NULL;
 
 	g_free(conn);
 }
 
-gboolean jabber_bosh_connection_is_ssl(PurpleBOSHConnection *conn)
+gboolean
+jabber_bosh_connection_is_ssl(const PurpleJabberBOSHConnection *conn)
 {
-	return conn->ssl;
+	return conn->is_ssl;
 }
 
-static PurpleHTTPConnection *
-find_available_http_connection(PurpleBOSHConnection *conn)
+static xmlnode *
+jabber_bosh_connection_parse(PurpleJabberBOSHConnection *conn,
+	PurpleHttpResponse *response)
 {
-	int i;
-
-	if (purple_debug_is_verbose())
-		debug_dump_http_connections(conn);
+	xmlnode *root;
+	const gchar *data;
+	size_t data_len;
+	const gchar *type;
 
-	/* Easy solution: Does everyone involved support pipelining? Hooray! Just use
-	 * one TCP connection! */
-	if (conn->pipelining)
-		return conn->connections[0]->state == HTTP_CONN_CONNECTED ?
-				conn->connections[0] : NULL;
+	g_return_val_if_fail(conn != NULL, NULL);
+	g_return_val_if_fail(response != NULL, NULL);
 
-	/* First loop, look for a connection that's ready */
-	for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
-		if (conn->connections[i] &&
-				conn->connections[i]->state == HTTP_CONN_CONNECTED &&
-				conn->connections[i]->requests == 0)
-			return conn->connections[i];
+	if (conn->is_terminating || purple_account_is_disconnecting(
+		purple_connection_get_account(conn->js->gc)))
+	{
+		return NULL;
 	}
 
-	/* Second loop, is something currently connecting? If so, just queue up. */
-	for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
-		if (conn->connections[i] &&
-				conn->connections[i]->state == HTTP_CONN_CONNECTING)
-			return NULL;
+	if (!purple_http_response_is_successfull(response)) {
+		purple_connection_error(conn->js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unable to connect"));
+		return NULL;
 	}
 
-	/* Third loop, is something offline that we can connect? */
-	for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
-		if (conn->connections[i] &&
-				conn->connections[i]->state == HTTP_CONN_OFFLINE) {
-			purple_debug_info("jabber", "bosh: Reconnecting httpconn "
-			                            "(%i, %p)\n", i, conn->connections[i]);
-			http_connection_connect(conn->connections[i]);
-			return NULL;
-		}
+	data = purple_http_response_get_data(response, &data_len);
+	root = xmlnode_from_str(data, data_len);
+
+	type = xmlnode_get_attrib(root, "type");
+	if (g_strcmp0(type, "terminate") == 0) {
+		purple_connection_error(conn->js->gc,
+			PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("The BOSH "
+			"connection manager terminated your session."));
+		xmlnode_free(root);
+		return NULL;
 	}
 
-	/* Fourth loop, look for one that's NULL and create a new connection */
-	for (i = 0; i < NUM_HTTP_CONNECTIONS; ++i) {
-		if (!conn->connections[i]) {
-			conn->connections[i] = jabber_bosh_http_connection_init(conn);
-			purple_debug_info("jabber", "bosh: Creating and connecting new httpconn "
-			                            "(%i, %p)\n", i, conn->connections[i]);
-
-			http_connection_connect(conn->connections[i]);
-			return NULL;
-		}
-	}
-
-	purple_debug_warning("jabber", "Could not find a HTTP connection!\n");
-
-	/* None available. */
-	return NULL;
+	return root;
 }
 
 static void
-jabber_bosh_connection_send(PurpleBOSHConnection *conn,
-                            const PurpleBOSHPacketType type, const char *data)
+jabber_bosh_connection_recv(PurpleHttpConnection *http_conn,
+	PurpleHttpResponse *response, gpointer _bosh_conn)
 {
-	PurpleHTTPConnection *chosen;
-	GString *packet = NULL;
-
-	if (type != PACKET_FLUSH && type != PACKET_TERMINATE) {
-		/*
-		 * Unless this is a flush (or session terminate, which needs to be
-		 * sent immediately), queue up the data and start a timer to flush
-		 * the buffer.
-		 */
-		if (data)
-			purple_circ_buffer_append(conn->pending, data, strlen(data));
+	PurpleJabberBOSHConnection *bosh_conn = _bosh_conn;
+	xmlnode *node, *child;
 
-		if (purple_debug_is_verbose())
-			purple_debug_misc("jabber", "bosh: %p has %" G_GSIZE_FORMAT " bytes in "
-			                  "the buffer.\n", conn, conn->pending->bufused);
-		if (conn->send_timer == 0)
-			conn->send_timer = purple_timeout_add_seconds(BUFFER_SEND_IN_SECS,
-					send_timer_cb, conn);
-		return;
-	}
-
-	chosen = find_available_http_connection(conn);
-
-	if (!chosen) {
-		if (type == PACKET_FLUSH)
-			return;
-		/*
-		 * For non-ordinary traffic, we can't 'buffer' it, so use the
-		 * first connection.
-		 */
-		chosen = conn->connections[0];
-
-		if (chosen->state != HTTP_CONN_CONNECTED) {
-			purple_debug_warning("jabber", "Unable to find a ready BOSH "
-					"connection. Ignoring send of type 0x%02x.\n", type);
-			return;
-		}
-	}
-
-	/* We're flushing the send buffer, so remove the send timer */
-	if (conn->send_timer != 0) {
-		purple_timeout_remove(conn->send_timer);
-		conn->send_timer = 0;
+	if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
+		purple_debug_misc("jabber-bosh", "received: %s\n",
+			purple_http_response_get_data(response, NULL));
 	}
 
-	packet = g_string_new(NULL);
-
-	g_string_printf(packet, "<body "
-	                "rid='%" G_GUINT64_FORMAT "' "
-	                "sid='%s' "
-	                "to='%s' "
-	                "xml:lang='en' "
-	                "xmlns='" NS_BOSH "' "
-	                "xmlns:xmpp='" NS_XMPP_BOSH "'",
-	                ++conn->rid,
-	                conn->sid,
-	                conn->js->user->domain);
-
-	if (conn->js->reinit) {
-		packet = g_string_append(packet, " xmpp:restart='true'/>");
-		/* TODO: Do we need to wait for a response? */
-		conn->js->reinit = FALSE;
-	} else {
-		gsize read_amt;
-		if (type == PACKET_TERMINATE)
-			packet = g_string_append(packet, " type='terminate'");
-
-		packet = g_string_append_c(packet, '>');
-
-		while ((read_amt = purple_circ_buffer_get_max_read(conn->pending)) > 0) {
-			packet = g_string_append_len(packet, conn->pending->outptr, read_amt);
-			purple_circ_buffer_mark_read(conn->pending, read_amt);
-		}
-
-		if (data)
-			packet = g_string_append(packet, data);
-		packet = g_string_append(packet, "</body>");
-	}
-
-	http_connection_send_request(chosen, packet);
-}
-
-void jabber_bosh_connection_close(PurpleBOSHConnection *conn)
-{
-	if (conn->state == BOSH_CONN_ONLINE)
-		jabber_bosh_connection_send(conn, PACKET_TERMINATE, NULL);
-}
-
-static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node) {
-	const char *type;
-
-	type = xmlnode_get_attrib(node, "type");
-
-	if (type != NULL && !strcmp(type, "terminate")) {
-		conn->state = BOSH_CONN_OFFLINE;
-		purple_connection_error(conn->js->gc,
-			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
-			_("The BOSH connection manager terminated your session."));
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
-send_timer_cb(gpointer data)
-{
-	PurpleBOSHConnection *bosh;
-
-	bosh = data;
-	bosh->send_timer = 0;
-
-	jabber_bosh_connection_send(bosh, PACKET_FLUSH, NULL);
-
-	return FALSE;
-}
-
-void
-jabber_bosh_connection_send_keepalive(PurpleBOSHConnection *bosh)
-{
-	if (bosh->send_timer != 0)
-		purple_timeout_remove(bosh->send_timer);
-
-	/* clears bosh->send_timer */
-	send_timer_cb(bosh);
-}
-
-static void
-jabber_bosh_disable_pipelining(PurpleBOSHConnection *bosh)
-{
-	/* Do nothing if it's already disabled */
-	if (!bosh->pipelining)
-		return;
-
-	purple_debug_info("jabber", "BOSH: Disabling pipelining on conn %p\n",
-	                            bosh);
-	bosh->pipelining = FALSE;
-	if (bosh->connections[1] == NULL) {
-		bosh->connections[1] = jabber_bosh_http_connection_init(bosh);
-		http_connection_connect(bosh->connections[1]);
-	} else {
-		/* Shouldn't happen; this should be the only place pipelining
-		 * is turned off.
-		 */
-		g_warn_if_reached();
-	}
-}
-
-static void jabber_bosh_connection_received(PurpleBOSHConnection *conn, xmlnode *node) {
-	xmlnode *child;
-	JabberStream *js = conn->js;
-
-	g_return_if_fail(node != NULL);
-	if (jabber_bosh_connection_error_check(conn, node))
+	node = jabber_bosh_connection_parse(bosh_conn, response);
+	if (node == NULL)
 		return;
 
 	child = node->child;
 	while (child != NULL) {
 		/* jabber_process_packet might free child */
 		xmlnode *next = child->next;
-		if (child->type == XMLNODE_TYPE_TAG) {
-			const char *xmlns = xmlnode_get_namespace(child);
-			/*
-			 * Workaround for non-compliant servers that don't stamp
-			 * the right xmlns on these packets.  See #11315.
-			 */
-			if ((xmlns == NULL /* shouldn't happen, but is equally wrong */ ||
-					g_str_equal(xmlns, NS_BOSH)) &&
-				(g_str_equal(child->name, "iq") ||
-				 g_str_equal(child->name, "message") ||
-				 g_str_equal(child->name, "presence"))) {
-				xmlnode_set_namespace(child, NS_XMPP_CLIENT);
-			}
-			jabber_process_packet(js, &child);
+		const gchar *xmlns;
+
+		if (child->type != XMLNODE_TYPE_TAG) {
+			child = next;
+			continue;
 		}
 
+		/* Workaround for non-compliant servers that don't stamp
+		 * the right xmlns on these packets. See #11315.
+		 */
+		xmlns = xmlnode_get_namespace(child);
+		if ((xmlns == NULL || g_strcmp0(xmlns, NS_BOSH) == 0) &&
+			(g_strcmp0(child->name, "iq") == 0 ||
+			g_strcmp0(child->name, "message") == 0 ||
+			g_strcmp0(child->name, "presence") == 0))
+		{
+			xmlnode_set_namespace(child, NS_XMPP_CLIENT);
+		}
+
+		jabber_process_packet(bosh_conn->js, &child);
+
 		child = next;
 	}
-}
 
-static void boot_response_cb(PurpleBOSHConnection *conn, xmlnode *node) {
-	JabberStream *js = conn->js;
-	const char *sid, *version;
-	const char *inactivity, *requests;
-	xmlnode *packet;
-
-	g_return_if_fail(node != NULL);
-	if (jabber_bosh_connection_error_check(conn, node))
-		return;
-
-	sid = xmlnode_get_attrib(node, "sid");
-	version = xmlnode_get_attrib(node, "ver");
-
-	inactivity = xmlnode_get_attrib(node, "inactivity");
-	requests = xmlnode_get_attrib(node, "requests");
-
-	if (sid) {
-		conn->sid = g_strdup(sid);
-	} else {
-		purple_connection_error(js->gc,
-		        PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-		        _("No session ID given"));
-		return;
-	}
-
-	if (version) {
-		const char *dot = strchr(version, '.');
-		int major, minor = 0;
-
-		purple_debug_info("jabber", "BOSH connection manager version %s\n", version);
-
-		major = atoi(version);
-		if (dot)
-			minor = atoi(dot + 1);
-
-		if (major != 1 || minor < 6) {
-			purple_connection_error(js->gc,
-			        PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			        _("Unsupported version of BOSH protocol"));
-			return;
-		}
-	} else {
-		purple_debug_info("jabber", "Missing version in BOSH initiation\n");
-	}
-
-	if (inactivity) {
-		js->max_inactivity = atoi(inactivity);
-		if (js->max_inactivity <= 5) {
-			purple_debug_warning("jabber", "Ignoring bogusly small inactivity: %s\n",
-			                     inactivity);
-			/* Leave it at the default */
-		} else {
-			/* TODO: Can this check fail? It shouldn't */
-			js->max_inactivity -= 5; /* rounding */
-
-			if (js->inactivity_timer == 0) {
-				purple_debug_misc("jabber", "Starting BOSH inactivity timer "
-						"for %d secs (compensating for rounding)\n",
-						js->max_inactivity);
-				jabber_stream_restart_inactivity_timer(js);
-			}
-		}
-	}
-
-	if (requests)
-		conn->max_requests = atoi(requests);
-
-	jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING);
-
-	/* FIXME: Depending on receiving features might break with some hosts */
-	packet = xmlnode_get_child(node, "features");
-	conn->state = BOSH_CONN_ONLINE;
-	conn->receive_cb = jabber_bosh_connection_received;
-	jabber_stream_features_parse(js, packet);
-}
-
-static void jabber_bosh_connection_boot(PurpleBOSHConnection *conn) {
-	GString *buf = g_string_new(NULL);
-
-	g_string_printf(buf, "<body content='text/xml; charset=utf-8' "
-	                "secure='true' "
-	                "to='%s' "
-	                "xml:lang='en' "
-	                "xmpp:version='1.0' "
-	                "ver='1.6' "
-	                "xmlns:xmpp='" NS_XMPP_BOSH "' "
-	                "rid='%" G_GUINT64_FORMAT "' "
-/* TODO: This should be adjusted/adjustable automatically according to
- * realtime network behavior */
-	                "wait='60' "
-	                "hold='1' "
-	                "xmlns='" NS_BOSH "'/>",
-	                conn->js->user->domain,
-	                ++conn->rid);
-
-	purple_debug_misc("jabber", "SendBOSH Boot %s(%" G_GSIZE_FORMAT "): %s\n",
-	                  conn->ssl ? "(ssl)" : "", buf->len, buf->str);
-	conn->receive_cb = boot_response_cb;
-	http_connection_send_request(conn->connections[0], buf);
-	g_string_free(buf, TRUE);
-}
-
-static void
-http_received_cb(const char *data, int len, PurpleBOSHConnection *conn)
-{
-	if (conn->failed_connections)
-		/* We've got some data, so reset the number of failed connections */
-		conn->failed_connections = 0;
-
-	if (conn->receive_cb) {
-		xmlnode *node = xmlnode_from_str(data, len);
-
-		purple_debug_info("jabber", "RecvBOSH %s(%d): %s\n",
-		                  conn->ssl ? "(ssl)" : "", len, data);
-
-		if (node) {
-			conn->receive_cb(conn, node);
-			xmlnode_free(node);
-		} else {
-			purple_debug_warning("jabber", "BOSH: Received invalid XML\n");
-		}
-	} else {
-		g_return_if_reached();
-	}
-}
-
-void jabber_bosh_connection_send_raw(PurpleBOSHConnection *conn,
-                                     const char *data)
-{
-	jabber_bosh_connection_send(conn, PACKET_NORMAL, data);
-}
-
-static void
-connection_common_established_cb(PurpleHTTPConnection *conn)
-{
-	purple_debug_misc("jabber", "bosh: httpconn %p re-connected\n", conn);
-
-	/* Indicate we're ready and reset some variables */
-	conn->state = HTTP_CONN_CONNECTED;
-	if (conn->requests != 0)
-		purple_debug_error("jabber", "bosh: httpconn %p has %d requests, != 0\n",
-		                   conn, conn->requests);
-
-	conn->requests = 0;
-	if (conn->read_buf) {
-		g_string_free(conn->read_buf, TRUE);
-		conn->read_buf = NULL;
-	}
-	conn->close = FALSE;
-	conn->headers_done = FALSE;
-	conn->handled_len = conn->body_len = 0;
-
-	if (purple_debug_is_verbose())
-		debug_dump_http_connections(conn->bosh);
-
-	if (conn->bosh->js->reinit)
-		jabber_bosh_connection_send(conn->bosh, PACKET_NORMAL, NULL);
-	else if (conn->bosh->state == BOSH_CONN_ONLINE) {
-		purple_debug_info("jabber", "BOSH session already exists. Trying to reuse it.\n");
-		if (conn->bosh->requests == 0 || conn->bosh->pending->bufused > 0) {
-			/* Send the pending data */
-			jabber_bosh_connection_send(conn->bosh, PACKET_FLUSH, NULL);
-		}
-	} else
-		jabber_bosh_connection_boot(conn->bosh);
-}
-
-static void http_connection_disconnected(PurpleHTTPConnection *conn)
-{
-	gboolean had_requests = FALSE;
-	/*
-	 * Well, then. Fine! I never liked you anyway, server! I was cheating on you
-	 * with AIM!
-	 */
-	conn->state = HTTP_CONN_OFFLINE;
-	if (conn->psc) {
-		purple_ssl_close(conn->psc);
-		conn->psc = NULL;
-	} else if (conn->fd >= 0) {
-		close(conn->fd);
-		conn->fd = -1;
-	}
-
-	if (conn->readh) {
-		purple_input_remove(conn->readh);
-		conn->readh = 0;
-	}
-
-	if (conn->writeh) {
-		purple_input_remove(conn->writeh);
-		conn->writeh = 0;
-	}
-
-	had_requests = (conn->requests > 0);
-	if (had_requests && conn->read_buf->len == 0) {
-		purple_debug_error("jabber", "bosh: Adjusting BOSHconn requests (%d) to %d\n",
-		                   conn->bosh->requests, conn->bosh->requests - conn->requests);
-		conn->bosh->requests -= conn->requests;
-		conn->requests = 0;
-	}
-
-	if (conn->bosh->pipelining) {
-		/* Hmmmm, fall back to multiple connections */
-		jabber_bosh_disable_pipelining(conn->bosh);
-	}
-
-	if (!had_requests)
-		/* If the server disconnected us without any requests, let's
-		 * just wait until we have something to send before we reconnect
-		 */
-		return;
-
-	if (++conn->bosh->failed_connections == MAX_FAILED_CONNECTIONS) {
-		purple_connection_error(conn->bosh->js->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Unable to establish a connection with the server"));
-	} else {
-		/* No! Please! Take me back. It was me, not you! I was weak! */
-		http_connection_connect(conn);
-	}
-}
-
-void jabber_bosh_connection_connect(PurpleBOSHConnection *bosh) {
-	PurpleHTTPConnection *conn = bosh->connections[0];
-
-	g_return_if_fail(bosh->state == BOSH_CONN_OFFLINE);
-	bosh->state = BOSH_CONN_BOOTING;
-
-	http_connection_connect(conn);
+	jabber_bosh_connection_send(bosh_conn, NULL);
 }
 
 static void
-jabber_bosh_http_connection_process(PurpleHTTPConnection *conn)
+jabber_bosh_connection_send_now(PurpleJabberBOSHConnection *conn)
 {
-	const char *cursor;
-
-	cursor = conn->read_buf->str + conn->handled_len;
-
-	if (purple_debug_is_verbose())
-		purple_debug_misc("jabber", "BOSH server sent: %s\n", cursor);
-
-	/* TODO: Chunked encoding and check response version :/ */
-	if (!conn->headers_done) {
-		const char *content_length = purple_strcasestr(cursor, "\r\nContent-Length:");
-		const char *connection = purple_strcasestr(cursor, "\r\nConnection:");
-		const char *end_of_headers = strstr(cursor, "\r\n\r\n");
-
-		/* Make sure Content-Length is in headers, not body */
-		if (content_length && (!end_of_headers || content_length < end_of_headers)) {
-			int len;
-
-			if (strstr(content_length, "\r\n") == NULL)
-				/*
-				 * The packet ends in the middle of the Content-Length line.
-				 * We'll try again later when we have more.
-				 */
-				return;
+	PurpleHttpRequest *req;
+	GString *data;
 
-			len = atoi(content_length + strlen("\r\nContent-Length:"));
-			if (len == 0)
-				purple_debug_warning("jabber", "Found mangled Content-Length header, or server returned 0-length response.\n");
-
-			conn->body_len = len;
-		}
-
-		if (connection && (!end_of_headers || content_length < end_of_headers)) {
-			const char *tmp;
-			if (strstr(connection, "\r\n") == NULL)
-				return;
-
+	g_return_if_fail(conn != NULL);
 
-			tmp = connection + strlen("\r\nConnection:");
-			while (*tmp && (*tmp == ' ' || *tmp == '\t'))
-				++tmp;
-
-			if (!g_ascii_strncasecmp(tmp, "close", strlen("close"))) {
-				conn->close = TRUE;
-				jabber_bosh_disable_pipelining(conn->bosh);
-			}
-		}
-
-		if (end_of_headers) {
-			conn->headers_done = TRUE;
-			conn->handled_len = end_of_headers - conn->read_buf->str + 4;
-		} else {
-			conn->handled_len = conn->read_buf->len;
-			return;
-		}
+	if (conn->send_timer != 0) {
+		purple_timeout_remove(conn->send_timer);
+		conn->send_timer = 0;
 	}
 
-	/* Have we handled everything in the buffer? */
-	if (conn->handled_len >= conn->read_buf->len)
-		return;
-
-	/* Have we read all that the Content-Length promised us? */
-	if (conn->read_buf->len - conn->handled_len < conn->body_len)
+	if (conn->sid == NULL)
 		return;
 
-	--conn->requests;
-	--conn->bosh->requests;
+	data = g_string_new(NULL);
 
-	http_received_cb(conn->read_buf->str + conn->handled_len, conn->body_len,
-	                 conn->bosh);
+	/* missing parameters: route, from, ack */
+	g_string_printf(data, "<body "
+		"rid='%" G_GUINT64_FORMAT "' "
+		"sid='%s' "
+		"xmlns='" NS_BOSH "' "
+		"xmlns:xmpp='" NS_XMPP_BOSH "' ",
+		++conn->rid, conn->sid);
 
-	/* Connection: Close? */
-	if (conn->close && conn->state == HTTP_CONN_CONNECTED) {
-		if (purple_debug_is_verbose())
-			purple_debug_misc("jabber", "bosh (%p), server sent Connection: "
-			                            "close\n", conn);
-		http_connection_disconnected(conn);
+	if (conn->js->reinit && !conn->is_terminating) {
+		g_string_append(data, "xmpp:restart='true'/>");
+		conn->js->reinit = FALSE;
+	} else {
+		if (conn->is_terminating)
+			g_string_append(data, "type='terminate' ");
+		g_string_append_c(data, '>');
+		g_string_append_len(data, conn->send_buff->str,
+			conn->send_buff->len);
+		g_string_append(data, "</body>");
+		g_string_set_size(conn->send_buff, 0);
+	}
+
+	if (purple_debug_is_verbose() && purple_debug_is_unsafe())
+		purple_debug_misc("jabber-bosh", "sending: %s\n", data->str);
+
+	req = jabber_bosh_connection_http_request_new(conn, data);
+	g_string_free(data, TRUE);
+
+	if (conn->is_terminating) {
+		purple_http_request(NULL, req, NULL, NULL);
+		g_free(conn->sid);
+		conn->sid = NULL;
+	} else {
+		purple_http_connection_set_add(conn->payload_reqs,
+			purple_http_request(conn->js->gc, req,
+				jabber_bosh_connection_recv, conn));
 	}
 
-	if (conn->bosh->state == BOSH_CONN_ONLINE &&
-			(conn->bosh->requests == 0 || conn->bosh->pending->bufused > 0)) {
-		purple_debug_misc("jabber", "BOSH: Sending an empty request\n");
-		jabber_bosh_connection_send(conn->bosh, PACKET_NORMAL, NULL);
-	}
+	purple_http_request_unref(req);
+}
 
-	g_string_free(conn->read_buf, TRUE);
-	conn->read_buf = NULL;
-	conn->headers_done = FALSE;
-	conn->handled_len = conn->body_len = 0;
+static gboolean
+jabber_bosh_connection_send_delayed(gpointer _conn)
+{
+	PurpleJabberBOSHConnection *conn = _conn;
+
+	conn->send_timer = 0;
+	jabber_bosh_connection_send_now(conn);
+
+	return FALSE;
 }
 
-/*
- * Common code for reading, called from http_connection_read_cb_ssl and
- * http_connection_read_cb.
- */
-static void
-http_connection_read(PurpleHTTPConnection *conn)
+void
+jabber_bosh_connection_send(PurpleJabberBOSHConnection *conn,
+	const gchar *data)
 {
-	char buffer[1025];
-	int cnt, count = 0;
-
-	if (!conn->read_buf)
-		conn->read_buf = g_string_new(NULL);
-
-	do {
-		if (conn->psc)
-			cnt = purple_ssl_read(conn->psc, buffer, sizeof(buffer));
-		else
-			cnt = read(conn->fd, buffer, sizeof(buffer));
+	g_return_if_fail(conn != NULL);
 
-		if (cnt > 0) {
-			count += cnt;
-			g_string_append_len(conn->read_buf, buffer, cnt);
-		}
-	} while (cnt > 0);
+	if (data)
+		g_string_append(conn->send_buff, data);
 
-	if (cnt == 0 || (cnt < 0 && errno != EAGAIN)) {
-		if (cnt < 0)
-			purple_debug_info("jabber", "BOSH (%p) read=%d, errno=%d, error=%s\n",
-			                  conn, cnt, errno, g_strerror(errno));
-		else
-			purple_debug_info("jabber", "BOSH server closed the connection (%p)\n",
-			                  conn);
-
-		/*
-		 * If the socket is closed, the processing really needs to know about
-		 * it. Handle that now.
-		 */
-		http_connection_disconnected(conn);
-
-		/* Process what we do have */
+	if (conn->send_timer == 0) {
+		conn->send_timer = purple_timeout_add(
+			JABBER_BOSH_SEND_DELAY,
+			jabber_bosh_connection_send_delayed, conn);
 	}
-
-	if (conn->read_buf->len > 0)
-		jabber_bosh_http_connection_process(conn);
 }
 
-static void
-http_connection_read_cb(gpointer data, gint fd, PurpleInputCondition condition)
+void
+jabber_bosh_connection_send_keepalive(PurpleJabberBOSHConnection *conn)
 {
-	PurpleHTTPConnection *conn = data;
+	g_return_if_fail(conn != NULL);
 
-	http_connection_read(conn);
+	jabber_bosh_connection_send_now(conn);
 }
 
-static void
-http_connection_read_cb_ssl(gpointer data, PurpleSslConnection *psc,
-                            PurpleInputCondition cond)
+static gboolean
+jabber_bosh_version_check(const gchar *version, int major_req, int minor_min)
 {
-	PurpleHTTPConnection *conn = data;
+	const gchar *dot;
+	int major, minor = 0;
 
-	http_connection_read(conn);
-}
+	if (version == NULL)
+		return FALSE;
 
-static void
-ssl_connection_established_cb(gpointer data, PurpleSslConnection *psc,
-                              PurpleInputCondition cond)
-{
-	PurpleHTTPConnection *conn = data;
+	major = atoi(version);
+	dot = strchr(version, '.');
+	if (dot)
+		minor = atoi(dot + 1);
 
-	purple_ssl_input_add(psc, http_connection_read_cb_ssl, conn);
-	connection_common_established_cb(conn);
+	if (major != major_req)
+		return FALSE;
+	if (minor < minor_min)
+		return FALSE;
+	return TRUE;
 }
 
 static void
-ssl_connection_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error,
-                        gpointer data)
+jabber_bosh_connection_session_created(PurpleHttpConnection *http_conn,
+	PurpleHttpResponse *response, gpointer _bosh_conn)
 {
-	PurpleHTTPConnection *conn = data;
+	PurpleJabberBOSHConnection *bosh_conn = _bosh_conn;
+	xmlnode *node, *features;
+	const gchar *sid, *ver, *inactivity_str;
+	int inactivity = 0;
 
-	/* sslconn frees the connection on error */
-	conn->psc = NULL;
-
-	purple_connection_ssl_error(conn->bosh->js->gc, error);
-}
+	bosh_conn->sc_req = NULL;
 
-static void
-connection_established_cb(gpointer data, gint source, const gchar *error)
-{
-	PurpleHTTPConnection *conn = data;
-	PurpleConnection *gc = conn->bosh->js->gc;
+	if (purple_debug_is_verbose() && purple_debug_is_unsafe()) {
+		purple_debug_misc("jabber-bosh",
+			"received (session creation): %s\n",
+			purple_http_response_get_data(response, NULL));
+	}
+
+	node = jabber_bosh_connection_parse(bosh_conn, response);
+	if (node == NULL)
+		return;
 
-	if (source < 0) {
-		gchar *tmp;
-		tmp = g_strdup_printf(_("Unable to establish a connection with the server: %s"),
-		        error);
-		purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
-		g_free(tmp);
+	sid = xmlnode_get_attrib(node, "sid");
+	ver = xmlnode_get_attrib(node, "ver");
+	inactivity_str = xmlnode_get_attrib(node, "inactivity");
+	/* requests = xmlnode_get_attrib(node, "requests"); */
+
+	if (!sid) {
+		purple_connection_error(bosh_conn->js->gc,
+			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+			_("No BOSH session ID given"));
+		xmlnode_free(node);
 		return;
 	}
 
-	conn->fd = source;
-	conn->readh = purple_input_add(conn->fd, PURPLE_INPUT_READ,
-	        http_connection_read_cb, conn);
-	connection_common_established_cb(conn);
-}
+	if (ver == NULL) {
+		purple_debug_info("jabber-bosh", "Missing version in BOSH initiation\n");
+	} else if (!jabber_bosh_version_check(ver, 1, 6)) {
+		purple_debug_error("jabber-bosh",
+			"Unsupported BOSH version: %s\n", ver);
+		purple_connection_error(bosh_conn->js->gc,
+			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+			_("Unsupported version of BOSH protocol"));
+		xmlnode_free(node);
+		return;
+	}
 
-static void http_connection_connect(PurpleHTTPConnection *conn)
-{
-	PurpleBOSHConnection *bosh = conn->bosh;
-	PurpleConnection *gc = bosh->js->gc;
-	PurpleAccount *account = purple_connection_get_account(gc);
+	purple_debug_misc("jabber-bosh", "Session created for %p\n", bosh_conn);
 
-	conn->state = HTTP_CONN_CONNECTING;
+	bosh_conn->sid = g_strdup(sid);
 
-	if (bosh->ssl) {
-		if (purple_ssl_is_supported()) {
-			conn->psc = purple_ssl_connect(account, bosh->host, bosh->port,
-			                               ssl_connection_established_cb,
-			                               ssl_connection_error_cb,
-			                               conn);
-			if (!conn->psc) {
-				purple_connection_error(gc,
-					PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
-					_("Unable to establish SSL connection"));
-			}
-		} else {
-			purple_connection_error(gc,
-			    PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
-			    _("SSL support unavailable"));
+	if (inactivity_str)
+		inactivity = atoi(inactivity_str);
+	if (inactivity < 0 || inactivity > 3600) {
+		purple_debug_warning("jabber-bosh", "Ignoring invalid "
+			"inactivity value: %s\n", inactivity_str);
+		inactivity = 0;
+	}
+	if (inactivity > 0) {
+		inactivity -= 5; /* rounding */
+		if (inactivity <= 0)
+			inactivity = 1;
+		bosh_conn->js->max_inactivity = inactivity;
+		if (bosh_conn->js->inactivity_timer == 0) {
+			purple_debug_misc("jabber-bosh", "Starting inactivity "
+				"timer for %d secs (compensating for "
+				"rounding)\n", inactivity);
+			jabber_stream_restart_inactivity_timer(bosh_conn->js);
 		}
-	} else if (purple_proxy_connect(conn, account, bosh->host, bosh->port,
-	                                 connection_established_cb, conn) == NULL) {
-		purple_connection_error(gc,
-		    PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-		    _("Unable to connect"));
 	}
-}
+
+	jabber_stream_set_state(bosh_conn->js, JABBER_STREAM_AUTHENTICATING);
 
-static int
-http_connection_do_send(PurpleHTTPConnection *conn, const char *data, int len)
-{
-	int ret;
+	/* FIXME: Depending on receiving features might break with some hosts */
+	features = xmlnode_get_child(node, "features");
+	jabber_stream_features_parse(bosh_conn->js, features);
 
-	if (conn->psc)
-		ret = purple_ssl_write(conn->psc, data, len);
-	else
-		ret = write(conn->fd, data, len);
+	xmlnode_free(node);
 
-	if (purple_debug_is_verbose())
-		purple_debug_misc("jabber", "BOSH (%p): wrote %d bytes\n", conn, ret);
-
-	return ret;
+	jabber_bosh_connection_send(bosh_conn, NULL);
 }
 
 static void
-http_connection_send_cb(gpointer data, gint source, PurpleInputCondition cond)
+jabber_bosh_connection_session_create(PurpleJabberBOSHConnection *conn)
 {
-	PurpleHTTPConnection *conn = data;
-	int ret;
-	int writelen = purple_circ_buffer_get_max_read(conn->write_buf);
+	PurpleHttpRequest *req;
+	GString *data;
 
-	if (writelen == 0) {
-		purple_input_remove(conn->writeh);
-		conn->writeh = 0;
-		return;
-	}
+	g_return_if_fail(conn != NULL);
 
-	ret = http_connection_do_send(conn, conn->write_buf->outptr, writelen);
-
-	if (ret < 0 && errno == EAGAIN)
+	if (conn->sid || conn->sc_req)
 		return;
-	else if (ret <= 0) {
-		/*
-		 * TODO: Handle this better. Probably requires a PurpleBOSHConnection
-		 * buffer that stores what is "being sent" until the
-		 * PurpleHTTPConnection reports it is fully sent.
-		 */
-		gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
-				g_strerror(errno));
-		purple_connection_error(conn->bosh->js->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				tmp);
-		g_free(tmp);
-		return;
-	}
+
+	purple_debug_misc("jabber-bosh", "Requesting Session Create for %p\n",
+		conn);
+
+	data = g_string_new(NULL);
 
-	purple_circ_buffer_mark_read(conn->write_buf, ret);
+	/* missing optional parameters: route, from, ack */
+	g_string_printf(data, "<body content='text/xml; charset=utf-8' "
+		"rid='%" G_GUINT64_FORMAT "' "
+		"to='%s' "
+		"xml:lang='en' "
+		"ver='1.10' "
+		"wait='%d' "
+		"hold='1' "
+		"xmlns='" NS_BOSH "' "
+		"xmpp:version='1.0' "
+		"xmlns:xmpp='urn:xmpp:xbosh' "
+		"/>",
+		++conn->rid, conn->js->user->domain, JABBER_BOSH_TIMEOUT);
+
+	req = jabber_bosh_connection_http_request_new(conn, data);
+	g_string_free(data, TRUE);
+
+	conn->sc_req = purple_http_request(conn->js->gc, req,
+		jabber_bosh_connection_session_created, conn);
+
+	purple_http_request_unref(req);
 }
 
-static void
-http_connection_send_request(PurpleHTTPConnection *conn, const GString *req)
+static PurpleHttpRequest *
+jabber_bosh_connection_http_request_new(PurpleJabberBOSHConnection *conn,
+	const GString *data)
 {
-	char *data;
-	int ret;
-	size_t len;
-
-	/* Sending something to the server, restart the inactivity timer */
-	jabber_stream_restart_inactivity_timer(conn->bosh->js);
+	PurpleHttpRequest *req;
 
-	data = g_strdup_printf("POST %s HTTP/1.1\r\n"
-	                       "Host: %s\r\n"
-	                       "User-Agent: %s\r\n"
-	                       "Content-Encoding: text/xml; charset=utf-8\r\n"
-	                       "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n"
-	                       "%s",
-	                       conn->bosh->path, conn->bosh->host, bosh_useragent,
-	                       req->len, req->str);
-
-	len = strlen(data);
-
-	++conn->requests;
-	++conn->bosh->requests;
-
-	if (purple_debug_is_unsafe() && purple_debug_is_verbose())
-		/* Will contain passwords for SASL PLAIN and is verbose */
-		purple_debug_misc("jabber", "BOSH (%p): Sending %s\n", conn, data);
-	else if (purple_debug_is_verbose())
-		purple_debug_misc("jabber", "BOSH (%p): Sending request of "
-		                            "%" G_GSIZE_FORMAT " bytes.\n", conn, len);
+	jabber_stream_restart_inactivity_timer(conn->js);
 
-	if (conn->writeh == 0)
-		ret = http_connection_do_send(conn, data, len);
-	else {
-		ret = -1;
-		errno = EAGAIN;
-	}
+	req = purple_http_request_new(conn->url);
+	purple_http_request_set_keepalive_pool(req, conn->kapool);
+	purple_http_request_set_method(req, "POST");
+	purple_http_request_set_timeout(req, JABBER_BOSH_TIMEOUT + 2);
+	purple_http_request_header_set(req, "User-Agent",
+		jabber_bosh_useragent);
+	purple_http_request_header_set(req, "Content-Encoding",
+		"text/xml; charset=utf-8");
+	purple_http_request_set_contents(req, data->str, data->len);
 
-	if (ret < 0 && errno != EAGAIN) {
-		/*
-		 * TODO: Handle this better. Probably requires a PurpleBOSHConnection
-		 * buffer that stores what is "being sent" until the
-		 * PurpleHTTPConnection reports it is fully sent.
-		 */
-		gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
-				g_strerror(errno));
-		purple_connection_error(conn->bosh->js->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				tmp);
-		g_free(tmp);
-		return;
-	} else if (ret < len) {
-		if (ret < 0)
-			ret = 0;
-		if (conn->writeh == 0)
-			conn->writeh = purple_input_add(conn->psc ? conn->psc->fd : conn->fd,
-					PURPLE_INPUT_WRITE, http_connection_send_cb, conn);
-		purple_circ_buffer_append(conn->write_buf, data + ret, len - ret);
-	}
+	return req;
 }
-

mercurial