Merged in bonjour-rename-xmpp (pull request #610)

Sat, 26 Oct 2019 16:53:30 +0000

author
Gary Kramlich <grim@reaperworld.com>
date
Sat, 26 Oct 2019 16:53:30 +0000
changeset 40084
fda9b1a584ca
parent 40079
a37a1e349491 (current diff)
parent 40083
11275a766260 (diff)
child 40086
56f7c2805a73

Merged in bonjour-rename-xmpp (pull request #610)

Rename Jabber to XMPP in Bonjour

Approved-by: Elliott Sales de Andrade
Approved-by: Gary Kramlich
Approved-by: Eion Robb

libpurple/protocols/bonjour/jabber.c file | annotate | diff | comparison | revisions
libpurple/protocols/bonjour/jabber.h file | annotate | diff | comparison | revisions
po/POTFILES.in file | annotate | diff | comparison | revisions
--- a/libpurple/protocols/bonjour/bonjour.c	Fri Oct 25 05:52:30 2019 -0400
+++ b/libpurple/protocols/bonjour/bonjour.c	Sat Oct 26 16:53:30 2019 +0000
@@ -35,9 +35,9 @@
 
 #include "bonjour.h"
 #include "mdns_common.h"
-#include "jabber.h"
 #include "buddy.h"
 #include "bonjour_ft.h"
+#include "xmpp.h"
 
 static PurpleProtocol *my_protocol = NULL;
 
@@ -104,12 +104,12 @@
 	bd = g_new0(BonjourData, 1);
 	purple_connection_set_protocol_data(gc, bd);
 
-	/* Start waiting for jabber connections (iChat style) */
-	bd->jabber_data = g_new0(BonjourJabber, 1);
-	bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
-	bd->jabber_data->account = account;
+	/* Start waiting for xmpp connections (iChat style) */
+	bd->xmpp_data = g_new0(BonjourXMPP, 1);
+	bd->xmpp_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
+	bd->xmpp_data->account = account;
 
-	if (bonjour_jabber_start(bd->jabber_data) == -1) {
+	if (bonjour_xmpp_start(bd->xmpp_data) == -1) {
 		/* Send a message about the connection error */
 		purple_connection_error (gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
@@ -121,7 +121,7 @@
 	bd->dns_sd_data = bonjour_dns_sd_new();
 	bd->dns_sd_data->first = g_strdup(purple_account_get_string(account, "first", default_firstname));
 	bd->dns_sd_data->last = g_strdup(purple_account_get_string(account, "last", default_lastname));
-	bd->dns_sd_data->port_p2pj = bd->jabber_data->port;
+	bd->dns_sd_data->port_p2pj = bd->xmpp_data->port;
 	/* Not engaged in AV conference */
 	bd->dns_sd_data->vc = g_strdup("!");
 
@@ -168,11 +168,11 @@
 		bonjour_dns_sd_free(bd->dns_sd_data);
 	}
 
-	if (bd != NULL && bd->jabber_data != NULL)
+	if (bd != NULL && bd->xmpp_data != NULL)
 	{
 		/* Stop waiting for conversations */
-		bonjour_jabber_stop(bd->jabber_data);
-		g_free(bd->jabber_data);
+		bonjour_xmpp_stop(bd->xmpp_data);
+		g_free(bd->xmpp_data);
 	}
 
 	/* Delete the bonjour group
@@ -206,7 +206,7 @@
 	if (purple_message_is_empty(msg) || !purple_message_get_recipient(msg))
 		return 0;
 
-	return bonjour_jabber_send_message(bd->jabber_data,
+	return bonjour_xmpp_send_message(bd->xmpp_data,
 		purple_message_get_recipient(msg),
 		purple_message_get_contents(msg));
 }
@@ -319,7 +319,7 @@
 		return;
 	}
 
-	bonjour_jabber_close_conversation(bb->conversation);
+	bonjour_xmpp_close_conversation(bb->conversation);
 	bb->conversation = NULL;
 }
 
@@ -448,7 +448,7 @@
 
 	new_group = purple_group_get_name(group);
 
-	g_list_foreach(moved_buddies, (GFunc)bonjour_do_group_change, new_group);
+	g_list_foreach(moved_buddies, (GFunc)bonjour_do_group_change, (gpointer)new_group);
 }
 
 static gboolean
--- a/libpurple/protocols/bonjour/bonjour.h	Fri Oct 25 05:52:30 2019 -0400
+++ b/libpurple/protocols/bonjour/bonjour.h	Sat Oct 26 16:53:30 2019 +0000
@@ -1,5 +1,5 @@
 /**
- * @file bonjour.h The Purple interface to mDNS and peer to peer Jabber.
+ * @file bonjour.h The Purple interface to mDNS and peer to peer XMPP.
  *
  * purple
  *
@@ -32,7 +32,7 @@
 #include <purple.h>
 
 #include "mdns_common.h"
-#include "jabber.h"
+#include "xmpp.h"
 
 #define BONJOUR_GROUP_NAME _("Bonjour")
 #define BONJOUR_PROTOCOL_NAME "bonjour"
@@ -64,7 +64,7 @@
 typedef struct
 {
 	BonjourDnsSd *dns_sd_data;
-	BonjourJabber *jabber_data;
+	BonjourXMPP *xmpp_data;
 	GSList *xfer_lists;
 	gchar *jid;
 } BonjourData;
--- a/libpurple/protocols/bonjour/bonjour_ft.c	Fri Oct 25 05:52:30 2019 -0400
+++ b/libpurple/protocols/bonjour/bonjour_ft.c	Sat Oct 26 16:53:30 2019 +0000
@@ -80,7 +80,7 @@
 		return;
 	}
 
-	iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->jabber_data->account), id);
+	iq = xep_iq_new(bd, XEP_IQ_ERROR, to, bonjour_get_jid(bd->xmpp_data->account), id);
 	if(iq == NULL)
 		return;
 
@@ -213,7 +213,7 @@
 	/* Assign stream id. */
 	g_free(xf->iq_id);
 	xf->iq_id = g_strdup_printf("%u", next_id++);
-	iq = xep_iq_new(xf->data, XEP_IQ_SET, to, bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
+	iq = xep_iq_new(xf->data, XEP_IQ_SET, to, bonjour_get_jid(bd->xmpp_data->account), xf->iq_id);
 	if(iq == NULL)
 		return;
 
@@ -271,7 +271,7 @@
 	bd = xf->data;
 
 	purple_debug_info("bonjour", "xep file transfer stream initialization result.\n");
-	iq = xep_iq_new(bd, XEP_IQ_RESULT, to, bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
+	iq = xep_iq_new(bd, XEP_IQ_RESULT, to, bonjour_get_jid(bd->xmpp_data->account), xf->iq_id);
 	if(iq == NULL)
 		return;
 
@@ -904,7 +904,7 @@
 
 	bd = xf->data;
 
-	iq = xep_iq_new(bd, XEP_IQ_SET, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->sid);
+	iq = xep_iq_new(bd, XEP_IQ_SET, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->xmpp_data->account), xf->sid);
 
 	query = purple_xmlnode_new_child(iq->node, "query");
 	purple_xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
@@ -913,7 +913,7 @@
 
 	purple_xfer_set_local_port(xfer, purple_network_get_port_from_fd(sock));
 
-	local_ips = bonjour_jabber_get_local_ips(sock);
+	local_ips = bonjour_xmpp_get_local_ips(sock);
 
 	port = g_strdup_printf("%hu", purple_xfer_get_local_port(xfer));
 	while(local_ips) {
@@ -981,7 +981,7 @@
 	/* Here, start the file transfer.*/
 
 	/* Notify Initiator of Connection */
-	iq = xep_iq_new(bd, XEP_IQ_RESULT, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->jabber_data->account), xf->iq_id);
+	iq = xep_iq_new(bd, XEP_IQ_RESULT, purple_xfer_get_remote_user(xfer), bonjour_get_jid(bd->xmpp_data->account), xf->iq_id);
 	q_node = purple_xmlnode_new_child(iq->node, "query");
 	purple_xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams");
 	tmp_node = purple_xmlnode_new_child(q_node, "streamhost-used");
--- a/libpurple/protocols/bonjour/buddy.c	Fri Oct 25 05:52:30 2019 -0400
+++ b/libpurple/protocols/bonjour/buddy.c	Sat Oct 26 16:53:30 2019 +0000
@@ -258,7 +258,7 @@
 	g_free(buddy->node);
 	g_free(buddy->ver);
 
-	bonjour_jabber_close_conversation(buddy->conversation);
+	bonjour_xmpp_close_conversation(buddy->conversation);
 	buddy->conversation = NULL;
 
 	/* Clean up any mdns implementation data */
--- a/libpurple/protocols/bonjour/buddy.h	Fri Oct 25 05:52:30 2019 -0400
+++ b/libpurple/protocols/bonjour/buddy.h	Sat Oct 26 16:53:30 2019 +0000
@@ -21,7 +21,7 @@
 
 #include <purple.h>
 
-#include "jabber.h"
+#include "xmpp.h"
 
 typedef struct
 {
@@ -45,7 +45,7 @@
 	gchar *node;
 	gchar *ver;
 
-	BonjourJabberConversation *conversation;
+	BonjourXMPPConversation *conversation;
 
 	gpointer mdns_impl_data;
 } BonjourBuddy;
--- a/libpurple/protocols/bonjour/jabber.c	Fri Oct 25 05:52:30 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1472 +0,0 @@
-/*
- * purple - Bonjour Protocol Plugin
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * 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
- */
-
-#include "internal.h"
-#include <purple.h>
-
-#ifndef _WIN32
-#include <net/if.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#endif
-#include <sys/types.h>
-
-/* Solaris */
-#if defined (__SVR4) && defined (__sun)
-#include <sys/sockio.h>
-#endif
-
-#include <glib.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <fcntl.h>
-
-#ifdef HAVE_GETIFADDRS
-#include <ifaddrs.h>
-#endif
-
-#include "jabber.h"
-#include "parser.h"
-#include "bonjour.h"
-#include "buddy.h"
-#include "bonjour_ft.h"
-
-#ifdef _SIZEOF_ADDR_IFREQ
-#  define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
-#else
-#  define HX_SIZE_OF_IFREQ(a) sizeof(a)
-#endif
-
-#define STREAM_END "</stream:stream>"
-/* TODO: specify version='1.0' and send stream features */
-#define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
-		"<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
-
-enum sent_stream_start_types {
-	NOT_SENT       = 0,
-	PARTIALLY_SENT = 1,
-	FULLY_SENT     = 2
-};
-
-static void
-xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb);
-
-static BonjourJabberConversation *
-bonjour_jabber_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) {
-
-	BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
-	bconv->cancellable = g_cancellable_new();
-	bconv->tx_buf = purple_circular_buffer_new(512);
-	bconv->tx_handler = 0;
-	bconv->rx_handler = 0;
-	bconv->pb = pb;
-	bconv->account = account;
-	bconv->ip = g_strdup(ip);
-
-	bonjour_parser_setup(bconv);
-
-	return bconv;
-}
-
-static const char *
-_font_size_ichat_to_purple(int size)
-{
-	if (size > 24) {
-		return "7";
-	} else if (size >= 21) {
-		return "6";
-	} else if (size >= 17) {
-		return "5";
-	} else if (size >= 14) {
-		return "4";
-	} else if (size >= 12) {
-		return "3";
-	} else if (size >= 10) {
-		return "2";
-	}
-
-	return "1";
-}
-
-static gchar *
-get_xmlnode_contents(PurpleXmlNode *node)
-{
-	gchar *contents;
-
-	contents = purple_xmlnode_to_str(node, NULL);
-
-	/* we just want the stuff inside <font></font>
-	 * There isn't stuff exposed in PurpleXmlNode.c to do this more cleanly. */
-
-	if (contents) {
-		char *bodystart = strchr(contents, '>');
-		char *bodyend = bodystart ? strrchr(bodystart, '<') : NULL;
-		if (bodystart && bodyend && (bodystart + 1) != bodyend) {
-			*bodyend = '\0';
-			memmove(contents, bodystart + 1, (bodyend - bodystart));
-		}
-	}
-
-	return contents;
-}
-
-static void
-_jabber_parse_and_write_message_to_ui(PurpleXmlNode *message_node, PurpleBuddy *pb)
-{
-	PurpleXmlNode *body_node, *html_node, *events_node;
-	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(pb));
-	gchar *body = NULL;
-
-	body_node = purple_xmlnode_get_child(message_node, "body");
-	html_node = purple_xmlnode_get_child(message_node, "html");
-
-	if (body_node == NULL && html_node == NULL) {
-		purple_debug_error("bonjour", "No body or html node found, discarding message.\n");
-		return;
-	}
-
-	events_node = purple_xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
-	if (events_node != NULL) {
-		if (purple_xmlnode_get_child(events_node, "id") != NULL) {
-			/* The user is just typing */
-			/* TODO: Deal with typing notification */
-			return;
-		}
-	}
-
-	if (html_node != NULL) {
-		PurpleXmlNode *html_body_node;
-
-		html_body_node = purple_xmlnode_get_child(html_node, "body");
-		if (html_body_node != NULL) {
-			PurpleXmlNode *html_body_font_node;
-
-			html_body_font_node = purple_xmlnode_get_child(html_body_node, "font");
-			/* Types of messages sent by iChat */
-			if (html_body_font_node != NULL) {
-				gchar *html_body;
-				const char *font_face, *font_size, *font_color,
-					*ichat_balloon_color, *ichat_text_color;
-
-				font_face = purple_xmlnode_get_attrib(html_body_font_node, "face");
-				/* The absolute iChat font sizes should be converted to 1..7 range */
-				font_size = purple_xmlnode_get_attrib(html_body_font_node, "ABSZ");
-				if (font_size != NULL)
-					font_size = _font_size_ichat_to_purple(atoi(font_size));
-				font_color = purple_xmlnode_get_attrib(html_body_font_node, "color");
-				ichat_balloon_color = purple_xmlnode_get_attrib(html_body_node, "ichatballooncolor");
-				ichat_text_color = purple_xmlnode_get_attrib(html_body_node, "ichattextcolor");
-
-				html_body = get_xmlnode_contents(html_body_font_node);
-
-				if (html_body == NULL)
-					/* This is the kind of formatted messages that Purple creates */
-					html_body = purple_xmlnode_to_str(html_body_font_node, NULL);
-
-				if (html_body != NULL) {
-					GString *str = g_string_new("<font");
-
-					if (font_face)
-						g_string_append_printf(str, " face='%s'", font_face);
-					if (font_size)
-						g_string_append_printf(str, " size='%s'", font_size);
-					if (font_color)
-						g_string_append_printf(str, " color='%s'", font_color);
-					else if (ichat_text_color)
-						g_string_append_printf(str, " color='%s'", ichat_text_color);
-					if (ichat_balloon_color)
-						g_string_append_printf(str, " back='%s'", ichat_balloon_color);
-					g_string_append_printf(str, ">%s</font>", html_body);
-
-					body = g_string_free(str, FALSE);
-
-					g_free(html_body);
-				}
-			}
-		}
-	}
-
-	/* Compose the message */
-	if (body == NULL && body_node != NULL)
-		body = purple_xmlnode_get_data(body_node);
-
-	if (body == NULL) {
-		purple_debug_error("bonjour", "No html body or regular body found.\n");
-		return;
-	}
-
-	/* Send the message to the UI */
-	purple_serv_got_im(gc, purple_buddy_get_name(pb), body, 0, time(NULL));
-
-	g_free(body);
-}
-
-struct _match_buddies_by_address {
-	const char *address;
-	GSList *matched_buddies;
-};
-
-static void
-_match_buddies_by_address(gpointer value, gpointer data)
-{
-	PurpleBuddy *pb = value;
-	BonjourBuddy *bb = NULL;
-	struct _match_buddies_by_address *mbba = data;
-
-	bb = purple_buddy_get_protocol_data(pb);
-
-	/*
-	 * If the current PurpleBuddy's data is not null, then continue to determine
-	 * whether one of the buddies IPs matches the target IP.
-	 */
-	if (bb != NULL)
-	{
-		const char *ip;
-		GSList *tmp = bb->ips;
-
-		while(tmp) {
-			ip = tmp->data;
-			if (ip != NULL && g_ascii_strcasecmp(ip, mbba->address) == 0) {
-				mbba->matched_buddies = g_slist_prepend(mbba->matched_buddies, pb);
-				break;
-			}
-			tmp = tmp->next;
-		}
-	}
-}
-
-static void
-_send_data_write_cb(GObject *stream, gpointer data)
-{
-	PurpleBuddy *pb = data;
-	BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
-	BonjourJabberConversation *bconv = bb->conversation;
-	gsize writelen;
-	gssize ret;
-	GError *error = NULL;
-
-	writelen = purple_circular_buffer_get_max_read(bconv->tx_buf);
-
-	if (writelen == 0) {
-		g_source_remove(bconv->tx_handler);
-		bconv->tx_handler = 0;
-		return;
-	}
-
-	ret = g_pollable_output_stream_write_nonblocking(
-	        G_POLLABLE_OUTPUT_STREAM(stream),
-	        purple_circular_buffer_get_output(bconv->tx_buf), writelen,
-	        bconv->cancellable, &error);
-
-	if (ret < 0 && error->code == G_IO_ERROR_WOULD_BLOCK) {
-		g_clear_error(&error);
-		return;
-	} else if (ret <= 0) {
-		PurpleConversation *conv = NULL;
-		PurpleAccount *account = NULL;
-
-		purple_debug_error(
-		        "bonjour",
-		        "Error sending message to buddy %s error: %s",
-		        purple_buddy_get_name(pb),
-		        error ? error->message : "(null)");
-
-		account = purple_buddy_get_account(pb);
-
-		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
-		if (conv != NULL)
-			purple_conversation_write_system_message(conv,
-				_("Unable to send message."),
-				PURPLE_MESSAGE_ERROR);
-
-		bonjour_jabber_close_conversation(bb->conversation);
-		bb->conversation = NULL;
-		g_clear_error(&error);
-		return;
-	}
-
-	purple_circular_buffer_mark_read(bconv->tx_buf, ret);
-}
-
-static gint
-_send_data(PurpleBuddy *pb, char *message)
-{
-	BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
-	BonjourJabberConversation *bconv = bb->conversation;
-	gsize len = strlen(message);
-	gssize ret;
-	GError *error = NULL;
-
-	/* If we're not ready to actually send, append it to the buffer */
-	if (bconv->tx_handler != 0
-			|| bconv->sent_stream_start != FULLY_SENT
-			|| !bconv->recv_stream_start
-			|| purple_circular_buffer_get_max_read(bconv->tx_buf) > 0) {
-		ret = -1;
-		g_set_error_literal(&error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
-		                    "Not yet ready to send.");
-	} else {
-		ret = g_pollable_output_stream_write_nonblocking(
-		        G_POLLABLE_OUTPUT_STREAM(bconv->output), message, len,
-		        bconv->cancellable, &error);
-	}
-
-	if (ret == -1 && error->code == G_IO_ERROR_WOULD_BLOCK) {
-		ret = 0;
-		g_clear_error(&error);
-	} else if (ret <= 0) {
-		PurpleConversation *conv;
-		PurpleAccount *account;
-
-		purple_debug_error(
-		        "bonjour",
-		        "Error sending message to buddy %s error: %s",
-		        purple_buddy_get_name(pb),
-		        error ? error->message : "(null)");
-
-		account = purple_buddy_get_account(pb);
-
-		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
-		if (conv != NULL)
-			purple_conversation_write_system_message(conv,
-				_("Unable to send message."),
-				PURPLE_MESSAGE_ERROR);
-
-		bonjour_jabber_close_conversation(bb->conversation);
-		bb->conversation = NULL;
-		g_clear_error(&error);
-		return -1;
-	}
-
-	if (ret < len) {
-		/* Don't interfere with the stream starting */
-		if (bconv->sent_stream_start == FULLY_SENT &&
-		    bconv->recv_stream_start && bconv->tx_handler == 0) {
-			GSource *source =
-			        g_pollable_output_stream_create_source(
-			                G_POLLABLE_OUTPUT_STREAM(bconv->output),
-			                bconv->cancellable);
-			g_source_set_callback(source,
-			                      (GSourceFunc)_send_data_write_cb,
-			                      pb, NULL);
-			bconv->tx_handler = g_source_attach(source, NULL);
-		}
-		purple_circular_buffer_append(bconv->tx_buf, message + ret, len - ret);
-	}
-
-	return ret;
-}
-
-void bonjour_jabber_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet) {
-
-	g_return_if_fail(packet != NULL);
-	g_return_if_fail(pb != NULL);
-
-	if (purple_strequal(packet->name, "message"))
-		_jabber_parse_and_write_message_to_ui(packet, pb);
-	else if (purple_strequal(packet->name, "iq"))
-		xep_iq_parse(packet, pb);
-	else {
-		purple_debug_warning("bonjour", "Unknown packet: %s\n",
-			packet->name ? packet->name : "(null)");
-	}
-}
-
-static void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
-
-	/* Inform the user that the conversation has been closed */
-	BonjourBuddy *bb = NULL;
-	const gchar *name = bconv->pb ? purple_buddy_get_name(bconv->pb) : "(unknown)";
-
-	purple_debug_info("bonjour", "Received conversation close notification from %s.\n", name);
-
-	if(bconv->pb != NULL)
-		bb = purple_buddy_get_protocol_data(bconv->pb);
-
-	/* Close the socket, clear the watcher and free memory */
-	bonjour_jabber_close_conversation(bconv);
-	if(bb)
-		bb->conversation = NULL;
-}
-
-static gboolean
-_client_socket_handler(GObject *stream, gpointer data)
-{
-	BonjourJabberConversation *bconv = data;
-	GError *error = NULL;
-	gssize len;
-	static char message[4096];
-
-	/* Read the data from the socket */
-	len = g_pollable_input_stream_read_nonblocking(
-	        G_POLLABLE_INPUT_STREAM(stream), message, sizeof(message) - 1,
-	        bconv->cancellable, &error);
-	if (len == -1) {
-		/* There has been an error reading from the socket */
-		if (error == NULL || (error->code != G_IO_ERROR_WOULD_BLOCK &&
-		                      error->code != G_IO_ERROR_CANCELLED)) {
-			purple_debug_warning(
-			        "bonjour",
-			        "receive of %" G_GSSIZE_FORMAT " error: %s",
-			        len, error ? error->message : "(null)");
-
-			bonjour_jabber_close_conversation(bconv);
-			if (bconv->pb != NULL) {
-				BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
-
-				if(bb != NULL)
-					bb->conversation = NULL;
-			}
-
-			/* I guess we really don't need to notify the user.
-			 * If they try to send another message it'll reconnect */
-		}
-		g_clear_error(&error);
-		return FALSE;
-	} else if (len == 0) { /* The other end has closed the socket */
-		const gchar *name = purple_buddy_get_name(bconv->pb);
-		purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (name) ? name : "(unknown)");
-		bonjour_jabber_stream_ended(bconv);
-		return FALSE;
-	}
-
-	message[len] = '\0';
-
-	purple_debug_info("bonjour", "Receive: -%s- %" G_GSSIZE_FORMAT " bytes\n", message, len);
-	bonjour_parser_process(bconv, message, len);
-
-	return TRUE;
-}
-
-struct _stream_start_data {
-	char *msg;
-};
-
-static void
-_start_stream(GObject *stream, gpointer data)
-{
-	BonjourJabberConversation *bconv = data;
-	struct _stream_start_data *ss = bconv->stream_data;
-	GError *error = NULL;
-	gsize len;
-	gssize ret;
-
-	len = strlen(ss->msg);
-
-	/* Start Stream */
-	ret = g_pollable_output_stream_write_nonblocking(
-	        G_POLLABLE_OUTPUT_STREAM(stream), ss->msg, len,
-	        bconv->cancellable, &error);
-
-	if (ret == -1 && error->code == G_IO_ERROR_WOULD_BLOCK) {
-		g_clear_error(&error);
-		return;
-	} else if (ret <= 0) {
-		PurpleConversation *conv;
-		const char *bname = bconv->buddy_name;
-		BonjourBuddy *bb = NULL;
-
-		if(bconv->pb) {
-			bb = purple_buddy_get_protocol_data(bconv->pb);
-			bname = purple_buddy_get_name(bconv->pb);
-		}
-
-		purple_debug_error(
-		        "bonjour",
-		        "Error starting stream with buddy %s at %s error: %s",
-		        bname ? bname : "(unknown)", bconv->ip,
-		        error ? error->message : "(null)");
-
-		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
-		if (conv != NULL)
-			purple_conversation_write_system_message(conv,
-				_("Unable to send the message, the conversation couldn't be started."),
-				PURPLE_MESSAGE_ERROR);
-
-		bonjour_jabber_close_conversation(bconv);
-		if(bb != NULL)
-			bb->conversation = NULL;
-
-		g_clear_error(&error);
-		return;
-	}
-
-	/* This is EXTREMELY unlikely to happen */
-	if (ret < len) {
-		char *tmp = g_strdup(ss->msg + ret);
-		g_free(ss->msg);
-		ss->msg = tmp;
-		return;
-	}
-
-	g_free(ss->msg);
-	g_free(ss);
-	bconv->stream_data = NULL;
-
-	/* Stream started; process the send buffer if there is one */
-	g_source_remove(bconv->tx_handler);
-	bconv->tx_handler = 0;
-	bconv->sent_stream_start = FULLY_SENT;
-
-	bonjour_jabber_stream_started(bconv);
-}
-
-static gboolean
-bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv,
-                                GError **error)
-{
-	gchar *stream_start;
-	gsize len;
-	gssize ret;
-	const char *bname = bconv->buddy_name;
-
-	g_return_val_if_fail(error != NULL, FALSE);
-
-	if (bconv->pb != NULL)
-		bname = purple_buddy_get_name(bconv->pb);
-
-	/* If we have no idea who "to" is, use an empty string.
-	 * If we don't know now, it is because the other side isn't playing nice, so they can't complain. */
-	if (bname == NULL)
-		bname = "";
-
-	stream_start = g_strdup_printf(DOCTYPE, bonjour_get_jid(bconv->account), bname);
-	len = strlen(stream_start);
-
-	bconv->sent_stream_start = PARTIALLY_SENT;
-
-	/* Start the stream */
-	ret = g_pollable_output_stream_write_nonblocking(
-	        G_POLLABLE_OUTPUT_STREAM(bconv->output), stream_start, len,
-	        bconv->cancellable, error);
-	if (ret == -1 && (*error)->code == G_IO_ERROR_WOULD_BLOCK) {
-		ret = 0;
-		g_clear_error(error);
-	} else if (ret <= 0) {
-		purple_debug_error(
-		        "bonjour",
-		        "Error starting stream with buddy %s at %s error: %s",
-		        (*bname) ? bname : "(unknown)", bconv->ip,
-		        *error ? (*error)->message : "(null)");
-
-		if (bconv->pb) {
-			PurpleConversation *conv;
-			conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
-			if (conv != NULL)
-				purple_conversation_write_system_message(conv,
-					_("Unable to send the message, the conversation couldn't be started."),
-					PURPLE_MESSAGE_ERROR);
-		}
-
-		purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
-		                          G_INPUT_STREAM(bconv->input),
-		                          G_OUTPUT_STREAM(bconv->output));
-		g_clear_object(&bconv->socket);
-		bconv->input = NULL;
-		bconv->output = NULL;
-		g_free(stream_start);
-
-		return FALSE;
-	}
-
-	/* This is unlikely to happen */
-	if (ret < len) {
-		GSource *source;
-		struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
-		ss->msg = g_strdup(stream_start + ret);
-		bconv->stream_data = ss;
-		/* Finish sending the stream start */
-		source = g_pollable_output_stream_create_source(
-		        G_POLLABLE_OUTPUT_STREAM(bconv->output),
-		        bconv->cancellable);
-		g_source_set_callback(source, (GSourceFunc)_start_stream, bconv,
-		                      NULL);
-		bconv->tx_handler = g_source_attach(source, NULL);
-	} else {
-		bconv->sent_stream_start = FULLY_SENT;
-	}
-
-	g_free(stream_start);
-
-	return TRUE;
-}
-
-/* This gets called when we've successfully sent our <stream:stream />
- * AND when we've received a <stream:stream /> */
-void
-bonjour_jabber_stream_started(BonjourJabberConversation *bconv)
-{
-	GError *error = NULL;
-
-	if (bconv->sent_stream_start == NOT_SENT &&
-	    !bonjour_jabber_send_stream_init(bconv, &error)) {
-		const char *bname = bconv->buddy_name;
-
-		if (bconv->pb)
-			bname = purple_buddy_get_name(bconv->pb);
-
-		purple_debug_error(
-		        "bonjour",
-		        "Error starting stream with buddy %s at %s error: %s",
-		        bname ? bname : "(unknown)", bconv->ip,
-		        error ? error->message : "(null)");
-
-		if (bconv->pb) {
-			PurpleConversation *conv;
-			conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
-			if (conv != NULL)
-				purple_conversation_write_system_message(conv,
-					_("Unable to send the message, the conversation couldn't be started."),
-					PURPLE_MESSAGE_ERROR);
-		}
-
-		/* We don't want to recieve anything else */
-		purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
-		                          G_INPUT_STREAM(bconv->input),
-		                          G_OUTPUT_STREAM(bconv->output));
-		g_clear_object(&bconv->socket);
-		bconv->input = NULL;
-		bconv->output = NULL;
-
-		/* This must be asynchronous because it destroys the parser and we
-		 * may be in the middle of parsing.
-		 */
-		async_bonjour_jabber_close_conversation(bconv);
-		g_clear_error(&error);
-		return;
-	}
-
-	/* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
-	/* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
-	if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start
-			&& bconv->pb && purple_circular_buffer_get_max_read(bconv->tx_buf) > 0) {
-		/* Watch for when we can write the buffered messages */
-		GSource *source = g_pollable_output_stream_create_source(
-		        G_POLLABLE_OUTPUT_STREAM(bconv->output),
-		        bconv->cancellable);
-		g_source_set_callback(source, (GSourceFunc)_send_data_write_cb,
-		                      bconv->pb, NULL);
-		bconv->tx_handler = g_source_attach(source, NULL);
-		/* We can probably write the data right now. */
-		_send_data_write_cb(G_OBJECT(bconv->output), bconv->pb);
-	}
-}
-
-#ifndef INET6_ADDRSTRLEN
-#define INET6_ADDRSTRLEN 46
-#endif
-
-static void
-_server_socket_handler(GSocketService *service, GSocketConnection *connection,
-                       GObject *source_object, gpointer data)
-{
-	BonjourJabber *jdata = data;
-	GSocketAddress *their_addr; /* connector's address information */
-	GInetAddress *their_inet_addr;
-	gchar *address_text;
-	struct _match_buddies_by_address *mbba;
-	BonjourJabberConversation *bconv;
-	GSList *buddies;
-	GSource *source;
-
-	their_addr = g_socket_connection_get_remote_address(connection, NULL);
-	if (their_addr == NULL) {
-		return;
-	}
-	their_inet_addr = g_inet_socket_address_get_address(
-	        G_INET_SOCKET_ADDRESS(their_addr));
-
-	/* Look for the buddy that has opened the conversation and fill information */
-	address_text = g_inet_address_to_string(their_inet_addr);
-	if (g_inet_address_get_family(their_inet_addr) ==
-	            G_SOCKET_FAMILY_IPV6 &&
-	    g_inet_address_get_is_link_local(their_inet_addr)) {
-		gchar *tmp = g_strdup_printf(
-		        "%s%%%d", address_text,
-		        g_inet_socket_address_get_scope_id(
-		                G_INET_SOCKET_ADDRESS(their_addr)));
-		g_free(address_text);
-		address_text = tmp;
-	}
-	g_object_unref(their_addr);
-
-	purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
-	mbba = g_new0(struct _match_buddies_by_address, 1);
-	mbba->address = address_text;
-
-	buddies = purple_blist_find_buddies(jdata->account, NULL);
-	g_slist_foreach(buddies, _match_buddies_by_address, mbba);
-	g_slist_free(buddies);
-
-	if (mbba->matched_buddies == NULL) {
-		purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
-		g_free(address_text);
-		g_free(mbba);
-		return;
-	}
-
-	g_slist_free(mbba->matched_buddies);
-	g_free(mbba);
-
-	/* We've established that this *could* be from one of our buddies.
-	 * Wait for the stream open to see if that matches too before assigning it.
-	 */
-	bconv = bonjour_jabber_conv_new(NULL, jdata->account, address_text);
-
-	/* We wait for the stream start before doing anything else */
-	bconv->socket = g_object_ref(connection);
-	bconv->input = g_io_stream_get_input_stream(G_IO_STREAM(bconv->socket));
-	bconv->output =
-	        g_io_stream_get_output_stream(G_IO_STREAM(bconv->socket));
-	source = g_pollable_input_stream_create_source(
-	        G_POLLABLE_INPUT_STREAM(bconv->input), bconv->cancellable);
-	g_source_set_callback(source, (GSourceFunc)_client_socket_handler,
-	                      bconv, NULL);
-	bconv->rx_handler = g_source_attach(source, NULL);
-	g_free(address_text);
-}
-
-gint
-bonjour_jabber_start(BonjourJabber *jdata)
-{
-	GError *error = NULL;
-	guint16 port;
-
-	purple_debug_info("bonjour", "Attempting to bind IP socket to port %d.",
-	                  jdata->port);
-
-	/* Open a listening server for incoming conversations */
-	jdata->service = g_socket_service_new();
-	g_socket_listener_set_backlog(G_SOCKET_LISTENER(jdata->service), 10);
-	port = jdata->port;
-	if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(jdata->service),
-	                                     port, NULL, &error)) {
-		purple_debug_info("bonjour",
-		                  "Unable to bind to specified port %i: %s",
-		                  port, error ? error->message : "(unknown)");
-		g_clear_error(&error);
-		port = g_socket_listener_add_any_inet_port(
-		        G_SOCKET_LISTENER(jdata->service), NULL, &error);
-		if (port == 0) {
-			purple_debug_error(
-			        "bonjour", "Unable to create socket: %s",
-			        error ? error->message : "(unknown)");
-			g_clear_error(&error);
-			return -1;
-		}
-	}
-	purple_debug_info("bonjour", "Bound IP socket to port %u.", port);
-	jdata->port = port;
-
-	g_signal_connect(G_OBJECT(jdata->service), "incoming",
-	                 G_CALLBACK(_server_socket_handler), jdata);
-
-	return jdata->port;
-}
-
-static void
-_connected_to_buddy(GObject *source, GAsyncResult *res, gpointer user_data)
-{
-	PurpleBuddy *pb = user_data;
-	BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
-	GSocketConnection *conn;
-	GSource *rx_source;
-	GError *error = NULL;
-
-	conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
-	                                              res, &error);
-
-	if (conn == NULL) {
-		PurpleConversation *conv = NULL;
-		PurpleAccount *account = NULL;
-		GSList *tmp;
-
-		if (error && error->code == G_IO_ERROR_CANCELLED) {
-			/* This conversation was closed before it started. */
-			g_error_free(error);
-			return;
-		}
-
-		purple_debug_error("bonjour",
-		                   "Error connecting to buddy %s at %s:%d "
-		                   "(%s); Trying next IP address",
-		                   purple_buddy_get_name(pb),
-		                   bb->conversation->ip, bb->port_p2pj,
-		                   error ? error->message : "(unknown)");
-		g_clear_error(&error);
-
-		/* There may be multiple entries for the same IP - one per
-		 * presence recieved (e.g. multiple interfaces).
-		 * We need to make sure that we find the previously used entry.
-		 */
-		tmp = g_slist_find(bb->ips, bb->conversation->ip_link);
-		if (tmp)
-			tmp = g_slist_next(tmp);
-
-		account = purple_buddy_get_account(pb);
-
-		if (tmp != NULL) {
-			const gchar *ip;
-			GSocketClient *client;
-
-			bb->conversation->ip_link = ip = tmp->data;
-
-			purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n",
-					  purple_buddy_get_name(pb), ip, bb->port_p2pj);
-
-			/* Make sure to connect without a proxy. */
-			client = g_socket_client_new();
-			if (client != NULL) {
-				g_free(bb->conversation->ip);
-				bb->conversation->ip = g_strdup(ip);
-				g_socket_client_connect_to_host_async(
-				        client, ip, bb->port_p2pj,
-				        bb->conversation->cancellable,
-				        _connected_to_buddy, pb);
-				g_object_unref(client);
-				return;
-			}
-		}
-
-		purple_debug_error("bonjour", "No more addresses for buddy %s. Aborting", purple_buddy_get_name(pb));
-
-		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
-		if (conv != NULL)
-			purple_conversation_write_system_message(conv,
-				_("Unable to send the message, the conversation couldn't be started."),
-				PURPLE_MESSAGE_ERROR);
-
-		bonjour_jabber_close_conversation(bb->conversation);
-		bb->conversation = NULL;
-		return;
-	}
-
-	bb->conversation->socket = conn;
-	bb->conversation->input =
-	        g_io_stream_get_input_stream(G_IO_STREAM(conn));
-	bb->conversation->output =
-	        g_io_stream_get_output_stream(G_IO_STREAM(conn));
-
-	if (!bonjour_jabber_send_stream_init(bb->conversation, &error)) {
-		PurpleConversation *conv = NULL;
-		PurpleAccount *account = NULL;
-
-		purple_debug_error("bonjour",
-		                   "Error starting stream with buddy %s at "
-		                   "%s:%d error: %s",
-		                   purple_buddy_get_name(pb),
-		                   bb->conversation->ip, bb->port_p2pj,
-		                   error ? error->message : "(null)");
-
-		account = purple_buddy_get_account(pb);
-
-		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
-		if (conv != NULL)
-			purple_conversation_write_system_message(conv,
-				_("Unable to send the message, the conversation couldn't be started."),
-				PURPLE_MESSAGE_ERROR);
-
-		bonjour_jabber_close_conversation(bb->conversation);
-		bb->conversation = NULL;
-		g_clear_error(&error);
-		return;
-	}
-
-	/* Start listening for the stream acknowledgement */
-	rx_source = g_pollable_input_stream_create_source(
-	        G_POLLABLE_INPUT_STREAM(bb->conversation->input),
-	        bb->conversation->cancellable);
-	g_source_set_callback(rx_source, (GSourceFunc)_client_socket_handler,
-	                      bb->conversation, NULL);
-	bb->conversation->rx_handler = g_source_attach(rx_source, NULL);
-}
-
-void
-bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
-	PurpleBuddy *pb = NULL;
-	BonjourBuddy *bb = NULL;
-
-	g_return_if_fail(bconv->ip != NULL);
-	g_return_if_fail(bconv->pb == NULL);
-
-	pb = purple_blist_find_buddy(bconv->account, bconv->buddy_name);
-	if (pb && (bb = purple_buddy_get_protocol_data(pb))) {
-		const char *ip;
-		GSList *tmp = bb->ips;
-
-		purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
-			purple_buddy_get_name(pb));
-
-		/* Check that one of the buddy's IPs matches */
-		while(tmp) {
-			ip = tmp->data;
-			if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
-				PurpleConnection *pc = purple_account_get_connection(bconv->account);
-				BonjourData *bd = purple_connection_get_protocol_data(pc);
-				BonjourJabber *jdata = bd->jabber_data;
-
-				purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
-					purple_buddy_get_name(pb), bconv->ip);
-
-				/* Attach conv. to buddy and remove from pending list */
-				jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
-
-				/* Check if the buddy already has a conversation and, if so, replace it */
-				if(bb->conversation != NULL && bb->conversation != bconv)
-					bonjour_jabber_close_conversation(bb->conversation);
-
-				bconv->pb = pb;
-				bb->conversation = bconv;
-
-				break;
-			}
-			tmp = tmp->next;
-		}
-	}
-
-	/* We've failed to match a buddy - give up */
-	if (bconv->pb == NULL) {
-		/* This must be asynchronous because it destroys the parser and we
-		 * may be in the middle of parsing.
-		 */
-		async_bonjour_jabber_close_conversation(bconv);
-	}
-}
-
-
-void
-bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) {
-	PurpleConnection *pc = purple_account_get_connection(bconv->account);
-	BonjourData *bd = purple_connection_get_protocol_data(pc);
-	BonjourJabber *jdata = bd->jabber_data;
-	struct _match_buddies_by_address *mbba;
-	GSList *buddies;
-
-	mbba = g_new0(struct _match_buddies_by_address, 1);
-	mbba->address = bconv->ip;
-
-	buddies = purple_blist_find_buddies(jdata->account, NULL);
-	g_slist_foreach(buddies, _match_buddies_by_address, mbba);
-	g_slist_free(buddies);
-
-	/* If there is exactly one match, use it */
-	if(mbba->matched_buddies != NULL) {
-		if(mbba->matched_buddies->next != NULL)
-			purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip);
-		else {
-			PurpleBuddy *pb = mbba->matched_buddies->data;
-			BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
-
-			purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
-				purple_buddy_get_name(pb), bconv->ip);
-
-			/* Attach conv. to buddy and remove from pending list */
-			jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
-
-			/* Check if the buddy already has a conversation and, if so, replace it */
-			if (bb->conversation != NULL && bb->conversation != bconv)
-				bonjour_jabber_close_conversation(bb->conversation);
-
-			bconv->pb = pb;
-			bb->conversation = bconv;
-		}
-	} else
-		purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv->ip);
-
-	/* We've failed to match a buddy - give up */
-	if (bconv->pb == NULL) {
-		/* This must be asynchronous because it destroys the parser and we
-		 * may be in the middle of parsing.
-		 */
-		async_bonjour_jabber_close_conversation(bconv);
-	}
-
-	g_slist_free(mbba->matched_buddies);
-	g_free(mbba);
-}
-
-static PurpleBuddy *
-_find_or_start_conversation(BonjourJabber *jdata, const gchar *to)
-{
-	PurpleBuddy *pb = NULL;
-	BonjourBuddy *bb = NULL;
-
-	g_return_val_if_fail(jdata != NULL, NULL);
-	g_return_val_if_fail(to != NULL, NULL);
-
-	pb = purple_blist_find_buddy(jdata->account, to);
-	if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL)
-		/* You can not send a message to an offline buddy */
-		return NULL;
-
-	/* Check if there is a previously open conversation */
-	if (bb->conversation == NULL) {
-		GSocketClient *client;
-		/* Start with the first IP address. */
-		const gchar *ip = bb->ips->data;
-
-		purple_debug_info("bonjour",
-		                  "Starting conversation with %s at %s:%d", to,
-		                  ip, bb->port_p2pj);
-
-		/* Make sure to connect without a proxy. */
-		client = g_socket_client_new();
-		if (client == NULL) {
-			purple_debug_error("bonjour",
-			                   "Unable to connect to buddy (%s).",
-			                   to);
-			return NULL;
-		}
-
-		bb->conversation = bonjour_jabber_conv_new(pb, jdata->account, ip);
-		bb->conversation->ip_link = ip;
-
-		g_socket_client_connect_to_host_async(
-		        client, ip, bb->port_p2pj,
-		        bb->conversation->cancellable, _connected_to_buddy, pb);
-		g_object_unref(client);
-	}
-	return pb;
-}
-
-int
-bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *body)
-{
-	PurpleXmlNode *message_node, *node, *node2;
-	gchar *message, *xhtml;
-	PurpleBuddy *pb;
-	BonjourBuddy *bb;
-	int ret;
-
-	pb = _find_or_start_conversation(jdata, to);
-	if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL) {
-		purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
-		/* You can not send a message to an offline buddy */
-		return -10000;
-	}
-
-	purple_markup_html_to_xhtml(body, &xhtml, &message);
-
-	message_node = purple_xmlnode_new("message");
-	purple_xmlnode_set_attrib(message_node, "to", bb->name);
-	purple_xmlnode_set_attrib(message_node, "from", bonjour_get_jid(jdata->account));
-	purple_xmlnode_set_attrib(message_node, "type", "chat");
-
-	/* Enclose the message from the UI within a "font" node */
-	node = purple_xmlnode_new_child(message_node, "body");
-	purple_xmlnode_insert_data(node, message, strlen(message));
-	g_free(message);
-
-	node = purple_xmlnode_new_child(message_node, "html");
-	purple_xmlnode_set_namespace(node, "http://www.w3.org/1999/xhtml");
-
-	node = purple_xmlnode_new_child(node, "body");
-	message = g_strdup_printf("<font>%s</font>", xhtml);
-	node2 = purple_xmlnode_from_str(message, strlen(message));
-	g_free(xhtml);
-	g_free(message);
-	purple_xmlnode_insert_child(node, node2);
-
-	node = purple_xmlnode_new_child(message_node, "x");
-	purple_xmlnode_set_namespace(node, "jabber:x:event");
-	purple_xmlnode_insert_child(node, purple_xmlnode_new("composing"));
-
-	message = purple_xmlnode_to_str(message_node, NULL);
-	purple_xmlnode_free(message_node);
-
-	ret = _send_data(pb, message) >= 0;
-
-	g_free(message);
-
-	return ret;
-}
-
-static gboolean
-_async_bonjour_jabber_close_conversation_cb(gpointer data) {
-	BonjourJabberConversation *bconv = data;
-	bonjour_jabber_close_conversation(bconv);
-	return FALSE;
-}
-
-void
-async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) {
-	PurpleConnection *pc = purple_account_get_connection(bconv->account);
-	BonjourData *bd = purple_connection_get_protocol_data(pc);
-	BonjourJabber *jdata = bd->jabber_data;
-
-	jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
-
-	/* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
-	if(bconv->pb != NULL) {
-		BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
-		if (bb->conversation == bconv)
-			bb->conversation = NULL;
-	}
-
-	bconv->close_timeout = g_timeout_add(0, _async_bonjour_jabber_close_conversation_cb, bconv);
-}
-
-void
-bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
-{
-	BonjourData *bd = NULL;
-	PurpleConnection *pc = NULL;
-
-	if (bconv == NULL) {
-		return;
-	}
-
-	pc = purple_account_get_connection(bconv->account);
-	PURPLE_ASSERT_CONNECTION_IS_VALID(pc);
-
-	bd = purple_connection_get_protocol_data(pc);
-	if (bd) {
-		bd->jabber_data->pending_conversations = g_slist_remove(
-			bd->jabber_data->pending_conversations, bconv);
-	}
-
-	/* Cancel any file transfers that are waiting to begin */
-	/* There wont be any transfers if it hasn't been attached to a buddy */
-	if (bconv->pb != NULL && bd != NULL) {
-		GSList *xfers, *tmp_next;
-		xfers = bd->xfer_lists;
-		while (xfers != NULL) {
-			PurpleXfer *xfer = xfers->data;
-			tmp_next = xfers->next;
-			/* We only need to cancel this if it hasn't actually started transferring. */
-			/* This will change if we ever support IBB transfers. */
-			if (purple_strequal(purple_xfer_get_remote_user(xfer), purple_buddy_get_name(bconv->pb))
-					&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
-						|| purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
-				purple_xfer_cancel_remote(xfer);
-			}
-			xfers = tmp_next;
-		}
-	}
-
-	/* Close the socket and remove the watcher */
-	if (bconv->socket != NULL) {
-		/* Send the end of the stream to the other end of the conversation */
-		if (bconv->sent_stream_start == FULLY_SENT) {
-			size_t len = strlen(STREAM_END);
-			if (g_pollable_output_stream_write_nonblocking(
-			            G_POLLABLE_OUTPUT_STREAM(bconv->output),
-			            STREAM_END, len, bconv->cancellable,
-			            NULL) != (gssize)len) {
-				purple_debug_error("bonjour",
-					"bonjour_jabber_close_conversation: "
-					"couldn't send data\n");
-			}
-		}
-		/* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
-		purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
-		                          G_INPUT_STREAM(bconv->input),
-		                          G_OUTPUT_STREAM(bconv->output));
-	}
-	if (bconv->rx_handler != 0) {
-		g_source_remove(bconv->rx_handler);
-		bconv->rx_handler = 0;
-	}
-	if (bconv->tx_handler != 0) {
-		g_source_remove(bconv->tx_handler);
-		bconv->tx_handler = 0;
-	}
-
-	/* Cancel any pending operations. */
-	if (bconv->cancellable != NULL) {
-		g_cancellable_cancel(bconv->cancellable);
-		g_clear_object(&bconv->cancellable);
-	}
-
-	/* Free all the data related to the conversation */
-	g_clear_object(&bconv->socket);
-	bconv->input = NULL;
-	bconv->output = NULL;
-
-	g_object_unref(G_OBJECT(bconv->tx_buf));
-	if (bconv->stream_data != NULL) {
-		struct _stream_start_data *ss = bconv->stream_data;
-		g_free(ss->msg);
-		g_free(ss);
-	}
-
-	if (bconv->context != NULL) {
-		bonjour_parser_setup(bconv);
-	}
-
-	if (bconv->close_timeout != 0) {
-		g_source_remove(bconv->close_timeout);
-	}
-
-	g_free(bconv->buddy_name);
-	g_free(bconv->ip);
-	g_free(bconv);
-}
-
-void
-bonjour_jabber_stop(BonjourJabber *jdata)
-{
-	/* Close the server socket and remove the watcher */
-	if (jdata->service) {
-		g_socket_service_stop(jdata->service);
-		g_socket_listener_close(G_SOCKET_LISTENER(jdata->service));
-		g_clear_object(&jdata->service);
-	}
-
-	/* Close all the conversation sockets and remove all the watchers after sending end streams */
-	if (!purple_account_is_disconnected(jdata->account)) {
-		GSList *buddies, *l;
-
-		buddies = purple_blist_find_buddies(jdata->account, NULL);
-		for (l = buddies; l; l = l->next) {
-			BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data);
-			if (bb && bb->conversation) {
-				/* Any ongoing connection attempt is cancelled
-				 * when a connection is destroyed */
-				bonjour_jabber_close_conversation(bb->conversation);
-				bb->conversation = NULL;
-			}
-		}
-
-		g_slist_free(buddies);
-	}
-
-	g_slist_free_full(jdata->pending_conversations, (GDestroyNotify)bonjour_jabber_close_conversation);
-}
-
-XepIq *
-xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
-{
-	PurpleXmlNode *iq_node = NULL;
-	XepIq *iq = NULL;
-
-	g_return_val_if_fail(data != NULL, NULL);
-	g_return_val_if_fail(to != NULL, NULL);
-	g_return_val_if_fail(id != NULL, NULL);
-
-	iq_node = purple_xmlnode_new("iq");
-
-	purple_xmlnode_set_attrib(iq_node, "to", to);
-	purple_xmlnode_set_attrib(iq_node, "from", from);
-	purple_xmlnode_set_attrib(iq_node, "id", id);
-	switch (type) {
-		case XEP_IQ_SET:
-			purple_xmlnode_set_attrib(iq_node, "type", "set");
-			break;
-		case XEP_IQ_GET:
-			purple_xmlnode_set_attrib(iq_node, "type", "get");
-			break;
-		case XEP_IQ_RESULT:
-			purple_xmlnode_set_attrib(iq_node, "type", "result");
-			break;
-		case XEP_IQ_ERROR:
-			purple_xmlnode_set_attrib(iq_node, "type", "error");
-			break;
-		case XEP_IQ_NONE:
-		default:
-			purple_xmlnode_set_attrib(iq_node, "type", "none");
-			break;
-	}
-
-	iq = g_new0(XepIq, 1);
-	iq->node = iq_node;
-	iq->type = type;
-	iq->data = ((BonjourData*)data)->jabber_data;
-	iq->to = (char*)to;
-
-	return iq;
-}
-
-static gboolean
-check_if_blocked(PurpleBuddy *pb)
-{
-	gboolean blocked = FALSE;
-	GSList *l = NULL;
-	PurpleAccount *acc = purple_buddy_get_account(pb);
-	const gchar *name;
-
-	if(acc == NULL)
-		return FALSE;
-
-	l = purple_account_privacy_get_denied(acc);
-	name = purple_buddy_get_name(pb);
-
-	if(g_slist_find_custom(l, name, (GCompareFunc)purple_utf8_strcasecmp) != NULL) {
-		const gchar *username = bonjour_get_jid(acc);
-
-		purple_debug_info("bonjour", "%s has been blocked by %s.\n", name, username);
-		blocked = TRUE;
-	}
-	return blocked;
-}
-
-static void
-xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb)
-{
-	PurpleAccount *account;
-	PurpleConnection *gc;
-
-	if(check_if_blocked(pb))
-		return;
-
-	account = purple_buddy_get_account(pb);
-	gc = purple_account_get_connection(account);
-
-	if (purple_xmlnode_get_child(packet, "si") != NULL || purple_xmlnode_get_child(packet, "error") != NULL)
-		xep_si_parse(gc, packet, pb);
-	else
-		xep_bytestreams_parse(gc, packet, pb);
-}
-
-int
-xep_iq_send_and_free(XepIq *iq)
-{
-	int ret = -1;
-	PurpleBuddy *pb = NULL;
-
-	/* start the talk, reuse the message socket  */
-	pb = _find_or_start_conversation((BonjourJabber*) iq->data, iq->to);
-	/* Send the message */
-	if (pb != NULL) {
-		/* Convert xml node into stream */
-		gchar *msg = purple_xmlnode_to_str(iq->node, NULL);
-		ret = _send_data(pb, msg);
-		g_free(msg);
-	}
-
-	purple_xmlnode_free(iq->node);
-	iq->node = NULL;
-	g_free(iq);
-
-	return (ret >= 0) ? 0 : -1;
-}
-
-/* This returns a list containing all non-localhost IPs */
-GSList *
-bonjour_jabber_get_local_ips(int fd)
-{
-	GSList *ips = NULL;
-	const char *address_text;
-	int ret;
-
-#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
-	struct ifaddrs *ifap, *ifa;
-	common_sockaddr_t addr;
-	char addrstr[INET6_ADDRSTRLEN];
-
-	ret = getifaddrs(&ifap);
-	if (ret != 0) {
-		const char *error = g_strerror(errno);
-		purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
-		return NULL;
-	}
-
-	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-		if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
-			continue;
-
-		memcpy(&addr, ifa->ifa_addr, sizeof(addr));
-		address_text = NULL;
-		switch (addr.sa.sa_family) {
-			case AF_INET:
-				address_text = inet_ntop(addr.sa.sa_family,
-					&addr.in.sin_addr,
-					addrstr, sizeof(addrstr));
-				break;
-#ifdef PF_INET6
-			case AF_INET6:
-				address_text = inet_ntop(addr.sa.sa_family,
-					&addr.in6.sin6_addr,
-					addrstr, sizeof(addrstr));
-				break;
-#endif
-		}
-
-		if (address_text != NULL) {
-			if (addr.sa.sa_family == AF_INET)
-				ips = g_slist_append(ips, g_strdup(address_text));
-			else
-				ips = g_slist_prepend(ips, g_strdup(address_text));
-		}
-	}
-
-	freeifaddrs(ifap);
-#else
-	char *tmp;
-	struct ifconf ifc;
-	struct ifreq *ifr;
-	char buffer[1024];
-	struct sockaddr_in *sinptr;
-	int source = fd;
-
-	if (fd < 0)
-		source = socket(PF_INET, SOCK_STREAM, 0);
-
-	ifc.ifc_len = sizeof(buffer);
-	ifc.ifc_req = (struct ifreq *)buffer;
-	ret = ioctl(source, SIOCGIFCONF, &ifc);
-
-	if (fd < 0)
-		close(source);
-
-	if (ret < 0) {
-		const char *error = g_strerror(errno);
-		purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
-		return NULL;
-	}
-
-	tmp = buffer;
-	while (tmp < buffer + ifc.ifc_len) {
-		ifr = (struct ifreq *)tmp;
-		tmp += HX_SIZE_OF_IFREQ(*ifr);
-
-		if (ifr->ifr_addr.sa_family == AF_INET) {
-			sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
-			if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) {
-				address_text = inet_ntoa(sinptr->sin_addr);
-				ips = g_slist_prepend(ips, g_strdup(address_text));
-			}
-		}
-	}
-#endif
-
-	return ips;
-}
-
-void
-append_iface_if_linklocal(char *ip, guint32 interface_param) {
-	struct in6_addr in6_addr;
-	int len_remain = INET6_ADDRSTRLEN - strlen(ip);
-
-	if (len_remain <= 1)
-		return;
-
-	if (inet_pton(AF_INET6, ip, &in6_addr) != 1 ||
-	    !IN6_IS_ADDR_LINKLOCAL(&in6_addr))
-		return;
-
-	snprintf(ip + strlen(ip), len_remain, "%%%d",
-		 interface_param);
-}
--- a/libpurple/protocols/bonjour/jabber.h	Fri Oct 25 05:52:30 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/**
- * @file jabber.h The Purple interface to mDNS and peer to peer Jabber.
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * 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
- *
- */
-
-#ifndef PURPLE_BONJOUR_JABBER_H
-#define PURPLE_BONJOUR_JABBER_H
-
-#include <libxml/parser.h>
-
-#include <purple.h>
-
-typedef struct
-{
-	GSocketService *service;
-	guint16 port;
-	PurpleAccount *account;
-	GSList *pending_conversations;
-} BonjourJabber;
-
-typedef struct
-{
-	GCancellable *cancellable;
-	GSocketConnection *socket;
-	GInputStream *input;
-	GOutputStream *output;
-	guint rx_handler;
-	guint tx_handler;
-	guint close_timeout;
-	PurpleCircularBuffer *tx_buf;
-	int sent_stream_start; /* 0 = Unsent, 1 = Partial, 2 = Complete */
-	gboolean recv_stream_start;
-	gpointer stream_data;
-	xmlParserCtxt *context;
-	PurpleXmlNode *current;
-	PurpleBuddy *pb;
-	PurpleAccount *account;
-
-	/* The following are only needed before attaching to a PurpleBuddy */
-	gchar *buddy_name;
-	gchar *ip;
-	/* This points to a data entry in BonjourBuddy->ips */
-	const gchar *ip_link;
-} BonjourJabberConversation;
-
-/**
- * Start listening for jabber connections.
- *
- * @return -1 if there was a problem, else returns the listening
- *         port number.
- */
-gint bonjour_jabber_start(BonjourJabber *data);
-
-int bonjour_jabber_send_message(BonjourJabber *data, const char *to, const char *body);
-
-void bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
-
-void async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
-
-void bonjour_jabber_stream_started(BonjourJabberConversation *bconv);
-
-void bonjour_jabber_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet);
-
-void bonjour_jabber_stop(BonjourJabber *data);
-
-void bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv);
-
-void bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv);
-
-typedef enum {
-	XEP_IQ_SET,
-	XEP_IQ_GET,
-	XEP_IQ_RESULT,
-	XEP_IQ_ERROR,
-	XEP_IQ_NONE
-} XepIqType;
-
-typedef struct {
-	XepIqType type;
-	char *id;
-	PurpleXmlNode *node;
-	char *to;
-	void *data;
-} XepIq;
-
-XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id);
-int xep_iq_send_and_free(XepIq *iq);
-GSList * bonjour_jabber_get_local_ips(int fd);
-
-void append_iface_if_linklocal(char *ip, guint32 interface_param);
-
-#endif /* PURPLE_BONJOUR_JABBER_H */
--- a/libpurple/protocols/bonjour/meson.build	Fri Oct 25 05:52:30 2019 -0400
+++ b/libpurple/protocols/bonjour/meson.build	Sat Oct 26 16:53:30 2019 +0000
@@ -3,8 +3,8 @@
 	'bonjour.h',
 	'buddy.c',
 	'buddy.h',
