From e2f1c1e94a4c44c50f2894ae8fa918d77b842d8f Mon Sep 17 00:00:00 2001 From: Codewalker Date: Wed, 24 Dec 2014 11:46:11 -0600 Subject: [PATCH 1/2] Add XML namespace support. --- src/auth.c | 8 ++-- src/conn.c | 2 +- src/parser_expat.c | 64 +++++++++++++++++++++++++++++-- src/parser_libxml2.c | 91 ++++++++++++++++++++++++++++++++++++++------ 4 files changed, 146 insertions(+), 19 deletions(-) diff --git a/src/auth.c b/src/auth.c index d208670..137ae4e 100644 --- a/src/auth.c +++ b/src/auth.c @@ -866,11 +866,11 @@ void auth_handle_open(xmpp_conn_t * const conn) /* setup handler for stream:error */ handler_add(conn, _handle_error, - NULL, "stream:error", NULL, NULL); + XMPP_NS_STREAMS, "error", NULL, NULL); /* setup handlers for incoming */ handler_add(conn, _handle_features, - NULL, "stream:features", NULL, NULL); + XMPP_NS_STREAMS, "features", NULL, NULL); handler_add_timed(conn, _handle_missing_features, FEATURES_TIMEOUT, NULL); } @@ -882,7 +882,7 @@ static void _handle_open_sasl(xmpp_conn_t * const conn) /* setup stream:features handlers */ handler_add(conn, _handle_features_sasl, - NULL, "stream:features", NULL, NULL); + XMPP_NS_STREAMS, "features", NULL, NULL); handler_add_timed(conn, _handle_missing_features_sasl, FEATURES_TIMEOUT, NULL); } @@ -1168,7 +1168,7 @@ void auth_handle_component_open(xmpp_conn_t * const conn) /* reset all timed handlers */ handler_reset_timed(conn, 0); - handler_add(conn, _handle_error, NULL, "stream:error", NULL, NULL); + handler_add(conn, _handle_error, XMPP_NS_STREAMS, "error", NULL, NULL); handler_add(conn, _handle_component_hs_response, NULL, "handshake", NULL, NULL); handler_add_timed(conn, _handle_missing_handshake, HANDSHAKE_TIMEOUT, NULL); diff --git a/src/conn.c b/src/conn.c index d71ae9d..c8e41d1 100644 --- a/src/conn.c +++ b/src/conn.c @@ -789,7 +789,7 @@ static void _handle_stream_start(char *name, char **attrs, xmpp_conn_t *conn = (xmpp_conn_t *)userdata; char *id; - if (strcmp(name, "stream:stream") != 0) { + if (strcmp(name, "stream")) { printf("name = %s\n", name); xmpp_error(conn->ctx, "conn", "Server did not open valid stream."); conn_disconnect(conn); diff --git a/src/parser_expat.c b/src/parser_expat.c index ea5289d..27e2744 100644 --- a/src/parser_expat.c +++ b/src/parser_expat.c @@ -23,6 +23,9 @@ #include "common.h" #include "parser.h" +/* ASCII FF is invalid UTF-8 and should never be present in well-formed XML */ +#define NAMESPACE_SEP ('\xFF') + struct _parser_t { xmpp_ctx_t *ctx; XML_Parser expat; @@ -34,23 +37,71 @@ struct _parser_t { xmpp_stanza_t *stanza; }; +/* return allocated string with the name from a delimited + * namespace/name string */ +static char *_xml_name(xmpp_ctx_t *ctx, const char *nsname) +{ + char *result = NULL; + const char *c; + int len; + + c = strchr(nsname, NAMESPACE_SEP); + if (c == NULL) return xmpp_strdup(ctx, nsname); + + c++; + len = strlen(c); + result = xmpp_alloc(ctx, len + 1); + if (result != NULL) { + memcpy(result, c, len); + result[len] = '\0'; + } + + return result; +} + +/* return allocated string with the namespace from a delimited string */ +static char *_xml_namespace(xmpp_ctx_t *ctx, const char *nsname) +{ + char *result = NULL; + const char *c; + + c = strchr(nsname, NAMESPACE_SEP); + if (c != NULL) { + result = xmpp_alloc(ctx, (c-nsname) + 1); + if (result != NULL) { + memcpy(result, nsname, (c-nsname)); + result[c-nsname] = '\0'; + } + } + + return result; +} + static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs) { + char *attr; int i; if (!attrs) return; for (i = 0; attrs[i]; i += 2) { - xmpp_stanza_set_attribute(stanza, attrs[i], attrs[i+1]); + /* namespaced attributes aren't used in xmpp, discard namespace */ + attr = _xml_name(stanza->ctx, attrs[i]); + xmpp_stanza_set_attribute(stanza, attr, attrs[i+1]); + xmpp_free(stanza->ctx, attr); } } static void _start_element(void *userdata, - const XML_Char *name, + const XML_Char *nsname, const XML_Char **attrs) { parser_t *parser = (parser_t *)userdata; xmpp_stanza_t *child; + char *ns, *name; + + ns = _xml_namespace(parser->ctx, nsname); + name = _xml_name(parser->ctx, nsname); if (parser->depth == 0) { /* notify the owner */ @@ -71,6 +122,8 @@ static void _start_element(void *userdata, } xmpp_stanza_set_name(parser->stanza, name); _set_attributes(parser->stanza, attrs); + if (ns) + xmpp_stanza_set_ns(parser->stanza, ns); } else { /* starting a child of parser->stanza */ child = xmpp_stanza_new(parser->ctx); @@ -79,6 +132,8 @@ static void _start_element(void *userdata, } xmpp_stanza_set_name(child, name); _set_attributes(child, attrs); + if (ns) + xmpp_stanza_set_ns(child, ns); /* add child to parent */ xmpp_stanza_add_child(parser->stanza, child); @@ -91,6 +146,9 @@ static void _start_element(void *userdata, } } + if (ns) xmpp_free(parser->ctx, ns); + if (name) xmpp_free(parser->ctx, name); + parser->depth++; } @@ -180,7 +238,7 @@ int parser_reset(parser_t *parser) if (parser->stanza) xmpp_stanza_release(parser->stanza); - parser->expat = XML_ParserCreate(NULL); + parser->expat = XML_ParserCreateNS(NULL, NAMESPACE_SEP); if (!parser->expat) return 0; parser->depth = 0; diff --git a/src/parser_libxml2.c b/src/parser_libxml2.c index 24bebc5..9018419 100644 --- a/src/parser_libxml2.c +++ b/src/parser_libxml2.c @@ -36,28 +36,91 @@ struct _parser_t { xmpp_stanza_t *stanza; }; -static void _set_attributes(xmpp_stanza_t *stanza, const xmlChar **attrs) +static void _set_attributes(xmpp_stanza_t *stanza, int nattrs, + const xmlChar **attrs) { - int i; + int i, len; + char *value; if (!attrs) return; - for (i = 0; attrs[i]; i += 2) { - xmpp_stanza_set_attribute(stanza, (const char *)attrs[i], (const char *)attrs[i+1]); + /* SAX2 uses array of localname/prefix/uri/value_begin/value_end */ + for (i = 0; i < nattrs*5; i += 5) { + len = attrs[i+4] - attrs[i+3]; + value = xmpp_alloc(stanza->ctx, len + 1); + if (value) { + memcpy(value, attrs[i+3], len); + value[len] = '\0'; + xmpp_stanza_set_attribute(stanza, (const char *)attrs[i], value); + xmpp_free(stanza->ctx, value); + } } } +/* SAX2 gives us the attrs in an incredibly inconvenient array, + * convert it to what the start callback is expecting */ +static char **_convert_attrs(parser_t *parser, int nattrs, + const xmlChar **attrs) +{ + int c, i, o, len; + char *value; + char **ret; + + if (!attrs) return NULL; + + ret = xmpp_alloc(parser->ctx, (nattrs+1)*2*sizeof(char*)); + if (!ret) return NULL; + memset(ret, 0, (nattrs+1)*2*sizeof(char*)); + + for (c = 0; c < nattrs; c++) { + i = c * 5; + o = c * 2; + + len = attrs[i+4] - attrs[i+3]; + value = xmpp_alloc(parser->ctx, len + 1); + if (value) { + memcpy(value, attrs[i+3], len); + value[len] = '\0'; + ret[o] = xmpp_strdup(parser->ctx, (char*)attrs[i]); + ret[o+1] = value; + } + } + + return ret; +} + +static void _free_cbattrs(parser_t *parser, char **attrs) +{ + int i; + + if (!attrs) + return; + + for (i = 0; attrs[i]; i += 2) { + if (attrs[i]) xmpp_free(parser->ctx, attrs[i]); + if (attrs[i+1]) xmpp_free(parser->ctx, attrs[i+1]); + } + + xmpp_free(parser->ctx, attrs); +} + static void _start_element(void *userdata, - const xmlChar *name, const xmlChar **attrs) + const xmlChar *name, const xmlChar *prefix, + const xmlChar *uri, int nnamespaces, + const xmlChar **namespaces, int nattrs, + int ndefaulted, const xmlChar **attrs) { parser_t *parser = (parser_t *)userdata; xmpp_stanza_t *child; + char **cbattrs; if (parser->depth == 0) { /* notify the owner */ if (parser->startcb) - parser->startcb((char *)name, (char **)attrs, + cbattrs = _convert_attrs(parser, nattrs, attrs); + parser->startcb((char *)name, cbattrs, parser->userdata); + _free_cbattrs(parser, cbattrs); } else { /* build stanzas at depth 1 */ if (!parser->stanza && parser->depth != 1) { @@ -71,7 +134,9 @@ static void _start_element(void *userdata, /* FIXME: can't allocate, disconnect */ } xmpp_stanza_set_name(parser->stanza, (char *)name); - _set_attributes(parser->stanza, attrs); + _set_attributes(parser->stanza, nattrs, attrs); + if (uri) + xmpp_stanza_set_ns(parser->stanza, (char *)uri); } else { /* starting a child of conn->stanza */ child = xmpp_stanza_new(parser->ctx); @@ -79,7 +144,9 @@ static void _start_element(void *userdata, /* FIXME: can't allocate, disconnect */ } xmpp_stanza_set_name(child, (char *)name); - _set_attributes(child, attrs); + _set_attributes(child, nattrs, attrs); + if (uri) + xmpp_stanza_set_ns(child, (char *)uri); /* add child to parent */ xmpp_stanza_add_child(parser->stanza, child); @@ -95,7 +162,8 @@ static void _start_element(void *userdata, parser->depth++; } -static void _end_element(void *userdata, const xmlChar *name) +static void _end_element(void *userdata, const xmlChar *name, + const xmlChar *prefix, const xmlChar *uri) { parser_t *parser = (parser_t *)userdata; @@ -153,8 +221,9 @@ parser_t *parser_new(xmpp_ctx_t *ctx, parser->ctx = ctx; parser->xmlctx = NULL; memset(&parser->handlers, 0, sizeof(xmlSAXHandler)); - parser->handlers.startElement = _start_element; - parser->handlers.endElement = _end_element; + parser->handlers.initialized = XML_SAX2_MAGIC; + parser->handlers.startElementNs = _start_element; + parser->handlers.endElementNs = _end_element; parser->handlers.characters = _characters; parser->startcb = startcb; parser->endcb = endcb; From e33a98e498cff5318b29a46e8def2855ef4dc786 Mon Sep 17 00:00:00 2001 From: Codewalker Date: Fri, 23 Jan 2015 11:02:42 -0600 Subject: [PATCH 2/2] Streamline xmpp_stanza_to_text for the namespace patch by following XML namespace scoping guidelines and not outputting the xmlns attribute in contexts where it can be inferred from the parent element. --- src/stanza.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/stanza.c b/src/stanza.c index 2085929..5e3c7f7 100644 --- a/src/stanza.c +++ b/src/stanza.c @@ -317,6 +317,20 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza, if (stanza->attributes && hash_num_keys(stanza->attributes) > 0) { iter = hash_iter_new(stanza->attributes); while ((key = hash_iter_next(iter))) { + if (!strcmp(key, "xmlns")) { + /* don't output namespace if parent stanza is the same */ + if (stanza->parent && + stanza->parent->attributes && + hash_get(stanza->parent->attributes, key) && + !strcmp((char*)hash_get(stanza->attributes, key), + (char*)hash_get(stanza->parent->attributes, key))) + continue; + /* or if this is the stream namespace */ + if (!stanza->parent && + !strcmp((char*)hash_get(stanza->attributes, key), + XMPP_NS_CLIENT)) + continue; + } tmp = _escape_xml(stanza->ctx, (char *)hash_get(stanza->attributes, key)); if (tmp == NULL) return XMPP_EMEM;