changeset 18761:316be7e715c6

Update the Bonjour prpl to use libxml explicitly instead of the xml_node stuff. This allows us to deal with partial reads. I also fixed issues related to starting conversations with iChat and a couple other things. Fixes #2022,#1652
author Daniel Atallah <daniel.atallah@gmail.com>
date Tue, 31 Jul 2007 23:23:25 +0000
parents bf47d0401a96
children a582b09109bc
files libpurple/protocols/bonjour/Makefile.am libpurple/protocols/bonjour/Makefile.mingw libpurple/protocols/bonjour/jabber.c libpurple/protocols/bonjour/jabber.h libpurple/protocols/bonjour/mdns_win32.c libpurple/protocols/bonjour/parser.c libpurple/protocols/bonjour/parser.h
diffstat 7 files changed, 386 insertions(+), 169 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/bonjour/Makefile.am	Tue Jul 31 23:20:56 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.am	Tue Jul 31 23:23:25 2007 +0000
@@ -17,7 +17,9 @@
 	mdns_common.h \
 	mdns_howl.c \
 	mdns_howl.h \
-	mdns_types.h
+	mdns_types.h \
+	parser.c \
+	parser.h
 
 AM_CFLAGS = $(st) -DUSE_BONJOUR_HOWL
 
@@ -36,7 +38,7 @@
 st =
 pkg_LTLIBRARIES       = libbonjour.la
 libbonjour_la_SOURCES = $(BONJOURSOURCES)
-libbonjour_la_LIBADD   = $(GLIB_LIBS) $(HOWL_LIBS)
+libbonjour_la_LIBADD   = $(GLIB_LIBS) $(HOWL_LIBS) $(LIBXML_LIBS)
 
 endif
 
@@ -46,4 +48,5 @@
 	-I$(top_builddir)/libpurple \
 	$(GLIB_CFLAGS) \
 	$(DEBUG_CFLAGS) \
-	$(HOWL_CFLAGS)
+	$(HOWL_CFLAGS) \
+	$(LIBXML_CFLAGS)
--- a/libpurple/protocols/bonjour/Makefile.mingw	Tue Jul 31 23:20:56 2007 +0000
+++ b/libpurple/protocols/bonjour/Makefile.mingw	Tue Jul 31 23:23:25 2007 +0000
@@ -30,12 +30,14 @@
 			-I$(GTK_TOP)/include/glib-2.0 \
 			-I$(GTK_TOP)/lib/glib-2.0/include \
 			-I$(BONJOUR_TOP)/include \
+			-I$(LIBXML2_TOP)/include \
 			-I$(PURPLE_TOP) \
 			-I$(PURPLE_TOP)/win32 \
 			-I$(PIDGIN_TREE_TOP)
 
 LIB_PATHS +=		-L$(GTK_TOP)/lib \
 			-L$(BONJOUR_TOP)/lib \
+			-L$(LIBXML2_TOP)/lib \
 			-L$(PURPLE_TOP)
 
 ##
@@ -45,6 +47,7 @@
 			buddy.c \
 			mdns_common.c \
 			mdns_win32.c \
+			parser.c \
 			jabber.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
@@ -58,6 +61,7 @@
 			-lintl \
 			-ldnssd \
 			-lnetapi32 \
+			-lxml2 \
 			-lpurple
 
 include $(PIDGIN_COMMON_RULES)
--- a/libpurple/protocols/bonjour/jabber.c	Tue Jul 31 23:20:56 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.c	Tue Jul 31 23:23:25 2007 +0000
@@ -42,6 +42,7 @@
 #include "util.h"
 
 #include "jabber.h"
+#include "parser.h"
 #include "bonjour.h"
 #include "buddy.h"
 
@@ -109,9 +110,10 @@
 }
 
 static void
