Files
libstrophe-gh-mirror/src/parser_expat.c
2009-04-14 15:36:03 -06:00

203 lines
5.1 KiB
C

/* parser.c
** strophe XMPP client library -- xml parser handlers and utility functions
**
** Copyright (C) 2005-2008 OGG, LLC. All rights reserved.
**
** This software is provided AS-IS with no warranty, either express
** or implied.
**
** This software is distributed under license and may not be copied,
** modified or distributed except as expressly authorized under the
** terms of the license contained in the file LICENSE.txt in this
** distribution.
*/
/** @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"
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;
};
static void _set_attributes(xmpp_stanza_t *stanza, const XML_Char **attrs)
{
int i;
if (!attrs) return;
for (i = 0; attrs[i]; i += 2) {
xmpp_stanza_set_attribute(stanza, attrs[i], attrs[i+1]);
}
}
static void _start_element(void *userdata,
const XML_Char *name,
const XML_Char **attrs)
{
parser_t *parser = (parser_t *)userdata;
xmpp_stanza_t *child;
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);
} 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);
/* 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;
}
}
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;
}
/* 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_ParserCreate(NULL);
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);
}