/***************************************************************************\ * * * BitlBee - An IRC to IM gateway * * Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly) * * * * Copyright 2006 Wilmer van der Gaast * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation, version * * 2.1. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public License * * along with this library; if not, write to the Free Software Foundation, * * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * * ****************************************************************************/ #include #include #include #include #include #include "xmltree.h" static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error ) { struct xt_parser *xt = data; struct xt_node *node = g_new0( struct xt_node, 1 ), *nt; int i; node->parent = xt->cur; node->name = g_strdup( element_name ); /* First count the number of attributes */ for( i = 0; attr_names[i]; i ++ ); /* Then allocate a NULL-terminated array. */ node->attr = g_new0( struct xt_attr, i + 1 ); /* And fill it, saving one variable by starting at the end. */ for( i --; i >= 0; i -- ) { node->attr[i].key = g_strdup( attr_names[i] ); node->attr[i].value = g_strdup( attr_values[i] ); } /* Add it to the linked list of children nodes, if we have a current node yet. */ if( xt->cur ) { if( xt->cur->children ) { for( nt = xt->cur->children; nt->next; nt = nt->next ); nt->next = node; } else { xt->cur->children = node; } } else if( xt->root ) { /* ERROR situation: A second root-element??? */ } /* Now this node will be the new current node. */ xt->cur = node; /* And maybe this is the root? */ if( xt->root == NULL ) xt->root = node; } static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error ) { struct xt_parser *xt = data; struct xt_node *node = xt->cur; if( node == NULL ) return; /* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */ node->text = g_renew( char, node->text, node->text_len + text_len + 1 ); memcpy( node->text + node->text_len, text, text_len ); node->text_len += text_len; /* Zero termination is always nice to have. */ node->text[node->text_len] = 0; } static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error ) { struct xt_parser *xt = data; xt->cur->flags |= XT_COMPLETE; xt->cur = xt->cur->parent; } GMarkupParser xt_parser_funcs = { xt_start_element, xt_end_element, xt_text, NULL, NULL }; struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ) { struct xt_parser *xt = g_new0( struct xt_parser, 1 ); xt->data = data; xt->handlers = handlers; xt_reset( xt ); return xt; } /* Reset the parser, flush everything we have so far. For example, we need this for XMPP when doing TLS/SASL to restart the stream. */ void xt_reset( struct xt_parser *xt ) { if( xt->parser ) g_markup_parse_context_free( xt->parser ); xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL ); if( xt->root ) { xt_free_node( xt->root ); xt->root = NULL; xt->cur = NULL; } } /* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on end-of-stream and 1 otherwise. */ int xt_feed( struct xt_parser *xt, const char *text, int text_len ) { if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) ) { return -1; } return !( xt->root && xt->root->flags & XT_COMPLETE ); } /* Find completed nodes and see if a handler has to be called. Passing a node isn't necessary if you want to start at the root, just pass NULL. This second argument is needed for recursive calls. */ int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ) { struct xt_node *c; xt_status st; int i; /* Just in case someone likes infinite loops... */ if( xt->root == NULL ) return 0; if( node == NULL ) return xt_handle( xt, xt->root, depth ); if( depth != 0 ) for( c = node->children; c; c = c->next ) if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) ) return 0; if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) ) { if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ ) { /* This one is fun! \o/ */ /* If handler.name == NULL it means it should always match. */ if( ( xt->handlers[i].name == NULL || /* If it's not, compare. There should always be a name. */ g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) && /* If handler.parent == NULL, it's a match. */ ( xt->handlers[i].parent == NULL || /* If there's a parent node, see if the name matches. */ ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : /* If there's no parent, the handler should mention as a parent. */ strcmp( xt->handlers[i].parent, "" ) == 0 ) ) ) { st = xt->handlers[i].func( node, xt->data ); if( st == XT_ABORT ) return 0; else if( st != XT_NEXT ) break; } } node->flags |= XT_SEEN; } return 1; } /* Garbage collection: Cleans up all nodes that are handled. Useful for streams because there's no reason to keep a complete packet history in memory. */ void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ) { struct xt_node *c, *prev; if( !xt || !xt->root ) return; if( node == NULL ) { xt_cleanup( xt, xt->root, depth ); return; } if( node->flags & XT_SEEN && node == xt->root ) { xt_free_node( xt->root ); xt->root = xt->cur = NULL; /* xt->cur should be NULL already, BTW... */
/*
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is expat.

The Initial Developer of the Original Code is James Clark.
Portions created by James Clark are Copyright (C) 1998, 1999
James Clark. All Rights Reserved.

Contributor(s):

Alternatively, the contents of this file may be used under the terms
of the GNU General Public License (the "GPL"), in which case the
provisions of the GPL are applicable instead of those above.  If you
wish to allow use of your version of this file only under the terms of
the GPL and not to allow others to use your version of this file under
the MPL, indicate your decision by deleting the provisions above and
replace them with the notice and other provisions required by the
GPL. If you do not delete the provisions above, a recipient may use
your version of this file under either the MPL or the GPL.
*/

#ifndef XmlRole_INCLUDED
#define XmlRole_INCLUDED 1

#include "xmltok.h"

#ifdef __cplusplus
extern "C" {
#endif

enum {
  XML_ROLE_ERROR = -1,
  XML_ROLE_NONE = 0,
  XML_ROLE_XML_DECL,
  XML_ROLE_INSTANCE_START,
  XML_ROLE_DOCTYPE_NAME,
  XML_ROLE_DOCTYPE_SYSTEM_ID,
  XML_ROLE_DOCTYPE_PUBLIC_ID,
  XML_ROLE_DOCTYPE_CLOSE,
  XML_ROLE_GENERAL_ENTITY_NAME,
  XML_ROLE_PARAM_ENTITY_NAME,
  XML_ROLE_ENTITY_VALUE,
  XML_ROLE_ENTITY_SYSTEM_ID,
  XML_ROLE_ENTITY_PUBLIC_ID,
  XML_ROLE_ENTITY_NOTATION_NAME,
  XML_ROLE_NOTATION_NAME,
  XML_ROLE_NOTATION_SYSTEM_ID,
  XML_ROLE_NOTATION_NO_SYSTEM_ID,
  XML_ROLE_NOTATION_PUBLIC_ID,
  XML_ROLE_ATTRIBUTE_NAME,
  XML_ROLE_ATTRIBUTE_TYPE_CDATA,
  XML_ROLE_ATTRIBUTE_TYPE_ID,
  XML_ROLE_ATTRIBUTE_TYPE_IDREF,
  XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
  XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
  XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
  XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
  XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
  XML_ROLE_ATTRIBUTE_ENUM_VALUE,
  XML_ROLE_ATTRIBUTE_NOTATION_VALUE,