The use of 0xff as a separator for libexpat only works on arch where char is defined as an unsigned char. This is an unportable behaviour. Before this patch this is what I experienced: xmpp DEBUG sock_connect to im.bouledef.eu:5222 returned 3 xmpp DEBUG attempting to connect to im.bouledef.eu xmpp DEBUG connection successful conn DEBUG SENT: <?xml version="1.0"?><stream:stream to="bouledef.eu" xml:lang="en" version="1.0" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams"> conn ERROR Server did not open valid stream. xmpp DEBUG Closing socket. DEBUG: disconnected event DEBUG Stopping event loop. xmpp DEBUG RECV: <http://etherx.jabber.org/streamsÿfeatures><urn:ietf:params:xml:ns:xmpp-tlsÿstarttls><urn:ietf:params:xml:ns:xmpp-tlsÿrequired/></urn:ietf:params:xml:ns:xmpp-tlsÿstarttls></http://etherx.jabber.org/streamsÿfeatures> event DEBUG Event loop completed. name = http://etherx.jabber.org/streamsÿstream
263 lines
6.4 KiB
C
263 lines
6.4 KiB
C
/* parser.c
|
|
** strophe XMPP client library -- xml parser handlers and utility functions
|
|
**
|
|
** Copyright (C) 2005-2009 Collecta, Inc.
|
|
**
|
|
** This software is provided AS-IS with no warranty, either express
|
|
** or implied.
|
|
**
|
|
** This program is dual licensed under the MIT and GPLv3 licenses.
|
|
*/
|
|
|
|
/** @file
|
|
* XML parser handlers.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <expat.h>
|
|
|
|
#include <strophe.h>
|
|
#include "common.h"
|
|
#include "parser.h"
|
|
|
|
/* Use the Unit Separator to delimit namespace and name in our XML*/
|
|
#define NAMESPACE_SEP ('\x1F')
|
|
|
|
struct _parser_t {
|
|
xmpp_ctx_t *ctx;
|
|
XML_Parser expat;
|
|
parser_start_callback startcb;
|
|
parser_end_callback endcb;
|
|
parser_stanza_callback stanzacb;
|
|
void *userdata;
|
|
int depth;
|
|
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) {
|
|
/* 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 *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 */
|
|
if (parser->startcb)
|
|
parser->startcb((char *)name, (char **)attrs,
|
|
parser->userdata);
|
|
} else {
|
|
/* build stanzas at depth 1 */
|
|
if (!parser->stanza && parser->depth != 1) {
|
|
/* something terrible happened */
|
|
/* FIXME: shutdown disconnect */
|
|
xmpp_error(parser->ctx, "parser", "oops, where did our stanza go?");
|
|
} else if (!parser->stanza) {
|
|
/* starting a new toplevel stanza */
|
|
parser->stanza = xmpp_stanza_new(parser->ctx);
|
|
if (!parser->stanza) {
|
|
/* FIXME: can't allocate, disconnect */
|
|
}
|
|
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);
|
|
if (!child) {
|
|
/* FIXME: can't allocate, disconnect */
|
|
}
|
|
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);
|
|
|
|
/* the child is owned by the toplevel stanza now */
|
|
xmpp_stanza_release(child);
|
|
|
|
/* make child the current stanza */
|
|
parser->stanza = child;
|
|
}
|
|
}
|
|
|
|
if (ns) xmpp_free(parser->ctx, ns);
|
|
if (name) xmpp_free(parser->ctx, name);
|
|
|
|
parser->depth++;
|
|
}
|
|
|
|
static void _end_element(void *userdata, const XML_Char *name)
|
|
{
|
|
parser_t *parser = (parser_t *)userdata;
|
|
|
|
parser->depth--;
|
|
|
|
if (parser->depth == 0) {
|
|
/* notify the owner */
|
|
if (parser->endcb)
|
|
parser->endcb((char *)name, parser->userdata);
|
|
} else {
|
|
if (parser->stanza->parent) {
|
|
/* we're finishing a child stanza, so set current to the parent */
|
|
parser->stanza = parser->stanza->parent;
|
|
} else {
|
|
if (parser->stanzacb)
|
|
parser->stanzacb(parser->stanza,
|
|
parser->userdata);
|
|
xmpp_stanza_release(parser->stanza);
|
|
parser->stanza = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void _characters(void *userdata, const XML_Char *s, int len)
|
|
{
|
|
parser_t *parser = (parser_t *)userdata;
|
|
xmpp_stanza_t *stanza;
|
|
|
|
if (parser->depth < 2) return;
|
|
|
|
/* create and populate stanza */
|
|
stanza = xmpp_stanza_new(parser->ctx);
|
|
if (!stanza) {
|
|
/* FIXME: allocation error, disconnect */
|
|
return;
|
|
}
|
|
xmpp_stanza_set_text_with_size(stanza, s, len);
|
|
|
|
xmpp_stanza_add_child(parser->stanza, stanza);
|
|
xmpp_stanza_release(stanza);
|
|
}
|
|
|
|
parser_t *parser_new(xmpp_ctx_t *ctx,
|
|
parser_start_callback startcb,
|
|
parser_end_callback endcb,
|
|
parser_stanza_callback stanzacb,
|
|
void *userdata)
|
|
{
|
|
parser_t *parser;
|
|
|
|
parser = xmpp_alloc(ctx, sizeof(parser_t));
|
|
if (parser != NULL) {
|
|
parser->ctx = ctx;
|
|
parser->expat = NULL;
|
|
parser->startcb = startcb;
|
|
parser->endcb = endcb;
|
|
parser->stanzacb = stanzacb;
|
|
parser->userdata = userdata;
|
|
parser->depth = 0;
|
|
parser->stanza = NULL;
|
|
|
|
parser_reset(parser);
|
|
}
|
|
|
|
return parser;
|
|
}
|
|
|
|
char* parser_attr_name(xmpp_ctx_t *ctx, char *nsname)
|
|
{
|
|
return _xml_name(ctx, nsname);
|
|
}
|
|
|
|
/* free a parser */
|
|
void parser_free(parser_t *parser)
|
|
{
|
|
if (parser->expat)
|
|
XML_ParserFree(parser->expat);
|
|
|
|
xmpp_free(parser->ctx, parser);
|
|
}
|
|
|
|
/* shuts down and restarts XML parser. true on success */
|
|
int parser_reset(parser_t *parser)
|
|
{
|
|
if (parser->expat)
|
|
XML_ParserFree(parser->expat);
|
|
|
|
if (parser->stanza)
|
|
xmpp_stanza_release(parser->stanza);
|
|
|
|
parser->expat = XML_ParserCreateNS(NULL, NAMESPACE_SEP);
|
|
if (!parser->expat) return 0;
|
|
|
|
parser->depth = 0;
|
|
parser->stanza = NULL;
|
|
|
|
XML_SetUserData(parser->expat, parser);
|
|
XML_SetElementHandler(parser->expat, _start_element, _end_element);
|
|
XML_SetCharacterDataHandler(parser->expat, _characters);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int parser_feed(parser_t *parser, char *chunk, int len)
|
|
{
|
|
return XML_Parse(parser->expat, chunk, len, 0);
|
|
}
|