-	'jabber.c',
-	'jabber.h',
+	'xmpp.c',
+	'xmpp.h',
 	'mdns_common.c',
 	'mdns_common.h',
 	'mdns_interface.h',
--- a/libpurple/protocols/bonjour/parser.c	Fri Oct 25 05:52:30 2019 -0400
+++ b/libpurple/protocols/bonjour/parser.c	Sat Oct 26 16:53:30 2019 +0000
@@ -1,5 +1,5 @@
 /*
- * purple - Bonjour Jabber XML parser stuff
+ * purple - Bonjour XMPP XML parser stuff
  *
  * Purple is the legal property of its developers, whose names are too numerous
  * to list here.  Please refer to the COPYRIGHT file distributed with this
@@ -25,11 +25,11 @@
 
 #include <libxml/parser.h>
 
-#include "jabber.h"
 #include "parser.h"
+#include "xmpp.h"
 
 static gboolean
-parse_from_attrib_and_find_buddy(BonjourJabberConversation *bconv, int nb_attributes, const xmlChar **attributes) {
+parse_from_attrib_and_find_buddy(BonjourXMPPConversation *bconv, int nb_attributes, const xmlChar **attributes) {
 	int i;
 
 	/* If the "from" attribute is specified, attach it to the conversation. */
@@ -37,7 +37,7 @@
 		if(!xmlStrcmp(attributes[i], (xmlChar*) "from")) {
 			int len = attributes[i+4] - attributes[i+3];
 			bconv->buddy_name = g_strndup((char *)attributes[i+3], len);
-			bonjour_jabber_conv_match_by_name(bconv);
+			bonjour_xmpp_conv_match_by_name(bconv);
 
 			return (bconv->pb != NULL);
 		}
