changeset 13806:25e63008d3bb

[gaim-migrate @ 16229] Use libxml2 for XML parsing, if available. The biggest benefit from this is actual support for XML namespaces. This fixes a handful of Google Talk integration problems, including typing notifications and buddy icons. committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Thu, 08 Jun 2006 01:03:51 +0000
parents 853fefb07c79
children a415805c7456
files configure.ac src/Makefile.am src/gtkdialogs.c src/protocols/jabber/Makefile.am src/protocols/jabber/auth.c src/protocols/jabber/buddy.c src/protocols/jabber/chat.c src/protocols/jabber/disco.c src/protocols/jabber/iq.c src/protocols/jabber/jabber.c src/protocols/jabber/jabber.h src/protocols/jabber/message.c src/protocols/jabber/oob.c src/protocols/jabber/parser.c src/protocols/jabber/presence.c src/protocols/jabber/si.c src/protocols/jabber/xdata.c src/xmlnode.c src/xmlnode.h
diffstat 19 files changed, 416 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Wed Jun 07 15:58:27 2006 +0000
+++ b/configure.ac	Thu Jun 08 01:03:51 2006 +0000
@@ -171,6 +171,17 @@
   AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for making sounds])
 fi
 
+dnl #################
+dnl # LibXML2
+dnl #################
+enable_libxml=yes
+PKG_CHECK_MODULES(LIBXML, libxml-2.0, ,enable_libxml=no)
+AC_SUBST(LIBXML_CFLAGS)
+AC_SUBST(LIBXML_LIBS)
+if test "x$enable_libxml" = "xyes"; then
+	AC_DEFINE(HAVE_LIBXML, 1, [Use libxml2 for xml parsing])
+fi
+
 dnl #######################################################################
 dnl # Check for Meanwhile headers (for Sametime)
 dnl #######################################################################
--- a/src/Makefile.am	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/Makefile.am	Thu Jun 08 01:03:51 2006 +0000
@@ -347,7 +347,8 @@
 	$(SM_LIBS) \
 	$(INTLLIBS) \
 	$(GTKSPELL_LIBS) \
-	$(STARTUP_NOTIFICATION_LIBS) 
+	$(STARTUP_NOTIFICATION_LIBS) \
+	$(LIBXML_LIBS)
 
 AM_CPPFLAGS = \
 	-DBR_PTHREADS=0 \
@@ -361,4 +362,5 @@
 	$(GTK_CFLAGS) \
 	$(DBUS_CFLAGS) \
 	$(GTKSPELL_CFLAGS) \
-	$(STARTUP_NOTIFICATION_CFLAGS)
+	$(STARTUP_NOTIFICATION_CFLAGS) \
+	$(LIBXML_CFLAGS)
--- a/src/gtkdialogs.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/gtkdialogs.c	Thu Jun 08 01:03:51 2006 +0000
@@ -438,6 +438,12 @@
 #endif
 #endif
 
+#ifdef HAVE_LIBXML
+	g_string_append_printf(str, "    <b>XML Parser:</b> libxml2<br/>");
+#else
+	g_string_append_printf(str, "    <b>XML Parser:</b> GMarkup<br/>");
+#endif
+
 #ifdef HAVE_LIBGADU
 #ifdef _WIN32
 	g_string_append(str, "    <b>Gadu-Gadu library (libgadu):</b> Internal<br/>");
--- a/src/protocols/jabber/Makefile.am	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/Makefile.am	Thu Jun 08 01:03:51 2006 +0000
@@ -60,4 +60,5 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src \
 	$(DEBUG_CFLAGS) \
-	$(GLIB_CFLAGS)
+	$(GLIB_CFLAGS) \
+	$(LIBXML_CFLAGS)
--- a/src/protocols/jabber/auth.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/auth.c	Thu Jun 08 01:03:51 2006 +0000
@@ -67,7 +67,7 @@
 		gchar *enc_out;
 
 		auth = xmlnode_new("auth");
