commit c0b1e5b7fe51c6b7018116d96ea98b7912374385 Author: Alexandre Erwin Ittner Date: Sun Sep 23 17:18:53 2012 -0300 Fix escaping of XML text and attributes There was a bug in xmpp_send which caused XML special characters to be sent to the server verbatim, implying in invalid stanzas and making the server drop the connection. This commit fixed the bug escaping such characters with the usual rules. Bugs: The code now do several (de)allocation operations in every send, this may have a negative effect on performance when used with slow memory managers. License: This code is distributed under the same license used by strophe (i.e., GPLv3 or MIT). diff --git a/src/stanza.c b/src/stanza.c index 6bf11b8..4ecc558 100644 --- a/src/stanza.c +++ b/src/stanza.c @@ -206,6 +206,64 @@ int xmpp_stanza_is_tag(xmpp_stanza_t * const stanza) return (stanza && stanza->type == XMPP_STANZA_TAG); } +/* Escape a string with for use in a XML text node or attribute. Assumes that + * the input string is encoded in UTF-8. On sucess, returns a pointer to a + * buffer with the resulting data which must be xmpp_free()'d by the caller. + * On failure, returns NULL. + */ + +static char *_escape_xml(xmpp_ctx_t * const ctx, char *text) +{ + size_t len = 0; + char *src; + char *dst; + char *buf; + for (src = text; *src != '\0'; src++) { + switch (*src) { + case '<': /* "<" */ + case '>': /* ">" */ + len += 4; + break; + case '&': /* "&" */ + len += 5; + break; + case '"': + len += 6; /*""" */ + break; + default: + len++; + } + } + if ((buf = xmpp_alloc(ctx, (len+1) * sizeof(char))) == NULL) + return NULL; /* Error */ + dst = buf; + for (src = text; *src != '\0'; src++) { + switch (*src) { + case '<': + strcpy(dst, "<"); + dst += 4; + break; + case '>': + strcpy(dst, ">"); + dst += 4; + break; + case '&': + strcpy(dst, "&"); + dst += 5; + break; + case '"': + strcpy(dst, """); + dst += 6; + break; + default: + *dst = *src; + dst++; + } + } + *dst = '\0'; + return buf; +} + /* small helper function */ static inline void _render_update(int *written, const int length, const int lastwrite, @@ -236,6 +294,7 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza, xmpp_stanza_t *child; hash_iterator_t *iter; const char *key; + char *tmp; written = 0; @@ -244,7 +303,10 @@ static int _render_stanza_recursive(xmpp_stanza_t *stanza, if (stanza->type == XMPP_STANZA_TEXT) { if (!stanza->data) return XMPP_EINVOP; - ret = xmpp_snprintf(ptr, left, "%s", stanza->data); + tmp = _escape_xml(stanza->ctx, stanza->data); + if (tmp == NULL) return XMPP_EMEM; + ret = xmpp_snprintf(ptr, left, "%s", tmp); + xmpp_free(stanza->ctx, tmp); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); } else { /* stanza->type == XMPP_STANZA_TAG */ @@ -258,8 +320,11 @@ 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))) { - ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, - (char *)hash_get(stanza->attributes, key)); + tmp = _escape_xml(stanza->ctx, + (char *)hash_get(stanza->attributes, key)); + if (tmp == NULL) return XMPP_EMEM; + ret = xmpp_snprintf(ptr, left, " %s=\"%s\"", key, tmp); + xmpp_free(stanza->ctx, tmp); if (ret < 0) return XMPP_EMEM; _render_update(&written, buflen, ret, &left, &ptr); }