-_jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleConnection *connection, PurpleBuddy *pb)
+_jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleBuddy *pb)
 {
 	xmlnode *body_node, *html_node, *events_node;
+	PurpleConnection *gc = pb->account->gc;
 	char *body, *html_body = NULL;
 	const char *ichat_balloon_color = NULL;
 	const char *ichat_text_color = NULL;
@@ -186,7 +188,7 @@
 	/* TODO: Should we do something with "composing_event" here? */
 
 	/* Send the message to the UI */
-	serv_got_im(connection, pb->name, body, 0, time(NULL));
+	serv_got_im(gc, pb->name, body, 0, time(NULL));
 
 	g_free(body);
 	g_free(html_body);
@@ -218,37 +220,6 @@
 	}
 }
 
-static gint
-_read_data(gint socket, char **message)
-{
-	GString *data = g_string_new("");
-	char partial_data[512];
-	gint total_message_length = 0;
-	gint partial_message_length = 0;
-
-	/* Read chunks of 512 bytes till the end of the data */
-	while ((partial_message_length = recv(socket, partial_data, 512, 0)) > 0)
-	{
-		g_string_append_len(data, partial_data, partial_message_length);
-		total_message_length += partial_message_length;
-	}
-
-	if (partial_message_length == -1)
-	{
-		if (errno != EAGAIN)
-			purple_debug_warning("bonjour", "receive error: %s\n", strerror(errno));
-		if (total_message_length == 0) {
-			return -1;
-		}
-	}
-
-	*message = g_string_free(data, FALSE);
-	if (total_message_length != 0)
-		purple_debug_info("bonjour", "Receive: -%s- %d bytes\n", *message, total_message_length);
-
-	return total_message_length;
-}
-
 static void
 _send_data_write_cb(gpointer data, gint source, PurpleInputCondition cond)
 {
@@ -303,7 +274,8 @@
 	/* If we're not ready to actually send, append it to the buffer */
 	if (bconv->tx_handler != -1
 			|| bconv->connect_data != NULL
-			|| !bconv->stream_started
+			|| !bconv->sent_stream_start
+			|| !bconv->recv_stream_start
 			|| purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
 		ret = -1;
 		errno = EAGAIN;
@@ -341,20 +313,32 @@
 	return ret;
 }
 
+void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) {
+	if (!strcmp(packet->name, "message"))
+		_jabber_parse_and_write_message_to_ui(packet, pb);
+	else
+		purple_debug_warning("bonjour", "Unknown packet: %s\n",
+				packet->name);
+}
+
+
 static void
 _client_socket_handler(gpointer data, gint socket, PurpleInputCondition condition)
 {
-	char *message = NULL;
-	gint message_length;
 	PurpleBuddy *pb = data;
-	PurpleAccount *account = pb->account;
-	BonjourBuddy *bb = pb->proto_data;
-	gboolean closed_conversation = FALSE;
+	gint len, message_length;
+	static char message[4096];
+
+	/*TODO: use a static buffer */
 
 	/* Read the data from the socket */
-	if ((message_length = _read_data(socket, &message)) == -1) {
+	if ((len = recv(socket, message, sizeof(message) - 1, 0)) == -1) {
 		/* There have been an error reading from the socket */
 		if (errno != EAGAIN) {
+			BonjourBuddy *bb = pb->proto_data;
+
+			purple_debug_warning("bonjour", "receive error: %s\n", strerror(errno));
+
 			bonjour_jabber_close_conversation(bb->conversation);
 			bb->conversation = NULL;
 
@@ -362,65 +346,71 @@
 			 * If they try to send another message it'll reconnect */
 		}
 		return;
-	} else if (message_length == 0) { /* The other end has closed the socket */
-		closed_conversation = TRUE;
+	} else if (len == 0) { /* The other end has closed the socket */
+		purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", pb->name);
+		bonjour_jabber_stream_ended(pb);
+		return;
 	} else {
+		message_length = len;
 		message[message_length] = '\0';
 
-		while (g_ascii_iscntrl(message[message_length - 1])) {
+		while (message_length > 0 && g_ascii_iscntrl(message[message_length - 1])) {
 			message[message_length - 1] = '\0';
 			message_length--;
 		}
 	}
 
-	/*
-	 * Check that this is not the end of the conversation.  This is
-	 * using a magic string, but xmlnode won't play nice when just
-	 * parsing an end tag
-	 */
-	if (closed_conversation || purple_str_has_prefix(message, STREAM_END)) {
-		PurpleConversation *conv;
+	purple_debug_info("bonjour", "Receive: -%s- %d bytes\n", message, len);
+
+	bonjour_parser_process(pb, message, message_length);
+}
 
-		/* Close the socket, clear the watcher and free memory */
-		bonjour_jabber_close_conversation(bb->conversation);
-		bb->conversation = NULL;
+void bonjour_jabber_stream_ended(PurpleBuddy *pb) {
+	BonjourBuddy *bb = pb->proto_data;
+	PurpleConversation *conv;
+
+	purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", pb->name);
+
+	/* Close the socket, clear the watcher and free memory */
+	bonjour_jabber_close_conversation(bb->conversation);
+	bb->conversation = NULL;
 
-		/* Inform the user that the conversation has been closed */
-		conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pb->name, account);
-		if (conv != NULL) {
-			char *tmp = g_strdup_printf(_("%s has closed the conversation."), pb->name);
-			purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
-			g_free(tmp);
-		}
-	} else {
-		xmlnode *message_node;
+	/* Inform the user that the conversation has been closed */
+	conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pb->name, pb->account);
+	if (conv != NULL) {
+		char *tmp = g_strdup_printf(_("%s has closed the conversation."), pb->name);
+		purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
+		g_free(tmp);
+	}
+}
 
-		/* Parse the message into an XMLnode for analysis */
-		message_node = xmlnode_from_str(message, strlen(message));
+void bonjour_jabber_stream_started(PurpleBuddy *pb) {
+	BonjourBuddy *bb = pb->proto_data;
+	BonjourJabberConversation *bconv = bb->conversation;
 
-		if (message_node != NULL) {
-			/* Parse the message to get the data and send to the ui */
-			_jabber_parse_and_write_message_to_ui(message_node, account->gc, pb);
-			xmlnode_free(message_node);
-		} else {
-			/* TODO: Deal with receiving only a partial message */
-		}
+	/* If the stream has been completely started, we can start doing stuff */
+	if (bconv->sent_stream_start && bconv->recv_stream_start && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
+		/* Watch for when we can write the buffered messages */
+		bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
+			_send_data_write_cb, pb);
+		/* We can probably write the data right now. */
+		_send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE);
 	}
 