@@ -52,7 +52,7 @@
 				   int nb_namespaces, const xmlChar **namespaces,
 				   int nb_attributes, int nb_defaulted, const xmlChar **attributes)
 {
-	BonjourJabberConversation *bconv = user_data;
+	BonjourXMPPConversation *bconv = user_data;
 
 	PurpleXmlNode *node;
 	int i;
@@ -66,7 +66,7 @@
 			if (bconv->pb == NULL)
 				parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes);
 
-			bonjour_jabber_stream_started(bconv);
+			bonjour_xmpp_stream_started(bconv);
 		}
 	} else {
 
@@ -79,7 +79,7 @@
 			/* We've run out of options for finding who the conversation is from
 			   using explicitly specified stuff; see if we can make a good match
 			   by using the IP */
-			bonjour_jabber_conv_match_by_ip(bconv);
+			bonjour_xmpp_conv_match_by_ip(bconv);
 
 		if(bconv->current)
 			node = purple_xmlnode_new_child(bconv->current, (const char*) element_name);
@@ -113,7 +113,7 @@
 bonjour_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
 				 const xmlChar *prefix, const xmlChar *namespace)
 {
-	BonjourJabberConversation *bconv = user_data;
+	BonjourXMPPConversation *bconv = user_data;
 
 	if(!bconv->current) {
 		/* We don't keep a reference to the start stream PurpleXmlNode,
@@ -121,7 +121,7 @@
 		if(!xmlStrcmp(element_name, (xmlChar*) "stream"))
 			/* Asynchronously close the conversation to prevent bonjour_parser_setup()
 			 * being called from within this context */
-			async_bonjour_jabber_close_conversation(bconv);
+			async_bonjour_xmpp_close_conversation(bconv);
 		return;
 	}
 
@@ -131,7 +131,7 @@
 	} else {
 		PurpleXmlNode *packet = bconv->current;
 		bconv->current = NULL;
-		bonjour_jabber_process_packet(bconv->pb, packet);
+		bonjour_xmpp_process_packet(bconv->pb, packet);
 		purple_xmlnode_free(packet);
 	}
 }
