Mercurial > pidgin
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.