-	g_free(message);
 }
 
 struct _stream_start_data {
 	char *msg;
-	PurpleInputFunction tx_handler_cb;
 };
 
+
 static void
 _start_stream(gpointer data, gint source, PurpleInputCondition condition)
 {
 	PurpleBuddy *pb = data;
 	BonjourBuddy *bb = pb->proto_data;
-	struct _stream_start_data *ss = bb->conversation->stream_data;
+	BonjourJabberConversation *bconv = bb->conversation;
+	struct _stream_start_data *ss = bconv->stream_data;
 	int len, ret;
 
 	len = strlen(ss->msg);
@@ -443,7 +433,7 @@
 				  _("Unable to send the message, the conversation couldn't be started."),
 				  PURPLE_MESSAGE_SYSTEM, time(NULL));
 
-		bonjour_jabber_close_conversation(bb->conversation);
+		bonjour_jabber_close_conversation(bconv);
 		bb->conversation = NULL;
 
 		return;
@@ -457,22 +447,67 @@
 		return;
 	}
 
-	/* Stream started; process the send buffer if there is one*/
-	purple_input_remove(bb->conversation->tx_handler);
-	bb->conversation->tx_handler= -1;
-
-	bb->conversation->stream_started = TRUE;
-
 	g_free(ss->msg);
 	g_free(ss);