@@ -139,7 +139,7 @@
 static void
 bonjour_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
 {
-	BonjourJabberConversation *bconv = user_data;
+	BonjourXMPPConversation *bconv = user_data;
 
 	if(!bconv->current)
 		return;
@@ -153,9 +153,9 @@
 static void
 bonjour_parser_structured_error_handler(void *user_data, xmlErrorPtr error)
 {
-	BonjourJabberConversation *bconv = user_data;
+	BonjourXMPPConversation *bconv = user_data;
 
-	purple_debug_error("jabber", "XML parser error for BonjourJabberConversation %p: "
+	purple_debug_error("bonjour", "XML parser error for BonjourXMPPConversation %p: "
 	                             "Domain %i, code %i, level %i: %s",
 	                   bconv,
 	                   error->domain, error->code, error->level,
@@ -198,7 +198,7 @@
 };
 
 void
-bonjour_parser_setup(BonjourJabberConversation *bconv)
+bonjour_parser_setup(BonjourXMPPConversation *bconv)
 {
 
 	/* This seems backwards, but it makes sense. The libxml code creates
@@ -213,7 +213,7 @@
 }
 
 
-void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len)
+void bonjour_parser_process(BonjourXMPPConversation *bconv, const char *buf, int len)
 {
 
 	if (bconv->context == NULL) {
--- a/libpurple/protocols/bonjour/parser.h	Fri Oct 25 05:52:30 2019 -0400
+++ b/libpurple/protocols/bonjour/parser.h	Sat Oct 26 16:53:30 2019 +0000
@@ -1,5 +1,5 @@
 /**
- * @file parser.h Bonjour Jabber XML parser functions
+ * @file parser.h Bonjour XMPP XML parser functions
  *
  * purple
  *
@@ -26,9 +26,9 @@
 #define PURPLE_BONJOUR_PARSER_H
 
 #include "buddy.h"
-#include "jabber.h"
+#include "xmpp.h"
 
-void bonjour_parser_setup(BonjourJabberConversation *bconv);
-void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len);
+void bonjour_parser_setup(BonjourXMPPConversation *bconv);
+void bonjour_parser_process(BonjourXMPPConversation *bconv, const char *buf, int len);
 
 #endif /* PURPLE_BONJOUR_PARSER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/xmpp.c	Sat Oct 26 16:53:30 2019 +0000
@@ -0,0 +1,1472 @@
+/*
+ * purple - Bonjour Protocol Plugin
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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
+ */
+
+#include "internal.h"
+#include <purple.h>
+
+#ifndef _WIN32
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <sys/types.h>
+
+/* Solaris */
+#if defined (__SVR4) && defined (__sun)
+#include <sys/sockio.h>
+#endif
+
+#include <glib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+#endif
+
+#include "xmpp.h"
+#include "parser.h"
+#include "bonjour.h"
+#include "buddy.h"
+#include "bonjour_ft.h"
+
+#ifdef _SIZEOF_ADDR_IFREQ
+#  define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
+#else
+#  define HX_SIZE_OF_IFREQ(a) sizeof(a)
+#endif
+
+#define STREAM_END "</stream:stream>"
+/* TODO: specify version='1.0' and send stream features */
+#define DOCTYPE "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" \
+		"<stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" from=\"%s\" to=\"%s\">"
+
+enum sent_stream_start_types {
+	NOT_SENT       = 0,
+	PARTIALLY_SENT = 1,
+	FULLY_SENT     = 2
+};
+
+static void
+xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb);
+
+static BonjourXMPPConversation *
+bonjour_xmpp_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) {
+
+	BonjourXMPPConversation *bconv = g_new0(BonjourXMPPConversation, 1);
+	bconv->cancellable = g_cancellable_new();
+	bconv->tx_buf = purple_circular_buffer_new(512);
+	bconv->tx_handler = 0;
+	bconv->rx_handler = 0;
+	bconv->pb = pb;
+	bconv->account = account;
+	bconv->ip = g_strdup(ip);
+
+	bonjour_parser_setup(bconv);
+
+	return bconv;
+}
+
+static const char *
+_font_size_ichat_to_purple(int size)
+{
+	if (size > 24) {
+		return "7";
+	} else if (size >= 21) {
+		return "6";
+	} else if (size >= 17) {
+		return "5";
+	} else if (size >= 14) {
+		return "4";
+	} else if (size >= 12) {
+		return "3";
+	} else if (size >= 10) {
+		return "2";
+	}
+
+	return "1";
+}
+
+static gchar *
+get_xmlnode_contents(PurpleXmlNode *node)
+{
+	gchar *contents;
+
+	contents = purple_xmlnode_to_str(node, NULL);
+
+	/* we just want the stuff inside <font></font>
+	 * There isn't stuff exposed in PurpleXmlNode.c to do this more cleanly. */
+
+	if (contents) {
+		char *bodystart = strchr(contents, '>');
+		char *bodyend = bodystart ? strrchr(bodystart, '<') : NULL;
+		if (bodystart && bodyend && (bodystart + 1) != bodyend) {
+			*bodyend = '\0';
+			memmove(contents, bodystart + 1, (bodyend - bodystart));
+		}
+	}
+
+	return contents;
+}
+
+static void
+_xmpp_parse_and_write_message_to_ui(PurpleXmlNode *message_node, PurpleBuddy *pb)
+{
+	PurpleXmlNode *body_node, *html_node, *events_node;
+	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(pb));
+	gchar *body = NULL;
+
+	body_node = purple_xmlnode_get_child(message_node, "body");
+	html_node = purple_xmlnode_get_child(message_node, "html");
+
+	if (body_node == NULL && html_node == NULL) {
+		purple_debug_error("bonjour", "No body or html node found, discarding message.\n");
+		return;
+	}
+
+	events_node = purple_xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
+	if (events_node != NULL) {
+		if (purple_xmlnode_get_child(events_node, "id") != NULL) {
+			/* The user is just typing */
+			/* TODO: Deal with typing notification */
+			return;
+		}
+	}
+
+	if (html_node != NULL) {
+		PurpleXmlNode *html_body_node;
+
+		html_body_node = purple_xmlnode_get_child(html_node, "body");
+		if (html_body_node != NULL) {
+			PurpleXmlNode *html_body_font_node;
+
+			html_body_font_node = purple_xmlnode_get_child(html_body_node, "font");
+			/* Types of messages sent by iChat */
+			if (html_body_font_node != NULL) {
+				gchar *html_body;
+				const char *font_face, *font_size, *font_color,
+					*ichat_balloon_color, *ichat_text_color;
+
+				font_face = purple_xmlnode_get_attrib(html_body_font_node, "face");
+				/* The absolute iChat font sizes should be converted to 1..7 range */
+				font_size = purple_xmlnode_get_attrib(html_body_font_node, "ABSZ");
+				if (font_size != NULL)
+					font_size = _font_size_ichat_to_purple(atoi(font_size));
+				font_color = purple_xmlnode_get_attrib(html_body_font_node, "color");
+				ichat_balloon_color = purple_xmlnode_get_attrib(html_body_node, "ichatballooncolor");
+				ichat_text_color = purple_xmlnode_get_attrib(html_body_node, "ichattextcolor");
+
+				html_body = get_xmlnode_contents(html_body_font_node);
+
+				if (html_body == NULL)
+					/* This is the kind of formatted messages that Purple creates */
+					html_body = purple_xmlnode_to_str(html_body_font_node, NULL);
+
+				if (html_body != NULL) {
+					GString *str = g_string_new("<font");
+
+					if (font_face)
+						g_string_append_printf(str, " face='%s'", font_face);
+					if (font_size)
+						g_string_append_printf(str, " size='%s'", font_size);
+					if (font_color)
+						g_string_append_printf(str, " color='%s'", font_color);
+					else if (ichat_text_color)
+						g_string_append_printf(str, " color='%s'", ichat_text_color);
+					if (ichat_balloon_color)
+						g_string_append_printf(str, " back='%s'", ichat_balloon_color);
+					g_string_append_printf(str, ">%s</font>", html_body);
+
+					body = g_string_free(str, FALSE);
+
+					g_free(html_body);
+				}
+			}
+		}
+	}
+
+	/* Compose the message */
+	if (body == NULL && body_node != NULL)
+		body = purple_xmlnode_get_data(body_node);
+
+	if (body == NULL) {
+		purple_debug_error("bonjour", "No html body or regular body found.\n");
+		return;
+	}
+
+	/* Send the message to the UI */
+	purple_serv_got_im(gc, purple_buddy_get_name(pb), body, 0, time(NULL));
+
+	g_free(body);
+}
+
+struct _match_buddies_by_address {
+	const char *address;
+	GSList *matched_buddies;
+};
+
+static void
+_match_buddies_by_address(gpointer value, gpointer data)
+{
+	PurpleBuddy *pb = value;
+	BonjourBuddy *bb = NULL;
+	struct _match_buddies_by_address *mbba = data;
+
+	bb = purple_buddy_get_protocol_data(pb);
+
+	/*
+	 * If the current PurpleBuddy's data is not null, then continue to determine
+	 * whether one of the buddies IPs matches the target IP.
+	 */
+	if (bb != NULL)
+	{
+		const char *ip;
+		GSList *tmp = bb->ips;
+
+		while(tmp) {
+			ip = tmp->data;
+			if (ip != NULL && g_ascii_strcasecmp(ip, mbba->address) == 0) {
+				mbba->matched_buddies = g_slist_prepend(mbba->matched_buddies, pb);
+				break;
+			}
+			tmp = tmp->next;
+		}
+	}
+}
+
+static void
+_send_data_write_cb(GObject *stream, gpointer data)
+{
+	PurpleBuddy *pb = data;
+	BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
+	BonjourXMPPConversation *bconv = bb->conversation;
+	gsize writelen;
+	gssize ret;
+	GError *error = NULL;
+
+	writelen = purple_circular_buffer_get_max_read(bconv->tx_buf);
+
+	if (writelen == 0) {
+		g_source_remove(bconv->tx_handler);
+		bconv->tx_handler = 0;
+		return;
+	}
+
+	ret = g_pollable_output_stream_write_nonblocking(
+	        G_POLLABLE_OUTPUT_STREAM(stream),
+	        purple_circular_buffer_get_output(bconv->tx_buf), writelen,
+	        bconv->cancellable, &error);
+
+	if (ret < 0 && error->code == G_IO_ERROR_WOULD_BLOCK) {
+		g_clear_error(&error);
+		return;
+	} else if (ret <= 0) {
+		PurpleConversation *conv = NULL;
+		PurpleAccount *account = NULL;
+
+		purple_debug_error(
+		        "bonjour",
+		        "Error sending message to buddy %s error: %s",
+		        purple_buddy_get_name(pb),
+		        error ? error->message : "(null)");
+
+		account = purple_buddy_get_account(pb);
+
+		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
+		if (conv != NULL)
+			purple_conversation_write_system_message(conv,
+				_("Unable to send message."),
+				PURPLE_MESSAGE_ERROR);
+
+		bonjour_xmpp_close_conversation(bb->conversation);
+		bb->conversation = NULL;
+		g_clear_error(&error);
+		return;
+	}
+
+	purple_circular_buffer_mark_read(bconv->tx_buf, ret);
+}
+
+static gint
+_send_data(PurpleBuddy *pb, char *message)
+{
+	BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
+	BonjourXMPPConversation *bconv = bb->conversation;
+	gsize len = strlen(message);
+	gssize ret;
+	GError *error = NULL;
+
+	/* If we're not ready to actually send, append it to the buffer */
+	if (bconv->tx_handler != 0
+			|| bconv->sent_stream_start != FULLY_SENT
+			|| !bconv->recv_stream_start
+			|| purple_circular_buffer_get_max_read(bconv->tx_buf) > 0) {
+		ret = -1;
+		g_set_error_literal(&error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
+		                    "Not yet ready to send.");
+	} else {
+		ret = g_pollable_output_stream_write_nonblocking(
+		        G_POLLABLE_OUTPUT_STREAM(bconv->output), message, len,
+		        bconv->cancellable, &error);
+	}
+
+	if (ret == -1 && error->code == G_IO_ERROR_WOULD_BLOCK) {
+		ret = 0;
+		g_clear_error(&error);
+	} else if (ret <= 0) {
+		PurpleConversation *conv;
+		PurpleAccount *account;
+
+		purple_debug_error(
+		        "bonjour",
+		        "Error sending message to buddy %s error: %s",
+		        purple_buddy_get_name(pb),
+		        error ? error->message : "(null)");
+
+		account = purple_buddy_get_account(pb);
+
+		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
+		if (conv != NULL)
+			purple_conversation_write_system_message(conv,
+				_("Unable to send message."),
+				PURPLE_MESSAGE_ERROR);
+
+		bonjour_xmpp_close_conversation(bb->conversation);
+		bb->conversation = NULL;
+		g_clear_error(&error);
+		return -1;
+	}
+
+	if (ret < len) {
+		/* Don't interfere with the stream starting */
+		if (bconv->sent_stream_start == FULLY_SENT &&
+		    bconv->recv_stream_start && bconv->tx_handler == 0) {
+			GSource *source =
+			        g_pollable_output_stream_create_source(
+			                G_POLLABLE_OUTPUT_STREAM(bconv->output),
+			                bconv->cancellable);
+			g_source_set_callback(source,
+			                      (GSourceFunc)_send_data_write_cb,
+			                      pb, NULL);
+			bconv->tx_handler = g_source_attach(source, NULL);
+		}
+		purple_circular_buffer_append(bconv->tx_buf, message + ret, len - ret);
+	}
+
+	return ret;
+}
+
+void bonjour_xmpp_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet) {
+
+	g_return_if_fail(packet != NULL);
+	g_return_if_fail(pb != NULL);
+
+	if (purple_strequal(packet->name, "message"))
+		_xmpp_parse_and_write_message_to_ui(packet, pb);
+	else if (purple_strequal(packet->name, "iq"))
+		xep_iq_parse(packet, pb);
+	else {
+		purple_debug_warning("bonjour", "Unknown packet: %s\n",
+			packet->name ? packet->name : "(null)");
+	}
+}
+
+static void bonjour_xmpp_stream_ended(BonjourXMPPConversation *bconv) {
+
+	/* Inform the user that the conversation has been closed */
+	BonjourBuddy *bb = NULL;
+	const gchar *name = bconv->pb ? purple_buddy_get_name(bconv->pb) : "(unknown)";
+
+	purple_debug_info("bonjour", "Received conversation close notification from %s.\n", name);
+
+	if(bconv->pb != NULL)
+		bb = purple_buddy_get_protocol_data(bconv->pb);
+
+	/* Close the socket, clear the watcher and free memory */
+	bonjour_xmpp_close_conversation(bconv);
+	if(bb)
+		bb->conversation = NULL;
+}
+
+static gboolean
+_client_socket_handler(GObject *stream, gpointer data)
+{
+	BonjourXMPPConversation *bconv = data;
+	GError *error = NULL;
+	gssize len;
+	static char message[4096];
+
+	/* Read the data from the socket */
+	len = g_pollable_input_stream_read_nonblocking(
+	        G_POLLABLE_INPUT_STREAM(stream), message, sizeof(message) - 1,
+	        bconv->cancellable, &error);
+	if (len == -1) {
+		/* There has been an error reading from the socket */
+		if (error == NULL || (error->code != G_IO_ERROR_WOULD_BLOCK &&
+		                      error->code != G_IO_ERROR_CANCELLED)) {
+			purple_debug_warning(
+			        "bonjour",
+			        "receive of %" G_GSSIZE_FORMAT " error: %s",
+			        len, error ? error->message : "(null)");
+
+			bonjour_xmpp_close_conversation(bconv);
+			if (bconv->pb != NULL) {
+				BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
+
+				if(bb != NULL)
+					bb->conversation = NULL;
+			}
+
+			/* I guess we really don't need to notify the user.
+			 * If they try to send another message it'll reconnect */
+		}
+		g_clear_error(&error);
+		return FALSE;
+	} else if (len == 0) { /* The other end has closed the socket */
+		const gchar *name = purple_buddy_get_name(bconv->pb);
+		purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (name) ? name : "(unknown)");
+		bonjour_xmpp_stream_ended(bconv);
+		return FALSE;
+	}
+
+	message[len] = '\0';
+
+	purple_debug_info("bonjour", "Receive: -%s- %" G_GSSIZE_FORMAT " bytes\n", message, len);
+	bonjour_parser_process(bconv, message, len);
+
+	return TRUE;
+}
+
+struct _stream_start_data {
+	char *msg;
+};
+
+static void
+_start_stream(GObject *stream, gpointer data)
+{
+	BonjourXMPPConversation *bconv = data;
+	struct _stream_start_data *ss = bconv->stream_data;
+	GError *error = NULL;
+	gsize len;
+	gssize ret;
+
+	len = strlen(ss->msg);
+
+	/* Start Stream */
+	ret = g_pollable_output_stream_write_nonblocking(
+	        G_POLLABLE_OUTPUT_STREAM(stream), ss->msg, len,
+	        bconv->cancellable, &error);
+
+	if (ret == -1 && error->code == G_IO_ERROR_WOULD_BLOCK) {
+		g_clear_error(&error);
+		return;
+	} else if (ret <= 0) {
+		PurpleConversation *conv;
+		const char *bname = bconv->buddy_name;
+		BonjourBuddy *bb = NULL;
+
+		if(bconv->pb) {
+			bb = purple_buddy_get_protocol_data(bconv->pb);
+			bname = purple_buddy_get_name(bconv->pb);
+		}
+
+		purple_debug_error(
+		        "bonjour",
+		        "Error starting stream with buddy %s at %s error: %s",
+		        bname ? bname : "(unknown)", bconv->ip,
+		        error ? error->message : "(null)");
+
+		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
+		if (conv != NULL)
+			purple_conversation_write_system_message(conv,
+				_("Unable to send the message, the conversation couldn't be started."),
+				PURPLE_MESSAGE_ERROR);
+
+		bonjour_xmpp_close_conversation(bconv);
+		if(bb != NULL)
+			bb->conversation = NULL;
+
+		g_clear_error(&error);
+		return;
+	}
+
+	/* This is EXTREMELY unlikely to happen */
+	if (G_UNLIKELY(ret < len)) {
+		char *tmp = g_strdup(ss->msg + ret);
+		g_free(ss->msg);
+		ss->msg = tmp;
+		return;
+	}
+
+	g_free(ss->msg);
+	g_free(ss);
+	bconv->stream_data = NULL;
+
+	/* Stream started; process the send buffer if there is one */
+	g_source_remove(bconv->tx_handler);
+	bconv->tx_handler = 0;
+	bconv->sent_stream_start = FULLY_SENT;
+
+	bonjour_xmpp_stream_started(bconv);
+}
+
+static gboolean
+bonjour_xmpp_send_stream_init(BonjourXMPPConversation *bconv,
+                                GError **error)
+{
+	gchar *stream_start;
+	gsize len;
+	gssize ret;
+	const char *bname = bconv->buddy_name;
+
+	g_return_val_if_fail(error != NULL, FALSE);
+
+	if (bconv->pb != NULL)
+		bname = purple_buddy_get_name(bconv->pb);
+
+	/* If we have no idea who "to" is, use an empty string.
+	 * If we don't know now, it is because the other side isn't playing nice, so they can't complain. */
+	if (bname == NULL)
+		bname = "";
+
+	stream_start = g_strdup_printf(DOCTYPE, bonjour_get_jid(bconv->account), bname);
+	len = strlen(stream_start);
+
+	bconv->sent_stream_start = PARTIALLY_SENT;
+
+	/* Start the stream */
+	ret = g_pollable_output_stream_write_nonblocking(
+	        G_POLLABLE_OUTPUT_STREAM(bconv->output), stream_start, len,
+	        bconv->cancellable, error);
+	if (ret == -1 && (*error)->code == G_IO_ERROR_WOULD_BLOCK) {
+		ret = 0;
+		g_clear_error(error);
+	} else if (ret <= 0) {
+		purple_debug_error(
+		        "bonjour",
+		        "Error starting stream with buddy %s at %s error: %s",
+		        (*bname) ? bname : "(unknown)", bconv->ip,
+		        *error ? (*error)->message : "(null)");
+
+		if (bconv->pb) {
+			PurpleConversation *conv;
+			conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
+			if (conv != NULL)
+				purple_conversation_write_system_message(conv,
+					_("Unable to send the message, the conversation couldn't be started."),
+					PURPLE_MESSAGE_ERROR);
+		}
+
+		purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
+		                          G_INPUT_STREAM(bconv->input),
+		                          G_OUTPUT_STREAM(bconv->output));
+		g_clear_object(&bconv->socket);
+		bconv->input = NULL;
+		bconv->output = NULL;
+		g_free(stream_start);
+
+		return FALSE;
+	}
+
+	/* This is unlikely to happen */
+	if (ret < len) {
+		GSource *source;
+		struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
+		ss->msg = g_strdup(stream_start + ret);
+		bconv->stream_data = ss;
+		/* Finish sending the stream start */
+		source = g_pollable_output_stream_create_source(
+		        G_POLLABLE_OUTPUT_STREAM(bconv->output),
+		        bconv->cancellable);
+		g_source_set_callback(source, (GSourceFunc)_start_stream, bconv,
+		                      NULL);
+		bconv->tx_handler = g_source_attach(source, NULL);
+	} else {
+		bconv->sent_stream_start = FULLY_SENT;
+	}
+
+	g_free(stream_start);
+
+	return TRUE;
+}
+
+/* This gets called when we've successfully sent our <stream:stream />
+ * AND when we've received a <stream:stream /> */
+void
+bonjour_xmpp_stream_started(BonjourXMPPConversation *bconv)
+{
+	GError *error = NULL;
+
+	if (bconv->sent_stream_start == NOT_SENT &&
+	    !bonjour_xmpp_send_stream_init(bconv, &error)) {
+		const char *bname = bconv->buddy_name;
+
+		if (bconv->pb)
+			bname = purple_buddy_get_name(bconv->pb);
+
+		purple_debug_error(
+		        "bonjour",
+		        "Error starting stream with buddy %s at %s error: %s",
+		        bname ? bname : "(unknown)", bconv->ip,
+		        error ? error->message : "(null)");
+
+		if (bconv->pb) {
+			PurpleConversation *conv;
+			conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
+			if (conv != NULL)
+				purple_conversation_write_system_message(conv,
+					_("Unable to send the message, the conversation couldn't be started."),
+					PURPLE_MESSAGE_ERROR);
+		}
+
+		/* We don't want to recieve anything else */
+		purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
+		                          G_INPUT_STREAM(bconv->input),
+		                          G_OUTPUT_STREAM(bconv->output));
+		g_clear_object(&bconv->socket);
+		bconv->input = NULL;
+		bconv->output = NULL;
+
+		/* This must be asynchronous because it destroys the parser and we
+		 * may be in the middle of parsing.
+		 */
+		async_bonjour_xmpp_close_conversation(bconv);
+		g_clear_error(&error);
+		return;
+	}
+
+	/* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
+	/* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
+	if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start
+			&& bconv->pb && purple_circular_buffer_get_max_read(bconv->tx_buf) > 0) {
+		/* Watch for when we can write the buffered messages */
+		GSource *source = g_pollable_output_stream_create_source(
+		        G_POLLABLE_OUTPUT_STREAM(bconv->output),
+		        bconv->cancellable);
+		g_source_set_callback(source, (GSourceFunc)_send_data_write_cb,
+		                      bconv->pb, NULL);
+		bconv->tx_handler = g_source_attach(source, NULL);
+		/* We can probably write the data right now. */
+		_send_data_write_cb(G_OBJECT(bconv->output), bconv->pb);
+	}
+}
+
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+static void
+_server_socket_handler(GSocketService *service, GSocketConnection *connection,
+                       GObject *source_object, gpointer data)
+{
+	BonjourXMPP *jdata = data;
+	GSocketAddress *their_addr; /* connector's address information */
+	GInetAddress *their_inet_addr;
+	gchar *address_text;
+	struct _match_buddies_by_address *mbba;
+	BonjourXMPPConversation *bconv;
+	GSList *buddies;
+	GSource *source;
+
+	their_addr = g_socket_connection_get_remote_address(connection, NULL);
+	if (their_addr == NULL) {
+		return;
+	}
+	their_inet_addr = g_inet_socket_address_get_address(
+	        G_INET_SOCKET_ADDRESS(their_addr));
+
+	/* Look for the buddy that has opened the conversation and fill information */
+	address_text = g_inet_address_to_string(their_inet_addr);
+	if (g_inet_address_get_family(their_inet_addr) ==
+	            G_SOCKET_FAMILY_IPV6 &&
+	    g_inet_address_get_is_link_local(their_inet_addr)) {
+		gchar *tmp = g_strdup_printf(
+		        "%s%%%d", address_text,
+		        g_inet_socket_address_get_scope_id(
+		                G_INET_SOCKET_ADDRESS(their_addr)));
+		g_free(address_text);
+		address_text = tmp;
+	}
+	g_object_unref(their_addr);
+
+	purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
+	mbba = g_new0(struct _match_buddies_by_address, 1);
+	mbba->address = address_text;
+
+	buddies = purple_blist_find_buddies(jdata->account, NULL);
+	g_slist_foreach(buddies, _match_buddies_by_address, mbba);
+	g_slist_free(buddies);
+
+	if (mbba->matched_buddies == NULL) {
+		purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
+		g_free(address_text);
+		g_free(mbba);
+		return;
+	}
+
+	g_slist_free(mbba->matched_buddies);
+	g_free(mbba);
+
+	/* We've established that this *could* be from one of our buddies.
+	 * Wait for the stream open to see if that matches too before assigning it.
+	 */
+	bconv = bonjour_xmpp_conv_new(NULL, jdata->account, address_text);
+
+	/* We wait for the stream start before doing anything else */
+	bconv->socket = g_object_ref(connection);
+	bconv->input = g_io_stream_get_input_stream(G_IO_STREAM(bconv->socket));
+	bconv->output =
+	        g_io_stream_get_output_stream(G_IO_STREAM(bconv->socket));
+	source = g_pollable_input_stream_create_source(
+	        G_POLLABLE_INPUT_STREAM(bconv->input), bconv->cancellable);
+	g_source_set_callback(source, (GSourceFunc)_client_socket_handler,
+	                      bconv, NULL);
+	bconv->rx_handler = g_source_attach(source, NULL);
+	g_free(address_text);
+}
+
+gint
+bonjour_xmpp_start(BonjourXMPP *jdata)
+{
+	GError *error = NULL;
+	guint16 port;
+
+	purple_debug_info("bonjour", "Attempting to bind IP socket to port %d.",
+	                  jdata->port);
+
+	/* Open a listening server for incoming conversations */
+	jdata->service = g_socket_service_new();
+	g_socket_listener_set_backlog(G_SOCKET_LISTENER(jdata->service), 10);
+	port = jdata->port;
+	if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(jdata->service),
+	                                     port, NULL, &error)) {
+		purple_debug_info("bonjour",
+		                  "Unable to bind to specified port %i: %s",
+		                  port, error ? error->message : "(unknown)");
+		g_clear_error(&error);
+		port = g_socket_listener_add_any_inet_port(
+		        G_SOCKET_LISTENER(jdata->service), NULL, &error);
+		if (port == 0) {
+			purple_debug_error(
+			        "bonjour", "Unable to create socket: %s",
+			        error ? error->message : "(unknown)");
+			g_clear_error(&error);
+			return -1;
+		}
+	}
+	purple_debug_info("bonjour", "Bound IP socket to port %u.", port);
+	jdata->port = port;
+
+	g_signal_connect(G_OBJECT(jdata->service), "incoming",
+	                 G_CALLBACK(_server_socket_handler), jdata);
+
+	return jdata->port;
+}
+
+static void
+_connected_to_buddy(GObject *source, GAsyncResult *res, gpointer user_data)
+{
+	PurpleBuddy *pb = user_data;
+	BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
+	GSocketConnection *conn;
+	GSource *rx_source;
+	GError *error = NULL;
+
+	conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
+	                                              res, &error);
+
+	if (conn == NULL) {
+		PurpleConversation *conv = NULL;
+		PurpleAccount *account = NULL;
+		GSList *tmp;
+
+		if (error && error->code == G_IO_ERROR_CANCELLED) {
+			/* This conversation was closed before it started. */
+			g_error_free(error);
+			return;
+		}
+
+		purple_debug_error("bonjour",
+		                   "Error connecting to buddy %s at %s:%d "
+		                   "(%s); Trying next IP address",
+		                   purple_buddy_get_name(pb),
+		                   bb->conversation->ip, bb->port_p2pj,
+		                   error ? error->message : "(unknown)");
+		g_clear_error(&error);
+
+		/* There may be multiple entries for the same IP - one per
+		 * presence recieved (e.g. multiple interfaces).
+		 * We need to make sure that we find the previously used entry.
+		 */
+		tmp = g_slist_find(bb->ips, bb->conversation->ip_link);
+		if (tmp)
+			tmp = g_slist_next(tmp);
+
+		account = purple_buddy_get_account(pb);
+
+		if (tmp != NULL) {
+			const gchar *ip;
+			GSocketClient *client;
+
+			bb->conversation->ip_link = ip = tmp->data;
+
+			purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n",
+					  purple_buddy_get_name(pb), ip, bb->port_p2pj);
+
+			/* Make sure to connect without a proxy. */
+			client = g_socket_client_new();
+			if (client != NULL) {
+				g_free(bb->conversation->ip);
+				bb->conversation->ip = g_strdup(ip);
+				g_socket_client_connect_to_host_async(
+				        client, ip, bb->port_p2pj,
+				        bb->conversation->cancellable,
+				        _connected_to_buddy, pb);
+				g_object_unref(client);
+				return;
+			}
+		}
+
+		purple_debug_error("bonjour", "No more addresses for buddy %s. Aborting", purple_buddy_get_name(pb));
+
+		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
+		if (conv != NULL)
+			purple_conversation_write_system_message(conv,
+				_("Unable to send the message, the conversation couldn't be started."),
+				PURPLE_MESSAGE_ERROR);
+
+		bonjour_xmpp_close_conversation(bb->conversation);
+		bb->conversation = NULL;
+		return;
+	}
+
+	bb->conversation->socket = conn;
+	bb->conversation->input =
+	        g_io_stream_get_input_stream(G_IO_STREAM(conn));
+	bb->conversation->output =
+	        g_io_stream_get_output_stream(G_IO_STREAM(conn));
+
+	if (!bonjour_xmpp_send_stream_init(bb->conversation, &error)) {
+		PurpleConversation *conv = NULL;
+		PurpleAccount *account = NULL;
+
+		purple_debug_error("bonjour",
+		                   "Error starting stream with buddy %s at "
+		                   "%s:%d error: %s",
+		                   purple_buddy_get_name(pb),
+		                   bb->conversation->ip, bb->port_p2pj,
+		                   error ? error->message : "(null)");
+
+		account = purple_buddy_get_account(pb);
+
+		conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bb->name, account));
+		if (conv != NULL)
+			purple_conversation_write_system_message(conv,
+				_("Unable to send the message, the conversation couldn't be started."),
+				PURPLE_MESSAGE_ERROR);
+
+		bonjour_xmpp_close_conversation(bb->conversation);
+		bb->conversation = NULL;
+		g_clear_error(&error);
+		return;
+	}
+
+	/* Start listening for the stream acknowledgement */
+	rx_source = g_pollable_input_stream_create_source(
+	        G_POLLABLE_INPUT_STREAM(bb->conversation->input),
+	        bb->conversation->cancellable);
+	g_source_set_callback(rx_source, (GSourceFunc)_client_socket_handler,
+	                      bb->conversation, NULL);
+	bb->conversation->rx_handler = g_source_attach(rx_source, NULL);
+}
+
+void
+bonjour_xmpp_conv_match_by_name(BonjourXMPPConversation *bconv) {
+	PurpleBuddy *pb = NULL;
+	BonjourBuddy *bb = NULL;
+
+	g_return_if_fail(bconv->ip != NULL);
+	g_return_if_fail(bconv->pb == NULL);
+
+	pb = purple_blist_find_buddy(bconv->account, bconv->buddy_name);
+	if (pb && (bb = purple_buddy_get_protocol_data(pb))) {
+		const char *ip;
+		GSList *tmp = bb->ips;
+
+		purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
+			purple_buddy_get_name(pb));
+
+		/* Check that one of the buddy's IPs matches */
+		while(tmp) {
+			ip = tmp->data;
+			if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
+				PurpleConnection *pc = purple_account_get_connection(bconv->account);
+				BonjourData *bd = purple_connection_get_protocol_data(pc);
+				BonjourXMPP *jdata = bd->xmpp_data;
+
+				purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
+					purple_buddy_get_name(pb), bconv->ip);
+
+				/* Attach conv. to buddy and remove from pending list */
+				jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
+
+				/* Check if the buddy already has a conversation and, if so, replace it */
+				if(bb->conversation != NULL && bb->conversation != bconv)
+					bonjour_xmpp_close_conversation(bb->conversation);
+
+				bconv->pb = pb;
+				bb->conversation = bconv;
+
+				break;
+			}
+			tmp = tmp->next;
+		}
+	}
+
+	/* We've failed to match a buddy - give up */
+	if (bconv->pb == NULL) {
+		/* This must be asynchronous because it destroys the parser and we
+		 * may be in the middle of parsing.
+		 */
+		async_bonjour_xmpp_close_conversation(bconv);
+	}
+}
+
+
+void
+bonjour_xmpp_conv_match_by_ip(BonjourXMPPConversation *bconv) {
+	PurpleConnection *pc = purple_account_get_connection(bconv->account);
+	BonjourData *bd = purple_connection_get_protocol_data(pc);
+	BonjourXMPP *jdata = bd->xmpp_data;
+	struct _match_buddies_by_address *mbba;
+	GSList *buddies;
+
+	mbba = g_new0(struct _match_buddies_by_address, 1);
+	mbba->address = bconv->ip;
+
+	buddies = purple_blist_find_buddies(jdata->account, NULL);
+	g_slist_foreach(buddies, _match_buddies_by_address, mbba);
+	g_slist_free(buddies);
+
+	/* If there is exactly one match, use it */
+	if(mbba->matched_buddies != NULL) {
+		if(mbba->matched_buddies->next != NULL)
+			purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip);
+		else {
+			PurpleBuddy *pb = mbba->matched_buddies->data;
+			BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
+
+			purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
+				purple_buddy_get_name(pb), bconv->ip);
+
+			/* Attach conv. to buddy and remove from pending list */
+			jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
+
+			/* Check if the buddy already has a conversation and, if so, replace it */
+			if (bb->conversation != NULL && bb->conversation != bconv)
+				bonjour_xmpp_close_conversation(bb->conversation);
+
+			bconv->pb = pb;
+			bb->conversation = bconv;
+		}
+	} else
+		purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv->ip);
+
+	/* We've failed to match a buddy - give up */
+	if (bconv->pb == NULL) {
+		/* This must be asynchronous because it destroys the parser and we
+		 * may be in the middle of parsing.
+		 */
+		async_bonjour_xmpp_close_conversation(bconv);
+	}
+
+	g_slist_free(mbba->matched_buddies);
+	g_free(mbba);
+}
+
+static PurpleBuddy *
+_find_or_start_conversation(BonjourXMPP *jdata, const gchar *to)
+{
+	PurpleBuddy *pb = NULL;
+	BonjourBuddy *bb = NULL;
+
+	g_return_val_if_fail(jdata != NULL, NULL);
+	g_return_val_if_fail(to != NULL, NULL);
+
+	pb = purple_blist_find_buddy(jdata->account, to);
+	if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL)
+		/* You can not send a message to an offline buddy */
+		return NULL;
+
+	/* Check if there is a previously open conversation */
+	if (bb->conversation == NULL) {
+		GSocketClient *client;
+		/* Start with the first IP address. */
+		const gchar *ip = bb->ips->data;
+
+		purple_debug_info("bonjour",
+		                  "Starting conversation with %s at %s:%d", to,
+		                  ip, bb->port_p2pj);
+
+		/* Make sure to connect without a proxy. */
+		client = g_socket_client_new();
+		if (client == NULL) {
+			purple_debug_error("bonjour",
+			                   "Unable to connect to buddy (%s).",
+			                   to);
+			return NULL;
+		}
+
+		bb->conversation = bonjour_xmpp_conv_new(pb, jdata->account, ip);
+		bb->conversation->ip_link = ip;
+
+		g_socket_client_connect_to_host_async(
+		        client, ip, bb->port_p2pj,
+		        bb->conversation->cancellable, _connected_to_buddy, pb);
+		g_object_unref(client);
+	}
+	return pb;
+}
+
+int
+bonjour_xmpp_send_message(BonjourXMPP *jdata, const gchar *to, const gchar *body)
+{
+	PurpleXmlNode *message_node, *node, *node2;
+	gchar *message, *xhtml;
+	PurpleBuddy *pb;
+	BonjourBuddy *bb;
+	int ret;
+
+	pb = _find_or_start_conversation(jdata, to);
+	if (pb == NULL || (bb = purple_buddy_get_protocol_data(pb)) == NULL) {
+		purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
+		/* You can not send a message to an offline buddy */
+		return -10000;
+	}
+
+	purple_markup_html_to_xhtml(body, &xhtml, &message);
+
+	message_node = purple_xmlnode_new("message");
+	purple_xmlnode_set_attrib(message_node, "to", bb->name);
+	purple_xmlnode_set_attrib(message_node, "from", bonjour_get_jid(jdata->account));
+	purple_xmlnode_set_attrib(message_node, "type", "chat");
+
+	/* Enclose the message from the UI within a "font" node */
+	node = purple_xmlnode_new_child(message_node, "body");
+	purple_xmlnode_insert_data(node, message, strlen(message));
+	g_free(message);
+
+	node = purple_xmlnode_new_child(message_node, "html");
+	purple_xmlnode_set_namespace(node, "http://www.w3.org/1999/xhtml");
+
+	node = purple_xmlnode_new_child(node, "body");
+	message = g_strdup_printf("<font>%s</font>", xhtml);
+	node2 = purple_xmlnode_from_str(message, strlen(message));
+	g_free(xhtml);
+	g_free(message);
+	purple_xmlnode_insert_child(node, node2);
+
+	node = purple_xmlnode_new_child(message_node, "x");
+	purple_xmlnode_set_namespace(node, "jabber:x:event");
+	purple_xmlnode_insert_child(node, purple_xmlnode_new("composing"));
+
+	message = purple_xmlnode_to_str(message_node, NULL);
+	purple_xmlnode_free(message_node);
+
+	ret = _send_data(pb, message) >= 0;
+
+	g_free(message);
+
+	return ret;
+}
+
+static gboolean
+_async_bonjour_xmpp_close_conversation_cb(gpointer data) {
+	BonjourXMPPConversation *bconv = data;
+	bonjour_xmpp_close_conversation(bconv);
+	return FALSE;
+}
+
+void
+async_bonjour_xmpp_close_conversation(BonjourXMPPConversation *bconv) {
+	PurpleConnection *pc = purple_account_get_connection(bconv->account);
+	BonjourData *bd = purple_connection_get_protocol_data(pc);
+	BonjourXMPP *jdata = bd->xmpp_data;
+
+	jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
+
+	/* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
+	if(bconv->pb != NULL) {
+		BonjourBuddy *bb = purple_buddy_get_protocol_data(bconv->pb);
+		if (bb->conversation == bconv)
+			bb->conversation = NULL;
+	}
+
+	bconv->close_timeout = g_timeout_add(0, _async_bonjour_xmpp_close_conversation_cb, bconv);
+}
+
+void
+bonjour_xmpp_close_conversation(BonjourXMPPConversation *bconv)
+{
+	BonjourData *bd = NULL;
+	PurpleConnection *pc = NULL;
+
+	if (bconv == NULL) {
+		return;
+	}
+
+	pc = purple_account_get_connection(bconv->account);
+	PURPLE_ASSERT_CONNECTION_IS_VALID(pc);
+
+	bd = purple_connection_get_protocol_data(pc);
+	if (bd) {
+		bd->xmpp_data->pending_conversations = g_slist_remove(
+			bd->xmpp_data->pending_conversations, bconv);
+	}
+
+	/* Cancel any file transfers that are waiting to begin */
+	/* There wont be any transfers if it hasn't been attached to a buddy */
+	if (bconv->pb != NULL && bd != NULL) {
+		GSList *xfers, *tmp_next;
+		xfers = bd->xfer_lists;
+		while (xfers != NULL) {
+			PurpleXfer *xfer = xfers->data;
+			tmp_next = xfers->next;
+			/* We only need to cancel this if it hasn't actually started transferring. */
+			/* This will change if we ever support IBB transfers. */
+			if (purple_strequal(purple_xfer_get_remote_user(xfer), purple_buddy_get_name(bconv->pb))
+					&& (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
+						|| purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
+				purple_xfer_cancel_remote(xfer);
+			}
+			xfers = tmp_next;
+		}
+	}
+
+	/* Close the socket and remove the watcher */
+	if (bconv->socket != NULL) {
+		/* Send the end of the stream to the other end of the conversation */
+		if (bconv->sent_stream_start == FULLY_SENT) {
+			size_t len = strlen(STREAM_END);
+			if (g_pollable_output_stream_write_nonblocking(
+			            G_POLLABLE_OUTPUT_STREAM(bconv->output),
+			            STREAM_END, len, bconv->cancellable,
+			            NULL) != (gssize)len) {
+				purple_debug_error("bonjour",
+					"bonjour_xmpp_close_conversation: "
+					"couldn't send data\n");
+			}
+		}
+		/* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
+		purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
+		                          G_INPUT_STREAM(bconv->input),
+		                          G_OUTPUT_STREAM(bconv->output));
+	}
+	if (bconv->rx_handler != 0) {
+		g_source_remove(bconv->rx_handler);
+		bconv->rx_handler = 0;
+	}
+	if (bconv->tx_handler != 0) {
+		g_source_remove(bconv->tx_handler);
+		bconv->tx_handler = 0;
+	}
+
+	/* Cancel any pending operations. */
+	if (bconv->cancellable != NULL) {
+		g_cancellable_cancel(bconv->cancellable);
+		g_clear_object(&bconv->cancellable);
+	}
+
+	/* Free all the data related to the conversation */
+	g_clear_object(&bconv->socket);
+	bconv->input = NULL;
+	bconv->output = NULL;
+
+	g_object_unref(G_OBJECT(bconv->tx_buf));
+	if (bconv->stream_data != NULL) {
+		struct _stream_start_data *ss = bconv->stream_data;
+		g_free(ss->msg);
+		g_free(ss);
+	}
+
+	if (bconv->context != NULL) {
+		bonjour_parser_setup(bconv);
+	}
+
+	if (bconv->close_timeout != 0) {
+		g_source_remove(bconv->close_timeout);
+	}
+
+	g_free(bconv->buddy_name);
+	g_free(bconv->ip);
+	g_free(bconv);
+}
+
+void
+bonjour_xmpp_stop(BonjourXMPP *jdata)
+{
+	/* Close the server socket and remove the watcher */
+	if (jdata->service) {
+		g_socket_service_stop(jdata->service);
+		g_socket_listener_close(G_SOCKET_LISTENER(jdata->service));
+		g_clear_object(&jdata->service);
+	}
+
+	/* Close all the conversation sockets and remove all the watchers after sending end streams */
+	if (!purple_account_is_disconnected(jdata->account)) {
+		GSList *buddies, *l;
+
+		buddies = purple_blist_find_buddies(jdata->account, NULL);
+		for (l = buddies; l; l = l->next) {
+			BonjourBuddy *bb = purple_buddy_get_protocol_data((PurpleBuddy*) l->data);
+			if (bb && bb->conversation) {
+				/* Any ongoing connection attempt is cancelled
+				 * when a connection is destroyed */
+				bonjour_xmpp_close_conversation(bb->conversation);
+				bb->conversation = NULL;
+			}
+		}
+
+		g_slist_free(buddies);
+	}
+
+	g_slist_free_full(jdata->pending_conversations, (GDestroyNotify)bonjour_xmpp_close_conversation);
+}
+
+XepIq *
+xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
+{
+	PurpleXmlNode *iq_node = NULL;
+	XepIq *iq = NULL;
+
+	g_return_val_if_fail(data != NULL, NULL);
+	g_return_val_if_fail(to != NULL, NULL);
+	g_return_val_if_fail(id != NULL, NULL);
+
+	iq_node = purple_xmlnode_new("iq");
+
+	purple_xmlnode_set_attrib(iq_node, "to", to);
+	purple_xmlnode_set_attrib(iq_node, "from", from);
+	purple_xmlnode_set_attrib(iq_node, "id", id);
+	switch (type) {
+		case XEP_IQ_SET:
+			purple_xmlnode_set_attrib(iq_node, "type", "set");
+			break;
+		case XEP_IQ_GET:
+			purple_xmlnode_set_attrib(iq_node, "type", "get");
+			break;
+		case XEP_IQ_RESULT:
+			purple_xmlnode_set_attrib(iq_node, "type", "result");
+			break;
+		case XEP_IQ_ERROR:
+			purple_xmlnode_set_attrib(iq_node, "type", "error");
+			break;
+		case XEP_IQ_NONE:
+		default:
+			purple_xmlnode_set_attrib(iq_node, "type", "none");
+			break;
+	}
+
+	iq = g_new0(XepIq, 1);
+	iq->node = iq_node;
+	iq->type = type;
+	iq->data = ((BonjourData*)data)->xmpp_data;
+	iq->to = (char*)to;
+
+	return iq;
+}
+
+static gboolean
+check_if_blocked(PurpleBuddy *pb)
+{
+	gboolean blocked = FALSE;
+	GSList *l = NULL;
+	PurpleAccount *acc = purple_buddy_get_account(pb);
+	const gchar *name;
+
+	if(acc == NULL)
+		return FALSE;
+
+	l = purple_account_privacy_get_denied(acc);
+	name = purple_buddy_get_name(pb);
+
+	if(g_slist_find_custom(l, name, (GCompareFunc)purple_utf8_strcasecmp) != NULL) {
+		const gchar *username = bonjour_get_jid(acc);
+
+		purple_debug_info("bonjour", "%s has been blocked by %s.\n", name, username);
+		blocked = TRUE;
+	}
+	return blocked;
+}
+
+static void
+xep_iq_parse(PurpleXmlNode *packet, PurpleBuddy *pb)
+{
+	PurpleAccount *account;
+	PurpleConnection *gc;
+
+	if(check_if_blocked(pb))
+		return;
+
+	account = purple_buddy_get_account(pb);
+	gc = purple_account_get_connection(account);
+
+	if (purple_xmlnode_get_child(packet, "si") != NULL || purple_xmlnode_get_child(packet, "error") != NULL)
+		xep_si_parse(gc, packet, pb);
+	else
+		xep_bytestreams_parse(gc, packet, pb);
+}
+
+int
+xep_iq_send_and_free(XepIq *iq)
+{
+	int ret = -1;
+	PurpleBuddy *pb = NULL;
+
+	/* start the talk, reuse the message socket  */
+	pb = _find_or_start_conversation((BonjourXMPP*) iq->data, iq->to);
+	/* Send the message */
+	if (pb != NULL) {
+		/* Convert xml node into stream */
+		gchar *msg = purple_xmlnode_to_str(iq->node, NULL);
+		ret = _send_data(pb, msg);
+		g_free(msg);
+	}
+
+	purple_xmlnode_free(iq->node);
+	iq->node = NULL;
+	g_free(iq);
+
+	return (ret >= 0) ? 0 : -1;
+}
+
+/* This returns a list containing all non-localhost IPs */
+GSList *
+bonjour_xmpp_get_local_ips(int fd)
+{
+	GSList *ips = NULL;
+	const char *address_text;
+	int ret;
+
+#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
+	struct ifaddrs *ifap, *ifa;
+	common_sockaddr_t addr;
+	char addrstr[INET6_ADDRSTRLEN];
+
+	ret = getifaddrs(&ifap);
+	if (ret != 0) {
+		const char *error = g_strerror(errno);
+		purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)");
+		return NULL;
+	}
+
+	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+		if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL)
+			continue;
+
+		memcpy(&addr, ifa->ifa_addr, sizeof(addr));
+		address_text = NULL;
+		switch (addr.sa.sa_family) {
+			case AF_INET:
+				address_text = inet_ntop(addr.sa.sa_family,
+					&addr.in.sin_addr,
+					addrstr, sizeof(addrstr));
+				break;
+#ifdef PF_INET6
+			case AF_INET6:
+				address_text = inet_ntop(addr.sa.sa_family,
+					&addr.in6.sin6_addr,
+					addrstr, sizeof(addrstr));
+				break;
+#endif
+		}
+
+		if (address_text != NULL) {
+			if (addr.sa.sa_family == AF_INET)
+				ips = g_slist_append(ips, g_strdup(address_text));
+			else
+				ips = g_slist_prepend(ips, g_strdup(address_text));
+		}
+	}
+
+	freeifaddrs(ifap);
+#else
+	char *tmp;
+	struct ifconf ifc;
+	struct ifreq *ifr;
+	char buffer[1024];
+	struct sockaddr_in *sinptr;
+	int source = fd;
+
+	if (fd < 0)
+		source = socket(PF_INET, SOCK_STREAM, 0);
+
+	ifc.ifc_len = sizeof(buffer);
+	ifc.ifc_req = (struct ifreq *)buffer;
+	ret = ioctl(source, SIOCGIFCONF, &ifc);
+
+	if (fd < 0)
+		close(source);
+
+	if (ret < 0) {
+		const char *error = g_strerror(errno);
+		purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)");
+		return NULL;
+	}
+
+	tmp = buffer;
+	while (tmp < buffer + ifc.ifc_len) {
+		ifr = (struct ifreq *)tmp;
+		tmp += HX_SIZE_OF_IFREQ(*ifr);
+
+		if (ifr->ifr_addr.sa_family == AF_INET) {
+			sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
+			if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) {
+				address_text = inet_ntoa(sinptr->sin_addr);
+				ips = g_slist_prepend(ips, g_strdup(address_text));
+			}
+		}
+	}
+#endif
+
+	return ips;
+}
+
+void
+append_iface_if_linklocal(char *ip, guint32 interface_param) {
+	struct in6_addr in6_addr;
+	int len_remain = INET6_ADDRSTRLEN - strlen(ip);
+
+	if (len_remain <= 1)
+		return;
+
+	if (inet_pton(AF_INET6, ip, &in6_addr) != 1 ||
+	    !IN6_IS_ADDR_LINKLOCAL(&in6_addr))
+		return;
+
+	snprintf(ip + strlen(ip), len_remain, "%%%d",
+		 interface_param);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/xmpp.h	Sat Oct 26 16:53:30 2019 +0000
@@ -0,0 +1,112 @@
+/**
+ * @file xmpp.h The Purple interface to mDNS and peer to peer XMPP.
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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
+ *
+ */
+
+#ifndef PURPLE_BONJOUR_XMPP_H
+#define PURPLE_BONJOUR_XMPP_H
+
+#include <libxml/parser.h>
+
+#include <purple.h>
+
+typedef struct
+{
+	GSocketService *service;
+	guint16 port;
+	PurpleAccount *account;
+	GSList *pending_conversations;
+} BonjourXMPP;
+
+typedef struct
+{
+	GCancellable *cancellable;
+	GSocketConnection *socket;
+	GInputStream *input;
+	GOutputStream *output;
+	guint rx_handler;
+	guint tx_handler;
+	guint close_timeout;
+	PurpleCircularBuffer *tx_buf;
+	int sent_stream_start; /* 0 = Unsent, 1 = Partial, 2 = Complete */
+	gboolean recv_stream_start;
+	gpointer stream_data;
+	xmlParserCtxt *context;
+	PurpleXmlNode *current;
+	PurpleBuddy *pb;
+	PurpleAccount *account;
+
+	/* The following are only needed before attaching to a PurpleBuddy */
+	gchar *buddy_name;
+	gchar *ip;
+	/* This points to a data entry in BonjourBuddy->ips */
+	const gchar *ip_link;
+} BonjourXMPPConversation;
+
+/**
+ * Start listening for xmpp connections.
+ *
+ * @return -1 if there was a problem, else returns the listening
+ *         port number.
+ */
+gint bonjour_xmpp_start(BonjourXMPP *data);
+
+int bonjour_xmpp_send_message(BonjourXMPP *data, const char *to, const char *body);
+
+void bonjour_xmpp_close_conversation(BonjourXMPPConversation *bconv);
+
+void async_bonjour_xmpp_close_conversation(BonjourXMPPConversation *bconv);
+
+void bonjour_xmpp_stream_started(BonjourXMPPConversation *bconv);
+
+void bonjour_xmpp_process_packet(PurpleBuddy *pb, PurpleXmlNode *packet);
+
+void bonjour_xmpp_stop(BonjourXMPP *data);
+
+void bonjour_xmpp_conv_match_by_ip(BonjourXMPPConversation *bconv);
+
+void bonjour_xmpp_conv_match_by_name(BonjourXMPPConversation *bconv);
+
+typedef enum {
+	XEP_IQ_SET,
+	XEP_IQ_GET,
+	XEP_IQ_RESULT,
+	XEP_IQ_ERROR,
+	XEP_IQ_NONE
+} XepIqType;
+
+typedef struct {
+	XepIqType type;
+	char *id;
+	PurpleXmlNode *node;
+	char *to;
+	void *data;
+} XepIq;
+
+XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id);
+int xep_iq_send_and_free(XepIq *iq);
+GSList * bonjour_xmpp_get_local_ips(int fd);
+
+void append_iface_if_linklocal(char *ip, guint32 interface_param);
+
+#endif /* PURPLE_BONJOUR_XMPP_H */
--- a/po/POTFILES.in	Fri Oct 25 05:52:30 2019 -0400
+++ b/po/POTFILES.in	Sat Oct 26 16:53:30 2019 +0000
@@ -98,11 +98,11 @@
 libpurple/protocols/bonjour/bonjour_ft.c
 libpurple/protocols/bonjour/buddy.c
 libpurple/protocols/bonjour/dns_sd_proxy.c
-libpurple/protocols/bonjour/jabber.c
 libpurple/protocols/bonjour/mdns_avahi.c
 libpurple/protocols/bonjour/mdns_common.c
 libpurple/protocols/bonjour/mdns_dns_sd.c
 libpurple/protocols/bonjour/parser.c
+libpurple/protocols/bonjour/xmpp.c
 libpurple/protocols.c
 libpurple/protocols/facebook/api.c
 libpurple/protocols/facebook/data.c

mercurial