-		xmlnode_set_attrib(auth, "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
+		xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl");
 
 		response = g_string_new("");
 		response = g_string_append_len(response, "\0", 1);
@@ -269,7 +269,7 @@
 
 	if (js->sasl_state == SASL_CONTINUE || js->sasl_state == SASL_OK) {
 		auth = xmlnode_new("auth");
-		xmlnode_set_attrib(auth, "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
+		xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl");
 		xmlnode_set_attrib(auth,"mechanism", mech);
 		if (clientout) {
 			if (coutlen == 0) {
@@ -386,7 +386,7 @@
 
 		js->auth_type = JABBER_AUTH_DIGEST_MD5;
 		auth = xmlnode_new("auth");
-		xmlnode_set_attrib(auth, "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
+		xmlnode_set_namespace(auth, "urn:ietf:params:xml:ns:xmpp-sasl");
 		xmlnode_set_attrib(auth, "mechanism", "DIGEST-MD5");
 
 		jabber_send(js, auth);
@@ -720,7 +720,7 @@
 			return;
 		} else {
 			response = xmlnode_new("response");
-			xmlnode_set_attrib(response, "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
+			xmlnode_set_namespace(response, "urn:ietf:params:xml:ns:xmpp-sasl");
 			if (c_out) {
 				enc_out = gaim_base64_encode((unsigned char*)c_out, clen);
 				xmlnode_insert_data(response, enc_out, -1);
@@ -735,7 +735,7 @@
 
 void jabber_auth_handle_success(JabberStream *js, xmlnode *packet)
 {
-	const char *ns = xmlnode_get_attrib(packet, "xmlns");
+	const char *ns = xmlnode_get_namespace(packet);
 #ifdef HAVE_CYRUS_SASL
 	int *x;
 #endif
--- a/src/protocols/jabber/buddy.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/buddy.c	Thu Jun 08 01:03:51 2006 +0000
@@ -1108,7 +1108,7 @@
 
 	xmlnode_set_attrib(iq->node, "to", jid);
 	vcard = xmlnode_new_child(iq->node, "vCard");
-	xmlnode_set_attrib(vcard, "xmlns", "vcard-temp");
+	xmlnode_set_namespace(vcard, "vcard-temp");
 
 	jabber_iq_set_callback(iq, jabber_vcard_parse, jbi);
 	jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id));
--- a/src/protocols/jabber/chat.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/chat.c	Thu Jun 08 01:03:51 2006 +0000
@@ -162,7 +162,7 @@
 	if(chat->muc) {
 		xmlnode_set_attrib(message, "to", room_jid);
 		x = xmlnode_new_child(message, "x");
-		xmlnode_set_attrib(x, "xmlns", "http://jabber.org/protocol/muc#user");
+		xmlnode_set_namespace(x, "http://jabber.org/protocol/muc#user");
 		invite = xmlnode_new_child(x, "invite");
 		xmlnode_set_attrib(invite, "to", name);
 		body = xmlnode_new_child(invite, "reason");
@@ -173,7 +173,7 @@
 		xmlnode_insert_data(body, msg, -1);
 		x = xmlnode_new_child(message, "x");
 		xmlnode_set_attrib(x, "jid", room_jid);
-		xmlnode_set_attrib(x, "xmlns", "jabber:x:conference");
+		xmlnode_set_namespace(x, "jabber:x:conference");
 	}
 
 	jabber_send(js, message);
@@ -267,7 +267,7 @@
 	g_free(full_jid);
 
 	x = xmlnode_new_child(presence, "x");
-	xmlnode_set_attrib(x, "xmlns", "http://jabber.org/protocol/muc");
+	xmlnode_set_namespace(x, "http://jabber.org/protocol/muc");
 
 	if(passwd && *passwd) {
 		xmlnode *password = xmlnode_new_child(x, "password");
@@ -380,7 +380,7 @@
 
 		for(x = xmlnode_get_child(query, "x"); x; x = xmlnode_get_next_twin(x)) {
 			const char *xmlns;
-			if(!(xmlns = xmlnode_get_attrib(x, "xmlns")))
+			if(!(xmlns = xmlnode_get_namespace(x)))
 				continue;
 
 			if(!strcmp(xmlns, "jabber:x:data")) {
@@ -451,7 +451,7 @@
 	room_jid = g_strdup_printf("%s@%s", chat->room, chat->server);
 
 	xmlnode_set_attrib(iq->node, "to", room_jid);
-	xmlnode_set_attrib(x, "xmlns", "jabber:x:data");
+	xmlnode_set_namespace(x, "jabber:x:data");
 	xmlnode_set_attrib(x, "type", "submit");
 
 	jabber_iq_send(iq);
@@ -524,7 +524,7 @@
 		for(x = xmlnode_get_child(query, "x"); x; x = xmlnode_get_next_twin(x)) {
 			const char *xmlns;
 
-			if(!(xmlns = xmlnode_get_attrib(x, "xmlns")))
+			if(!(xmlns = xmlnode_get_namespace(x)))
 				continue;
 
 			if(!strcmp(xmlns, "jabber:x:data")) {
--- a/src/protocols/jabber/disco.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/disco.c	Thu Jun 08 01:03:51 2006 +0000
@@ -104,7 +104,7 @@
 			xmlnode_set_attrib(error, "code", "404");
 			xmlnode_set_attrib(error, "type", "cancel");
 			inf = xmlnode_new_child(error, "item-not-found");
-			xmlnode_set_attrib(inf, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
+			xmlnode_set_namespace(inf, "urn:ietf:params:xml:ns:xmpp-stanzas");
 		}
 
 		jabber_iq_send(iq);
--- a/src/protocols/jabber/iq.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/iq.c	Thu Jun 08 01:03:51 2006 +0000
@@ -78,7 +78,7 @@
 	xmlnode *query;
 
 	query = xmlnode_new_child(iq->node, "query");
-	xmlnode_set_attrib(query, "xmlns", xmlns);
+	xmlnode_set_namespace(query, xmlns);
 
 	return iq;
 }
@@ -268,7 +268,7 @@
 
 	/* Apparently not, so lets see if we have a pre-defined handler */
 
-	if(type && query && (xmlns = xmlnode_get_attrib(query, "xmlns"))) {
+	if(type && query && (xmlns = xmlnode_get_namespace(query))) {
 		if(!strcmp(type, "set")) {
 			if(!strcmp(xmlns, "jabber:iq:roster")) {
 				jabber_roster_parse(js, packet);
@@ -329,7 +329,7 @@
 		xmlnode_set_attrib(error, "type", "cancel");
 		xmlnode_set_attrib(error, "code", "501");
 		x = xmlnode_new_child(error, "feature-not-implemented");
-		xmlnode_set_attrib(x, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
+		xmlnode_set_namespace(x, "urn:ietf:params:xml:ns:xmpp-stanzas");
 
 		jabber_iq_send(iq);
 	}
--- a/src/protocols/jabber/jabber.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/jabber.c	Thu Jun 08 01:03:51 2006 +0000
@@ -63,9 +63,9 @@
 						  "xmlns:stream='http://etherx.jabber.org/streams' "
 						  "version='1.0'>",
 						  js->user->domain);
-
+	/* setup the parser fresh for each stream */
+	jabber_parser_setup(js);
 	jabber_send_raw(js, open_stream, -1);
-
 	g_free(open_stream);
 }
 
@@ -88,7 +88,7 @@
 	jabber_iq_set_callback(iq, jabber_session_initialized_cb, NULL);
 
 	session = xmlnode_new_child(iq->node, "session");
-	xmlnode_set_attrib(session, "xmlns", "urn:ietf:params:xml:ns:xmpp-session");
+	xmlnode_set_namespace(session, "urn:ietf:params:xml:ns:xmpp-session");
 
 	jabber_iq_send(iq);
 }
@@ -137,7 +137,7 @@
 		xmlnode *bind, *resource;
 		JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET);
 		bind = xmlnode_new_child(iq->node, "bind");
-		xmlnode_set_attrib(bind, "xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
+		xmlnode_set_namespace(bind, "urn:ietf:params:xml:ns:xmpp-bind");
 		resource = xmlnode_new_child(bind, "resource");
 		xmlnode_insert_data(resource, js->user->resource, -1);
 
@@ -174,8 +174,14 @@
 		jabber_message_parse(js, packet);
 	} else if(!strcmp(packet->name, "stream:features")) {
 		jabber_stream_features_parse(js, packet);
+	} else if (!strcmp(packet->name, "features") && 
+		   !strcmp(xmlnode_get_namespace(packet), "http://etherx.jabber.org/streams")) {
+		jabber_stream_features_parse(js, packet);
 	} else if(!strcmp(packet->name, "stream:error")) {
 		jabber_stream_handle_error(js, packet);
+	} else if (!strcmp(packet->name, "error") &&
+		   !strcmp(xmlnode_get_namespace(packet), "http://etherx.jabber.org/streams")) {
+		jabber_stream_handle_error(js, packet);
 	} else if(!strcmp(packet->name, "challenge")) {
 		if(js->state == JABBER_STREAM_AUTHENTICATING)
 			jabber_auth_handle_challenge(js, packet);
@@ -405,7 +411,6 @@
 
 	if(js->state == JABBER_STREAM_CONNECTING)
 		jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
-
 	jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING);
 	gaim_ssl_input_add(gsc, jabber_recv_cb_ssl, gc);
 }
@@ -923,9 +928,10 @@
 			gaim_input_remove(js->gc->inpa);
 		close(js->fd);
 	}
-
+#ifndef HAVE_LIBXML
 	if(js->context)
 		g_markup_parse_context_free(js->context);
+#endif
 	if(js->iq_callbacks)
 		g_hash_table_destroy(js->iq_callbacks);
 	if(js->disco_callbacks)
@@ -981,7 +987,6 @@
 			gaim_connection_update_progress(js->gc, _("Initializing Stream"),
 					js->gsc ? 5 : 2, JABBER_CONNECT_STEPS);
 			jabber_stream_init(js);
-			jabber_parser_setup(js);
 			break;
 		case JABBER_STREAM_AUTHENTICATING:
 			gaim_connection_update_progress(js->gc, _("Authenticating"),
@@ -1400,7 +1405,7 @@
 {
 	xmlnode *error;
 	const char *code = NULL, *text = NULL;
-	const char *xmlns = xmlnode_get_attrib(packet, "xmlns");
+	const char *xmlns = xmlnode_get_namespace(packet);
 	char *cdata = NULL;
 
 	if((error = xmlnode_get_child(packet, "error"))) {
--- a/src/protocols/jabber/jabber.h	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/jabber.h	Thu Jun 08 01:03:51 2006 +0000
@@ -22,6 +22,9 @@
 #ifndef _GAIM_JABBER_H_
 #define _GAIM_JABBER_H_
 
+#ifdef HAVE_LIBXML
+#include <libxml/parser.h>
+#endif
 #include <glib.h>
 #include "connection.h"
 #include "roomlist.h"
@@ -64,7 +67,11 @@
 {
 	int fd;
 
+#ifdef HAVE_LIBXML
+	xmlParserCtxt *context;
+#else
 	GMarkupParseContext *context;
+#endif
 	xmlnode *current;
 
 	enum {
--- a/src/protocols/jabber/message.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/message.c	Thu Jun 08 01:03:51 2006 +0000
@@ -123,7 +123,7 @@
 
 	for(etc = jm->etc; etc; etc = etc->next) {
 		xmlnode *x = etc->data;
-		const char *xmlns = xmlnode_get_attrib(x, "xmlns");
+		const char *xmlns = xmlnode_get_namespace(x);
 		if(xmlns && !strcmp(xmlns, "jabber:x:oob")) {
 			xmlnode *url, *desc;
 			char *urltxt, *desctxt;
@@ -325,7 +325,7 @@
 			g_free(code_txt);
 			g_free(text);
 		} else if(!strcmp(child->name, "x")) {
-			const char *xmlns = xmlnode_get_attrib(child, "xmlns");
+			const char *xmlns = xmlnode_get_namespace(child);
 			if(xmlns && !strcmp(xmlns, "jabber:x:event")) {
 				if(xmlnode_get_child(child, "composing")) {
 					if(jm->chat_state == JM_STATE_ACTIVE)
@@ -440,7 +440,7 @@
 
 	if(JM_TS_JEP_0022 == (jm->typing_style & JM_TS_JEP_0022)) {
 		child = xmlnode_new_child(message, "x");
-		xmlnode_set_attrib(child, "xmlns", "jabber:x:event");
+		xmlnode_set_namespace(child, "jabber:x:event");
 		if(jm->chat_state == JM_STATE_COMPOSING || jm->body)
 			xmlnode_new_child(child, "composing");
 	}
@@ -466,7 +466,7 @@
 				break;
 		}
 		if(child)
-			xmlnode_set_attrib(child, "xmlns", "http://jabber.org/protocol/chatstates");
+			xmlnode_set_namespace(child, "http://jabber.org/protocol/chatstates");
 	}
 
 	if(jm->subject) {
--- a/src/protocols/jabber/oob.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/oob.c	Thu Jun 08 01:03:51 2006 +0000
@@ -168,11 +168,11 @@
 	if(!strcmp(code, "406")) {
 		z = xmlnode_new_child(y, "not-acceptable");
 		xmlnode_set_attrib(y, "type", "modify");
-		xmlnode_set_attrib(z, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
+		xmlnode_set_namespace(z, "urn:ietf:params:xml:ns:xmpp-stanzas");
 	} else if(!strcmp(code, "404")) {
 		z = xmlnode_new_child(y, "not-found");
 		xmlnode_set_attrib(y, "type", "cancel");
-		xmlnode_set_attrib(z, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
+		xmlnode_set_namespace(z, "urn:ietf:params:xml:ns:xmpp-stanzas");
 	}
 	jabber_iq_send(iq);
 
--- a/src/protocols/jabber/parser.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/parser.c	Thu Jun 08 01:03:51 2006 +0000
@@ -20,12 +20,17 @@
  */
 #include "internal.h"
 
+#ifdef HAVE_LIBXML
+#include <libxml/parser.h>
+#endif
+
 #include "connection.h"
-
+#include "debug.h"
 #include "jabber.h"
 #include "parser.h"
 #include "xmlnode.h"
 
+#ifndef HAVE_LIBXML
 static void
 jabber_parser_element_start(GMarkupParseContext *context,
 		const char *element_name, const char **attrib_names,
@@ -104,6 +109,136 @@
 	xmlnode_insert_data(js->current, text, text_len);
 }
 
+#else  /* HAVE_LIBXML */
+
+static void
+jabber_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)
+{
+	JabberStream *js = user_data;
+	xmlnode *node;
+	int i;
+
+	if(!element_name) {
+		return;
+	} else if(!strcmp(element_name, "stream")) {
+		js->protocol_version = JABBER_PROTO_0_9;
+		for(i=0; i < nb_attributes * 5; i += 5) {
+			int attrib_len = attributes[i+4] - attributes[i+3];
+			char *attrib = g_malloc(attrib_len + 1);
+			memcpy(attrib, attributes[i+3], attrib_len);
+			attrib[attrib_len] = '\0';
+			       
+			if(!strcmp(attributes[i], "version")
+					&& !strcmp(attrib, "1.0")) {
+				js->protocol_version = JABBER_PROTO_1_0;
+			} else if(!strcmp(attributes[i], "id")) {
+				if(js->stream_id)
+					g_free(js->stream_id);
+				js->stream_id = g_strdup(attrib);
+			}
+			g_free(attrib);
+		}
+		if(js->protocol_version == JABBER_PROTO_0_9)
+			js->auth_type = JABBER_AUTH_IQ_AUTH;
+
+		if(js->state == JABBER_STREAM_INITIALIZING)
+			jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING);
+	} else {
+
+		if(js->current)
+			node = xmlnode_new_child(js->current, element_name);
+		else
+			node = xmlnode_new(element_name);
+		xmlnode_set_namespace(node, namespace);
+
+		for(i=0; i < nb_attributes * 5; i+=5) {
+			int attrib_len = attributes[i+4] - attributes[i+3];
+			char *attrib = g_malloc(attrib_len + 1);
+			memcpy(attrib, attributes[i+3], attrib_len);
+			attrib[attrib_len] = '\0';
+			xmlnode_set_attrib(node, attributes[i], attrib);
+			g_free(attrib);
+		}
+
+		js->current = node;
+	}
+}
+
+static void
+jabber_parser_element_end_libxml(void *user_data, const xmlChar *element_name, 
+				 const xmlChar *prefix, const xmlChar *namespace)
+{
+	JabberStream *js = user_data;
+
+	if(!js->current)
+		return;
+
+	if(js->current->parent) {
+		if(!strcmp(js->current->name, element_name))
+			js->current = js->current->parent;
+	} else {
+		xmlnode *packet = js->current;
+		js->current = NULL;
+		jabber_process_packet(js, packet);
+		xmlnode_free(packet);
+	}
+}
+
+static void
+jabber_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
+{
+	JabberStream *js = user_data;
+
+	if(!js->current)
+		return;
+
+	if(!text || !text_len)
+		return;
+
+	xmlnode_insert_data(js->current, text, text_len);
+}
+#endif  /* HAVE_LIBXML */
+
+
+#ifdef HAVE_LIBXML
+static xmlSAXHandler jabber_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             = jabber_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         = jabber_parser_element_start_libxml,
+	.endElementNs           = jabber_parser_element_end_libxml,
+	.serror                 = NULL
+};
+#else
 static GMarkupParser jabber_parser = {
 	jabber_parser_element_start,
 	jabber_parser_element_end,
@@ -111,24 +246,47 @@
 	NULL,
 	NULL
 };
+#endif
 
 void
 jabber_parser_setup(JabberStream *js)
 {
+#ifdef HAVE_LIBXML
+	/* 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 (js->context) {
+		xmlParseChunk(js->context, NULL,0,1);
+		xmlFreeParserCtxt(js->context);
+		js->context = NULL;
+	}
+#else
 	if(!js->context)
 		js->context = g_markup_parse_context_new(&jabber_parser, 0, js, NULL);
+#endif
 }
 
 
 void jabber_parser_process(JabberStream *js, const char *buf, int len)
 {
 
+#ifndef HAVE_LIBXML
 	/* May need to check for other encodings and do the conversion here */
-
 	if(!g_markup_parse_context_parse(js->context, buf, len, NULL)) {
 		g_markup_parse_context_free(js->context);
 		js->context = NULL;
 		gaim_connection_error(js->gc, _("XML Parse error"));
 	}
+#else
+	if (js->context ==  NULL) {
+		/* libxml inconsistently starts parsing on creating the parser, so so a ParseChunk
+		 * right afterwards to force it. */
+		js->context = xmlCreatePushParserCtxt(&jabber_parser_libxml, js, buf, len, NULL);
+		xmlParseChunk(js->context, NULL, 0, 0);
+	} else if (xmlParseChunk(js->context, buf, len, 0) < 0) {
+		gaim_connection_error(js->gc, _("XML Parse error"));
+	}
+#endif
 }
 
--- a/src/protocols/jabber/presence.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/presence.c	Thu Jun 08 01:03:51 2006 +0000
@@ -121,7 +121,7 @@
 
 	if(js->avatar_hash) {
 		x = xmlnode_new_child(presence, "x");
-		xmlnode_set_attrib(x, "xmlns", "vcard-temp:x:update");
+		xmlnode_set_namespace(x, "vcard-temp:x:update");
 		photo = xmlnode_new_child(x, "photo");
 		xmlnode_insert_data(photo, js->avatar_hash, -1);
 	}
@@ -167,7 +167,7 @@
 
 	/* JEP-0115 */
 	c = xmlnode_new_child(presence, "c");
-	xmlnode_set_attrib(c, "xmlns",  "http://jabber.org/protocol/caps");
+	xmlnode_set_namespace(c, "http://jabber.org/protocol/caps");
 	xmlnode_set_attrib(c, "node", CAPS0115_NODE);
 	xmlnode_set_attrib(c, "ver", VERSION);
 
@@ -290,7 +290,6 @@
 	gboolean muc = FALSE;
 	char *avatar_hash = NULL;
 
-
 	if(!(jb = jabber_buddy_find(js, from, TRUE)))
 		return;
 
@@ -363,7 +362,7 @@
 				g_free(p);
 			}
 		} else if(!strcmp(y->name, "x")) {
-			const char *xmlns = xmlnode_get_attrib(y, "xmlns");
+			const char *xmlns = xmlnode_get_namespace(y);
 			if(xmlns && !strcmp(xmlns, "jabber:x:delay")) {
 				/* XXX: compare the time.  jabber:x:delay can happen on presence packets that aren't really and truly delayed */
 				delayed = TRUE;
@@ -464,7 +463,7 @@
 				for(x = xmlnode_get_child(packet, "x"); x; x = xmlnode_get_next_twin(x)) {
 					const char *xmlns, *nick, *code;
 					xmlnode *stat, *item;
-					if(!(xmlns = xmlnode_get_attrib(x, "xmlns")) ||
+					if(!(xmlns = xmlnode_get_namespace(x)) ||
 							strcmp(xmlns, "http://jabber.org/protocol/muc#user"))
 						continue;
 					if(!(stat = xmlnode_get_child(x, "status")))
@@ -560,7 +559,7 @@
 					iq = jabber_iq_new(js, JABBER_IQ_GET);
 					xmlnode_set_attrib(iq->node, "to", buddy_name);
 					vcard = xmlnode_new_child(iq->node, "vCard");
-					xmlnode_set_attrib(vcard, "xmlns", "vcard-temp");
+					xmlnode_set_namespace(vcard, "vcard-temp");
 
 					jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL);
 					jabber_iq_send(iq);
--- a/src/protocols/jabber/si.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/si.c	Thu Jun 08 01:03:51 2006 +0000
@@ -139,7 +139,7 @@
 		xmlnode_set_attrib(error, "code", "404");
 		xmlnode_set_attrib(error, "type", "cancel");
 		condition = xmlnode_new_child(error, "condition");
-		xmlnode_set_attrib(condition, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
+		xmlnode_set_namespace(condition, "urn:ietf:params:xml:ns:xmpp-stanzas");
 		xmlnode_new_child(condition, "item-not-found");
 
 		jabber_iq_send(iq);
@@ -637,14 +637,14 @@
 	iq = jabber_iq_new(jsx->js, JABBER_IQ_SET);
 	xmlnode_set_attrib(iq->node, "to", xfer->who);
 	si = xmlnode_new_child(iq->node, "si");
-	xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si");
+	xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
 	jsx->stream_id = jabber_get_next_id(jsx->js);
 	xmlnode_set_attrib(si, "id", jsx->stream_id);
 	xmlnode_set_attrib(si, "profile",
 			"http://jabber.org/protocol/si/profile/file-transfer");
 
 	file = xmlnode_new_child(si, "file");
-	xmlnode_set_attrib(file, "xmlns",
+	xmlnode_set_namespace(file,
 			"http://jabber.org/protocol/si/profile/file-transfer");
 	xmlnode_set_attrib(file, "name", xfer->filename);
 	g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
@@ -652,10 +652,10 @@
 	/* maybe later we'll do hash and date attribs */
 
 	feature = xmlnode_new_child(si, "feature");
-	xmlnode_set_attrib(feature, "xmlns",
+	xmlnode_set_namespace(feature,
 			"http://jabber.org/protocol/feature-neg");
 	x = xmlnode_new_child(feature, "x");
-	xmlnode_set_attrib(x, "xmlns", "jabber:x:data");
+	xmlnode_set_namespace(x, "jabber:x:data");
 	xmlnode_set_attrib(x, "type", "form");
 	field = xmlnode_new_child(x, "field");
 	xmlnode_set_attrib(field, "var", "stream-method");
@@ -771,13 +771,13 @@
 		jsx->accepted = TRUE;
 
 		si = xmlnode_new_child(iq->node, "si");
-		xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si");
+		xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
 
 		feature = xmlnode_new_child(si, "feature");
-		xmlnode_set_attrib(feature, "xmlns", "http://jabber.org/protocol/feature-neg");
+		xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
 
 		x = xmlnode_new_child(feature, "x");
-		xmlnode_set_attrib(x, "xmlns", "jabber:x:data");
+		xmlnode_set_namespace(x, "jabber:x:data");
 		xmlnode_set_attrib(x, "type", "submit");
 
 		field = xmlnode_new_child(x, "field");
--- a/src/protocols/jabber/xdata.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/protocols/jabber/xdata.c	Thu Jun 08 01:03:51 2006 +0000
@@ -49,7 +49,7 @@
 	JabberStream *js = data->js;
 	GList *groups, *flds;
 
-	xmlnode_set_attrib(result, "xmlns", "jabber:x:data");
+	xmlnode_set_namespace(result, "jabber:x:data");
 	xmlnode_set_attrib(result, "type", "submit");
 
 	for(groups = gaim_request_fields_get_groups(fields); groups; groups = groups->next) {
@@ -140,7 +140,7 @@
 	}
 	g_free(data);
 
-	xmlnode_set_attrib(result, "xmlns", "jabber:x:data");
+	xmlnode_set_namespace(result, "jabber:x:data");
 	xmlnode_set_attrib(result, "type", "cancel");
 
 	cb(js, result, user_data);
--- a/src/xmlnode.c	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/xmlnode.c	Thu Jun 08 01:03:51 2006 +0000
@@ -29,6 +29,9 @@
 
 #include "internal.h"
 
+#ifdef HAVE_LIBXML
+#include <libxml/parser.h>
+#endif
 #include <string.h>
 #include <glib.h>
 
@@ -172,6 +175,32 @@
 	return NULL;
 }
 
+
+void xmlnode_set_namespace(xmlnode *node, const char *xmlns)
+{
+#ifdef HAVE_LIBXML
+	g_return_if_fail(node != NULL);
+
+	if (node->namespace)
+		g_free(node->namespace);
+	
+	node->namespace = g_strdup(xmlns);
+#else
+	return xmlnode_set_attrib(node, "xmlns", xmlns);
+#endif
+}
+
+const char *xmlnode_get_namespace(xmlnode *node)
+{
+#ifdef HAVE_LIBXML
+	g_return_val_if_fail(node != NULL, NULL);
+
+	return node->namespace;
+#else
+	return xmlnode_get_attrib(node, "xmlns");
+#endif
+}
+
 void
 xmlnode_free(xmlnode *node)
 {
@@ -190,6 +219,10 @@
 		g_free(node->name);
 	if(node->data)
 		g_free(node->data);
+#ifdef HAVE_LIBXML
+	if(node->namespace)
+		g_free(node->namespace);
+#endif
 	g_free(node);
 }
 
@@ -216,7 +249,7 @@
 	for(x = parent->child; x; x = x->next) {
 		const char *xmlns = NULL;
 		if(ns)
-			xmlns = xmlnode_get_attrib(x, "xmlns");
+			xmlns = xmlnode_get_namespace(x);
 
 		if(x->type == XMLNODE_TYPE_TAG && name && !strcmp(parent_name, x->name)
 				&& (!ns || (xmlns && !strcmp(ns, xmlns)))) {
@@ -272,6 +305,13 @@
 	node_name = g_markup_escape_text(node->name, -1);
 	g_string_append_printf(text, "<%s", node_name);
 
+#ifdef HAVE_LIBXML
+	if (node->namespace) {
+		char *namespace = g_markup_escape_text(node->namespace, -1);
+		g_string_append_printf(text, " xmlns='%s'", namespace);
+		g_free(namespace);
+	}
+#endif	
 	for(c = node->child; c; c = c->next)
 	{
 		if(c->type == XMLNODE_TYPE_ATTRIB) {
@@ -347,6 +387,71 @@
 	xmlnode *current;
 };
 
+#ifdef HAVE_LIBXML
+static void
+xmlnode_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)
+{
+	struct _xmlnode_parser_data *xpd = user_data;
+	xmlnode *node;
+	int i;
+
+	if(!element_name) {
+		return;
+	} else {
+		if(xpd->current)
+			node = xmlnode_new_child(xpd->current, element_name);
+		else
+			node = xmlnode_new(element_name);
+
+		xmlnode_set_namespace(node, namespace);
+
+		for(i=0; i < nb_attributes * 5; i+=5) {
+			int attrib_len = attributes[i+4] - attributes[i+3];
+			char *attrib = g_malloc(attrib_len + 1);
+			memcpy(attrib, attributes[i+3], attrib_len);
+			attrib[attrib_len] = '\0';
+			xmlnode_set_attrib(node, attributes[i], attrib);
+			g_free(attrib);
+		}
+
+		xpd->current = node;
+	}
+}
+
+static void
+xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name, 
+				 const xmlChar *prefix, const xmlChar *namespace)
+{
+	struct _xmlnode_parser_data *xpd = user_data;
+
+	if(!element_name || !xpd->current)
+		return;
+
+	if(xpd->current->parent) {
+		if(!strcmp(xpd->current->name, element_name))
+			xpd->current = xpd->current->parent;
+	}
+}
+
+static void
+xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
+{
+	struct _xmlnode_parser_data *xpd = user_data;
+
+	if(!xpd->current)
+		return;
+
+	if(!text || !text_len)
+		return;
+
+	xmlnode_insert_data(xpd->current, text, text_len);
+}
+
+#else
+
 static void
 xmlnode_parser_element_start(GMarkupParseContext *context,
 		const char *element_name, const char **attrib_names,
@@ -400,7 +505,44 @@
 
 	xmlnode_insert_data(xpd->current, text, text_len);
 }
+#endif
 
+#ifdef HAVE_LIBXML
+static xmlSAXHandler xmlnode_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             = xmlnode_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         = xmlnode_parser_element_start_libxml,
+	.endElementNs           = xmlnode_parser_element_end_libxml,
+	.serror                 = NULL
+};
+#else
 static GMarkupParser xmlnode_parser = {
 	xmlnode_parser_element_start,
 	xmlnode_parser_element_end,
@@ -408,7 +550,7 @@
 	NULL,
 	NULL
 };
-
+#endif
 
 xmlnode *
 xmlnode_from_str(const char *str, gssize size)
@@ -422,6 +564,16 @@
 
 	real_size = size < 0 ? strlen(str) : size;
 	xpd = g_new0(struct _xmlnode_parser_data, 1);
+
+#ifdef HAVE_LIBXML
+	if (xmlSAXUserParseMemory(&xmlnode_parser_libxml, xpd, str, size) < 0) {
+		while(xpd->current && xpd->current->parent)
+			xpd->current = xpd->current->parent;
+		if(xpd->current)
+			xmlnode_free(xpd->current);
+		xpd->current = NULL;
+	}
+#else
 	context = g_markup_parse_context_new(&xmlnode_parser, 0, xpd, NULL);
 
 	if(!g_markup_parse_context_parse(context, str, real_size, NULL)) {
@@ -432,7 +584,7 @@
 		xpd->current = NULL;
 	}
 	g_markup_parse_context_free(context);
-
+#endif
 	ret = xpd->current;
 	g_free(xpd);
 	return ret;
@@ -477,7 +629,7 @@
 xmlnode_get_next_twin(xmlnode *node)
 {
 	xmlnode *sibling;
-	const char *ns = xmlnode_get_attrib(node, "xmlns");
+	const char *ns = xmlnode_get_namespace(node);
 
 	g_return_val_if_fail(node != NULL, NULL);
 	g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL);
@@ -485,7 +637,7 @@
 	for(sibling = node->next; sibling; sibling = sibling->next) {
 		const char *xmlns = NULL;
 		if(ns)
-			xmlns = xmlnode_get_attrib(sibling, "xmlns");
+			xmlns = xmlnode_get_namespace(sibling);
 
 		if(sibling->type == XMLNODE_TYPE_TAG && !strcmp(node->name, sibling->name) &&
 				(!ns || (xmlns && !strcmp(ns, xmlns))))
--- a/src/xmlnode.h	Wed Jun 07 15:58:27 2006 +0000
+++ b/src/xmlnode.h	Thu Jun 08 01:03:51 2006 +0000
@@ -41,6 +41,9 @@
 typedef struct _xmlnode
 {
 	char *name;					/**< The name of the node. */
+#ifdef HAVE_LIBXML
+	char *namespace;                    /**< The namespace of the node */
+#endif
 	XMLNodeType type;			/**< The type of the node. */
 	char *data;					/**< The data for the node. */
 	size_t data_sz;				/**< The size of the data. */
@@ -154,6 +157,22 @@
 void xmlnode_remove_attrib(xmlnode *node, const char *attr);
 
 /**
+ * Sets the namespace of a node
+ *
+ * @param node The node to qualify
+ * @param xmlns The namespace of the node
+ */
+void xmlnode_set_namespace(xmlnode *node, const char *xmlns);
+
+/**
+ * Returns the namespace of a node
+ *
+ * @param node The node to get the namepsace from
+ * @return The namespace of this node
+ */
+const char *xmlnode_get_namespace(xmlnode *node);
+
+/**
  * Returns the node in a string of xml.
  *
  * @param node The starting node to output.