-	bb->conversation->stream_data = NULL;
+	bconv->stream_data = NULL;
+
+	/* Stream started; process the send buffer if there is one */
+	purple_input_remove(bconv->tx_handler);
+	bconv->tx_handler= -1;
+	bconv->sent_stream_start = TRUE;
+
+	bonjour_jabber_stream_started(pb);
+
+}
+
+static gboolean bonjour_jabber_stream_init(PurpleBuddy *pb, int client_socket)
+{
+	int ret, len;
+	char *stream_start;
+	BonjourBuddy *bb = pb->proto_data;
+
+	stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account),
+								   purple_buddy_get_name(pb));
+	len = strlen(stream_start);
+
+	/* Start the stream */
+	ret = send(client_socket, stream_start, len, 0);
+
+	if (ret == -1 && errno == EAGAIN)
+		ret = 0;
+	else if (ret <= 0) {
+		const char *err = strerror(errno);
 
-	if (ss->tx_handler_cb) {
-		bb->conversation->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE,
-			ss->tx_handler_cb, pb);
-		/* We can probably write the data now. */
-		(ss->tx_handler_cb)(pb, source, PURPLE_INPUT_WRITE);
+		purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
+				   purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
+
+		close(client_socket);
+		g_free(stream_start);
+
+		return FALSE;
 	}
+
+	/* This is unlikely to happen */
+	if (ret < len) {
+		struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
+		ss->msg = g_strdup(stream_start + ret);
+		bb->conversation->stream_data = ss;
+		/* Finish sending the stream start */
+		bb->conversation->tx_handler = purple_input_add(client_socket,
+			PURPLE_INPUT_WRITE, _start_stream, pb);
+	} else
+		bb->conversation->sent_stream_start = TRUE;
+
+	g_free(stream_start);
+
+	/* setup the parser fresh for each stream */
+	bonjour_parser_setup(bb->conversation);
+
+	bb->conversation->socket = client_socket;
+	bb->conversation->rx_handler = purple_input_add(client_socket,
+		PURPLE_INPUT_READ, _client_socket_handler, pb);
+
+	return TRUE;
 }
 
 static void
@@ -498,6 +533,7 @@
 
 	/* Look for the buddy that has opened the conversation and fill information */
 	address_text = inet_ntoa(their_addr.sin_addr);
+	purple_debug_info("bonjour", "Received incoming connection from %s\n.", address_text);
 	cbba = g_new0(struct _check_buddy_by_address_t, 1);
 	cbba->address = address_text;
 	cbba->pb = &pb;
@@ -515,49 +551,15 @@
 	/* Check if the conversation has been previously started */
 	if (bb->conversation == NULL)
 	{
-		int ret, len;
-		char *stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account),
-			purple_buddy_get_name(pb));
-
-		len = strlen(stream_start);
-
-		/* Start the stream */
-		ret = send(client_socket, stream_start, len, 0);
+		bb->conversation = bonjour_jabber_conv_new();
 
-		if (ret == -1 && errno == EAGAIN)
-			ret = 0;
-		else if (ret <= 0) {
-			const char *err = strerror(errno);
-
-			purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
-					   purple_buddy_get_name(pb), bb->ip ? bb->ip : "(null)", bb->port_p2pj, err ? err : "(null)");
-
+		if (!bonjour_jabber_stream_init(pb, client_socket)) {
 			close(client_socket);
-			g_free(stream_start);
-
 			return;
 		}
 
-		bb->conversation = bonjour_jabber_conv_new();
-		bb->conversation->socket = client_socket;
-		bb->conversation->rx_handler = purple_input_add(client_socket,
-			PURPLE_INPUT_READ, _client_socket_handler, pb);
-
-		/* This is unlikely to happen */
-		if (ret < len) {
-			struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
-			ss->msg = g_strdup(stream_start + ret);
-			ss->tx_handler_cb = NULL; /* We have nothing to write yet */
-			bb->conversation->stream_data = ss;
-			/* Finish sending the stream start */
-			bb->conversation->tx_handler = purple_input_add(client_socket,
-				PURPLE_INPUT_WRITE, _start_stream, pb);
-		} else {
-			bb->conversation->stream_started = TRUE;
-		}
-
-		g_free(stream_start);
 	} else {
+		purple_debug_warning("bonjour", "Ignoring incoming connection because an existing connection exists.\n");
 		close(client_socket);
 	}
 }
@@ -639,8 +641,6 @@
 {
 	PurpleBuddy *pb = data;
 	BonjourBuddy *bb = pb->proto_data;
-	int len, ret;
-	char *stream_start;
 
 	bb->conversation->connect_data = NULL;
 
@@ -661,15 +661,7 @@
 		return;
 	}
 
-	stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account), purple_buddy_get_name(pb));
-	len = strlen(stream_start);
-
-	/* Start the stream and send queued messages */
-	ret = send(source, stream_start, len, 0);
-
-	if (ret == -1 && errno == EAGAIN)
-		ret = 0;
-	else if (ret <= 0) {
+	if (!bonjour_jabber_stream_init(pb, source)) {
 		const char *err = strerror(errno);
 		PurpleConversation *conv;
 
@@ -685,37 +677,8 @@
 		close(source);
 		bonjour_jabber_close_conversation(bb->conversation);
 		bb->conversation = NULL;
-
-		g_free(stream_start);
-
 		return;
 	}
-
-	bb->conversation->socket = source;
-	bb->conversation->rx_handler = purple_input_add(source,
-		PURPLE_INPUT_READ, _client_socket_handler, pb);
-
-	/* This is unlikely to happen */
-	if (ret < len) {
-		struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
-		ss->msg = g_strdup(stream_start + ret);
-		ss->tx_handler_cb = _send_data_write_cb;
-		bb->conversation->stream_data = ss;
-		/* Finish sending the stream start */
-		bb->conversation->tx_handler = purple_input_add(source,
-			PURPLE_INPUT_WRITE, _start_stream, pb);
-	}
-	/* Process the send buffer */
-	else {
-		bb->conversation->stream_started = TRUE;
-		/* Watch for when we can write the buffered messages */
-		bb->conversation->tx_handler = purple_input_add(source, PURPLE_INPUT_WRITE,
-			_send_data_write_cb, pb);
-		/* We can probably write the data now. */
-		_send_data_write_cb(pb, source, PURPLE_INPUT_WRITE);
-	}
-
-	g_free(stream_start);
 }
 
 int
@@ -809,7 +772,7 @@
 		/* Close the socket and remove the watcher */
 		if (bconv->socket >= 0) {
 			/* Send the end of the stream to the other end of the conversation */
-			if (bconv->stream_started)
+			if (bconv->sent_stream_start)
 				send(bconv->socket, STREAM_END, strlen(STREAM_END), 0);
 			/* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
 			close(bconv->socket);
@@ -828,6 +791,10 @@
 			g_free(ss->msg);
 			g_free(ss);
 		}
+
+		if (bconv->context != NULL)
+			bonjour_parser_setup(bconv);
+
 		g_free(bconv);
 	}
 }
--- a/libpurple/protocols/bonjour/jabber.h	Tue Jul 31 23:20:56 2007 +0000
+++ b/libpurple/protocols/bonjour/jabber.h	Tue Jul 31 23:23:25 2007 +0000
@@ -26,6 +26,10 @@
 #ifndef _BONJOUR_JABBER_H_
 #define _BONJOUR_JABBER_H_
 
+#include <libxml/parser.h>
+
+#include "xmlnode.h"
+
 #include "account.h"
 #include "circbuffer.h"
 
@@ -43,9 +47,12 @@
 	guint rx_handler;
 	guint tx_handler;
 	PurpleCircBuffer *tx_buf;
-	gboolean stream_started;
+	gboolean sent_stream_start;
+	gboolean recv_stream_start;
 	PurpleProxyConnectData *connect_data;
 	gpointer stream_data;
+	xmlParserCtxt *context;
+	xmlnode *current;
 } BonjourJabberConversation;
 
 /**
@@ -60,6 +67,12 @@
 
 void bonjour_jabber_close_conversation(BonjourJabberConversation *bconv);
 
+void bonjour_jabber_stream_started(PurpleBuddy *pb);
+
+void bonjour_jabber_stream_ended(PurpleBuddy *pb);
+
+void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet);
+
 void bonjour_jabber_stop(BonjourJabber *data);
 
 #endif /* _BONJOUR_JABBER_H_ */
--- a/libpurple/protocols/bonjour/mdns_win32.c	Tue Jul 31 23:20:56 2007 +0000
+++ b/libpurple/protocols/bonjour/mdns_win32.c	Tue Jul 31 23:23:25 2007 +0000
@@ -84,6 +84,8 @@
 			buddy->txt_query_fd = purple_input_add(fd, PURPLE_INPUT_READ, _mdns_handle_event, buddy->txt_query);
 
 			bonjour_buddy_add_to_purple(buddy);
+
+			purple_debug_info("bonjour", "Found buddy %s at %s:%d\n", buddy->name, buddy->ip, buddy->port_p2pj);
 		}
 		else
 			bonjour_buddy_delete(buddy);
@@ -262,6 +264,7 @@
 		switch (type)
 		{
 			case PUBLISH_START:
+				purple_debug_info("bonjour", "Registering service on port %d\n", data->port_p2pj);
 				err = DNSServiceRegister(&data->advertisement, 0, 0, purple_account_get_username(data->account), ICHAT_SERVICE,
 					NULL, NULL, htons(data->port_p2pj), TXTRecordGetLength(&dns_data), TXTRecordGetBytesPtr(&dns_data),
 					_mdns_service_register_callback, NULL);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/parser.c	Tue Jul 31 23:23:25 2007 +0000
@@ -0,0 +1,194 @@
+/*
+ * purple - Bonjour Jabber 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
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include "internal.h"
+
+#include <libxml/parser.h>
+
+#include "connection.h"
+#include "debug.h"
+#include "jabber.h"
+#include "parser.h"
+#include "util.h"
+#include "xmlnode.h"
+
+static void
+bonjour_parser_element_start_libxml(void *user_data,
+				   const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace,
+				   int nb_namespaces, const xmlChar **namespaces,
+				   int nb_attributes, int nb_defaulted, const xmlChar **attributes)
+{
+	PurpleBuddy *pb = user_data;
+	BonjourBuddy *bb = pb->proto_data;
+	BonjourJabberConversation *bconv = bb->conversation;
+
+	xmlnode *node;
+	int i;
+
+	if(!element_name) {
+		return;
+	} else if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
+		bconv->recv_stream_start = TRUE;
+		bonjour_jabber_stream_started(pb);
+	} else {
+
+		if(bconv->current)
+			node = xmlnode_new_child(bconv->current, (const char*) element_name);
+		else
+			node = xmlnode_new((const char*) element_name);
+		xmlnode_set_namespace(node, (const char*) namespace);
+
+		for(i=0; i < nb_attributes * 5; i+=5) {
+			char *txt;
+			int attrib_len = attributes[i+4] - attributes[i+3];
+			char *attrib = g_malloc(attrib_len + 1);
+			char *attrib_ns = NULL;
+
+			if (attributes[i+2]) {
+				attrib_ns = g_strdup((char*)attributes[i+2]);;
+			}
+
+			memcpy(attrib, attributes[i+3], attrib_len);
+			attrib[attrib_len] = '\0';
+
+			txt = attrib;
+			attrib = purple_unescape_html(txt);
+			g_free(txt);
+			xmlnode_set_attrib_with_namespace(node, (const char*) attributes[i], attrib_ns, attrib);
+			g_free(attrib);
+			g_free(attrib_ns);
+		}
+
+		bconv->current = node;
+	}
+}
+
+static void
+bonjour_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
+				 const xmlChar *prefix, const xmlChar *namespace)
+{
+	PurpleBuddy *pb = user_data;
+	BonjourBuddy *bb = pb->proto_data;
+	BonjourJabberConversation *bconv = bb->conversation;
+
+	if(!bconv->current) {
+		/* We don't keep a reference to the start stream xmlnode,
+		 * so we have to check for it here to close the conversation */
+		if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
+			bonjour_jabber_stream_ended(pb);
+		}
+		return;
+	}
+
+	if(bconv->current->parent) {
+		if(!xmlStrcmp((xmlChar*) bconv->current->name, element_name))
+			bconv->current = bconv->current->parent;
+	} else {
+		xmlnode *packet = bconv->current;
+		bconv->current = NULL;
+		bonjour_jabber_process_packet(pb, packet);
+		xmlnode_free(packet);
+	}
+}
+
+static void
+bonjour_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
+{
+	PurpleBuddy *pb = user_data;
+	BonjourBuddy *bb = pb->proto_data;
+	BonjourJabberConversation *bconv = bb->conversation;
+
+	if(!bconv->current)
+		return;
+
+	if(!text || !text_len)
+		return;
+
+	xmlnode_insert_data(bconv->current, (const char*) text, text_len);
+}
+
+static xmlSAXHandler bonjour_parser_libxml = {
+	.internalSubset         = NULL,
+	.isStandalone           = NULL,
+	.hasInternalSubset      = NULL,
+	.hasExternalSubset      = NULL,
+	.resolveEntity          = NULL,
+	.getEntity              = NULL,
+	.entityDecl             = NULL,
+	.notationDecl           = NULL,
+	.attributeDecl          = NULL,
+	.elementDecl            = NULL,
+	.unparsedEntityDecl     = NULL,
+	.setDocumentLocator     = NULL,
+	.startDocument          = NULL,
+	.endDocument            = NULL,
+	.startElement           = NULL,
+	.endElement             = NULL,
+	.reference              = NULL,
+	.characters             = bonjour_parser_element_text_libxml,
+	.ignorableWhitespace    = NULL,
+	.processingInstruction  = NULL,
+	.comment                = NULL,
+	.warning                = NULL,
+	.error                  = NULL,
+	.fatalError             = NULL,
+	.getParameterEntity     = NULL,
+	.cdataBlock             = NULL,
+	.externalSubset         = NULL,
+	.initialized            = XML_SAX2_MAGIC,
+	._private               = NULL,
+	.startElementNs         = bonjour_parser_element_start_libxml,
+	.endElementNs           = bonjour_parser_element_end_libxml,
+	.serror                 = NULL
+};
+
+void
+bonjour_parser_setup(BonjourJabberConversation *bconv)
+{
+
+	/* This seems backwards, but it makes sense. The libxml code creates
+	 * the parser context when you try to use it (this way, it can figure
+	 * out the encoding at creation time. So, setting up the parser is
+	 * just a matter of destroying any current parser. */
+	if (bconv->context) {
+		xmlParseChunk(bconv->context, NULL,0,1);
+		xmlFreeParserCtxt(bconv->context);
+		bconv->context = NULL;
+	}
+}
+
+
+void bonjour_parser_process(PurpleBuddy *pb, const char *buf, int len)
+{
+	BonjourBuddy *bb = pb->proto_data;
+
+	if (bb->conversation->context ==  NULL) {
+		/* libxml inconsistently starts parsing on creating the
+		 * parser, so do a ParseChunk right afterwards to force it. */
+		bb->conversation->context = xmlCreatePushParserCtxt(&bonjour_parser_libxml, pb, buf, len, NULL);
+		xmlParseChunk(bb->conversation->context, "", 0, 0);
+	} else if (xmlParseChunk(bb->conversation->context, buf, len, 0) < 0) {
+		/* TODO: What should we do here - I assume we should display an error or something (maybe just print something to the conv?) */
+		purple_debug_error("bonjour", "Error parsing xml.\n");
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/bonjour/parser.h	Tue Jul 31 23:23:25 2007 +0000
@@ -0,0 +1,33 @@
+/**
+ * @file parser.h Bonjour Jabber XML parser functions
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef _PURPLE_BONJOUR_PARSER_H_
+#define _PURPLE_BONJOUR_PARSER_H_
+
+#include "buddy.h"
+#include "jabber.h"
+
+void bonjour_parser_setup(BonjourJabberConversation *bconv);
+void bonjour_parser_process(PurpleBuddy *pb, const char *buf, int len);
+
+#endif /* _PURPLE_BONJOUR_PARSER_H_ */