source: lib/xmltree.c @ d0752e8

Last change on this file since d0752e8 was d0752e8, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-09-22T12:12:12Z

Little cleanup. Use xt_from_string() where possible.

  • Property mode set to 100644
File size: 17.6 KB
RevLine 
[8f243ad]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
5*                                                                           *
6*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
7*                                                                           *
8*  This library is free software; you can redistribute it and/or            *
9*  modify it under the terms of the GNU Lesser General Public               *
10*  License as published by the Free Software Foundation, version            *
11*  2.1.                                                                     *
12*                                                                           *
13*  This library is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
16*  Lesser General Public License for more details.                          *
17*                                                                           *
18*  You should have received a copy of the GNU Lesser General Public License *
19*  along with this library; if not, write to the Free Software Foundation,  *
20*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
21*                                                                           *
22****************************************************************************/
23
24#include <glib.h>
25#include <string.h>
26#include <unistd.h>
27#include <ctype.h>
28#include <stdio.h>
29
30#include "xmltree.h"
31
[7a2a486]32#define g_strcasecmp g_ascii_strcasecmp
33#define g_strncasecmp g_ascii_strncasecmp
34
[8f243ad]35static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
36{
37        struct xt_parser *xt = data;
38        struct xt_node *node = g_new0( struct xt_node, 1 ), *nt;
39        int i;
40       
41        node->parent = xt->cur;
42        node->name = g_strdup( element_name );
43       
44        /* First count the number of attributes */
45        for( i = 0; attr_names[i]; i ++ );
46       
47        /* Then allocate a NULL-terminated array. */
48        node->attr = g_new0( struct xt_attr, i + 1 );
49       
50        /* And fill it, saving one variable by starting at the end. */
51        for( i --; i >= 0; i -- )
52        {
53                node->attr[i].key = g_strdup( attr_names[i] );
54                node->attr[i].value = g_strdup( attr_values[i] );
55        }
56       
57        /* Add it to the linked list of children nodes, if we have a current
58           node yet. */
59        if( xt->cur )
60        {
61                if( xt->cur->children )
62                {
63                        for( nt = xt->cur->children; nt->next; nt = nt->next );
64                        nt->next = node;
65                }
66                else
67                {
68                        xt->cur->children = node;
69                }
70        }
71        else if( xt->root )
72        {
73                /* ERROR situation: A second root-element??? */
74        }
75       
76        /* Now this node will be the new current node. */
77        xt->cur = node;
78        /* And maybe this is the root? */
79        if( xt->root == NULL )
80                xt->root = node;
81}
82
83static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
84{
85        struct xt_parser *xt = data;
86        struct xt_node *node = xt->cur;
87       
88        if( node == NULL )
89                return;
90       
91        /* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */
92        node->text = g_renew( char, node->text, node->text_len + text_len + 1 );
93        memcpy( node->text + node->text_len, text, text_len );
94        node->text_len += text_len;
95        /* Zero termination is always nice to have. */
96        node->text[node->text_len] = 0;
97}
98
99static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
100{
101        struct xt_parser *xt = data;
102       
103        xt->cur->flags |= XT_COMPLETE;
104        xt->cur = xt->cur->parent;
105}
106
107GMarkupParser xt_parser_funcs =
108{
109        xt_start_element,
110        xt_end_element,
111        xt_text,
112        NULL,
113        NULL
114};
115
[4bbcba3]116struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data )
[8f243ad]117{
118        struct xt_parser *xt = g_new0( struct xt_parser, 1 );
119       
120        xt->data = data;
[4bbcba3]121        xt->handlers = handlers;
[8f243ad]122        xt_reset( xt );
123       
124        return xt;
125}
126
127/* Reset the parser, flush everything we have so far. For example, we need
128   this for XMPP when doing TLS/SASL to restart the stream. */
129void xt_reset( struct xt_parser *xt )
130{
131        if( xt->parser )
132                g_markup_parse_context_free( xt->parser );
133       
134        xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL );
135       
136        if( xt->root )
137        {
138                xt_free_node( xt->root );
139                xt->root = NULL;
140                xt->cur = NULL;
141        }
142}
143
144/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
145   end-of-stream and 1 otherwise. */
[d93c0eb9]146int xt_feed( struct xt_parser *xt, const char *text, int text_len )
[8f243ad]147{
148        if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
149        {
150                return -1;
151        }
152       
153        return !( xt->root && xt->root->flags & XT_COMPLETE );
154}
155
156/* Find completed nodes and see if a handler has to be called. Passing
157   a node isn't necessary if you want to start at the root, just pass
[101d84f]158   NULL. This second argument is needed for recursive calls. */
159int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
[8f243ad]160{
161        struct xt_node *c;
162        xt_status st;
163        int i;
164       
[101d84f]165        /* Just in case someone likes infinite loops... */
166        if( xt->root == NULL )
167                return 0;
168       
[8f243ad]169        if( node == NULL )
[101d84f]170                return xt_handle( xt, xt->root, depth );
[8f243ad]171       
[101d84f]172        if( depth != 0 )
173                for( c = node->children; c; c = c->next )
174                        if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) )
175                                return 0;
[8f243ad]176       
177        if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
178        {
[4452e69]179                if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ )
[8f243ad]180                {
181                        /* This one is fun! \o/ */
182                       
[4452e69]183                            /* If handler.name == NULL it means it should always match. */
[8f243ad]184                        if( ( xt->handlers[i].name == NULL || 
[4452e69]185                              /* If it's not, compare. There should always be a name. */
[8f243ad]186                              g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
[4452e69]187                            /* If handler.parent == NULL, it's a match. */
[8f243ad]188                            ( xt->handlers[i].parent == NULL ||
[4452e69]189                              /* If there's a parent node, see if the name matches. */
[8f243ad]190                              ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
[4452e69]191                              /* If there's no parent, the handler should mention <root> as a parent. */
192                                               strcmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
[8f243ad]193                        {
194                                st = xt->handlers[i].func( node, xt->data );
195                               
196                                if( st == XT_ABORT )
197                                        return 0;
198                                else if( st != XT_NEXT )
199                                        break;
200                        }
201                }
202               
203                node->flags |= XT_SEEN;
204        }
205       
206        return 1;
207}
208
209/* Garbage collection: Cleans up all nodes that are handled. Useful for
210   streams because there's no reason to keep a complete packet history
211   in memory. */
[101d84f]212void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
[8f243ad]213{
214        struct xt_node *c, *prev;
215       
[21167d2]216        if( !xt || !xt->root )
217                return;
218       
[8f243ad]219        if( node == NULL )
[daae10f]220        {
221                xt_cleanup( xt, xt->root, depth );
222                return;
223        }
[8f243ad]224       
225        if( node->flags & XT_SEEN && node == xt->root )
226        {
227                xt_free_node( xt->root );
228                xt->root = xt->cur = NULL;
229                /* xt->cur should be NULL already, BTW... */
230               
231                return;
232        }
233       
234        /* c contains the current node, prev the previous node (or NULL).
235           I admit, this one's pretty horrible. */
236        for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
237        {
238                if( c->flags & XT_SEEN )
239                {
240                        /* Remove the node from the linked list. */
241                        if( prev )
242                                prev->next = c->next;
243                        else
244                                node->children = c->next;
245                       
246                        xt_free_node( c );
247                       
248                        /* Since the for loop wants to get c->next, make sure
249                           c points at something that exists (and that c->next
250                           will actually be the next item we should check). c
251                           can be NULL now, if we just removed the first item.
252                           That explains the ? thing in for(). */
253                        c = prev;
254                }
255                else
256                {
257                        /* This node can't be cleaned up yet, but maybe a
258                           subnode can. */
[101d84f]259                        if( depth != 0 )
260                                xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
[8f243ad]261                }
262        }
263}
264
[d0752e8]265struct xt_node *xt_from_string( const char *in, int len )
[d93c0eb9]266{
267        struct xt_parser *parser;
268        struct xt_node *ret;
269       
[d0752e8]270        if( len == 0 )
271                len = strlen( in );
272       
[d93c0eb9]273        parser = xt_new( NULL, NULL );
[d0752e8]274        xt_feed( parser, in, len );
[d93c0eb9]275        ret = parser->root;
276        parser->root = NULL;
277        xt_free( parser );
278       
279        return ret;
280}
281
[8f243ad]282static void xt_to_string_real( struct xt_node *node, GString *str )
283{
284        char *buf;
285        struct xt_node *c;
286        int i;
287       
288        g_string_append_printf( str, "<%s", node->name );
289       
290        for( i = 0; node->attr[i].key; i ++ )
291        {
292                buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
293                g_string_append( str, buf );
294                g_free( buf );
295        }
296       
297        if( node->text == NULL && node->children == NULL )
298        {
299                g_string_append( str, "/>" );
300                return;
301        }
302       
303        g_string_append( str, ">" );
304        if( node->text_len > 0 )
305        {
306                buf = g_markup_escape_text( node->text, node->text_len );
307                g_string_append( str, buf );
308                g_free( buf );
309        }
310       
311        for( c = node->children; c; c = c->next )
312                xt_to_string_real( c, str );
313       
314        g_string_append_printf( str, "</%s>", node->name );
315}
316
317char *xt_to_string( struct xt_node *node )
318{
319        GString *ret;
320        char *real;
321       
322        ret = g_string_new( "" );
323        xt_to_string_real( node, ret );
324       
325        real = ret->str;
326        g_string_free( ret, FALSE );
327       
328        return real;
329}
330
331void xt_print( struct xt_node *node )
332{
333        int i;
334        struct xt_node *c;
335       
336        /* Indentation */
337        for( c = node; c->parent; c = c->parent )
[ca974d7]338                fprintf( stderr, "    " );
[8f243ad]339       
340        /* Start the tag */
[ca974d7]341        fprintf( stderr, "<%s", node->name );
[8f243ad]342       
343        /* Print the attributes */
344        for( i = 0; node->attr[i].key; i ++ )
[327af51]345        {
346                char *v = g_markup_escape_text( node->attr[i].value, -1 );
[ca974d7]347                fprintf( stderr, " %s=\"%s\"", node->attr[i].key, v );
[327af51]348                g_free( v );
349        }
[8f243ad]350       
351        /* /> in case there's really *nothing* inside this tag, otherwise
352           just >. */
353        /* If this tag doesn't have any content at all... */
354        if( node->text == NULL && node->children == NULL )
355        {
[ca974d7]356                fprintf( stderr, "/>\n" );
[8f243ad]357                return;
358                /* Then we're finished! */
359        }
360       
361        /* Otherwise... */
[ca974d7]362        fprintf( stderr, ">" );
[8f243ad]363       
364        /* Only print the text if it contains more than whitespace (TEST). */
365        if( node->text_len > 0 )
366        {
367                for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
368                if( node->text[i] )
[327af51]369                {
370                        char *v = g_markup_escape_text( node->text, -1 );
[ca974d7]371                        fprintf( stderr, "%s", v );
[327af51]372                        g_free( v );
373                }
[8f243ad]374        }
375       
376        if( node->children )
[ca974d7]377                fprintf( stderr, "\n" );
[8f243ad]378       
379        for( c = node->children; c; c = c->next )
380                xt_print( c );
381       
382        if( node->children )
383                for( c = node; c->parent; c = c->parent )
[ca974d7]384                        fprintf( stderr, "    " );
[8f243ad]385       
386        /* Non-empty tag is now finished. */
[ca974d7]387        fprintf( stderr, "</%s>\n", node->name );
[8f243ad]388}
389
[022df46]390struct xt_node *xt_dup( struct xt_node *node )
391{
392        struct xt_node *dup = g_new0( struct xt_node, 1 );
393        struct xt_node *c, *dc = NULL;
394        int i;
395       
396        /* Let's NOT copy the parent element here BTW! Only do it for children. */
397       
398        dup->name = g_strdup( node->name );
399        dup->flags = node->flags;
400        if( node->text )
401        {
402                dup->text = g_memdup( node->text, node->text_len + 1 );
403                dup->text_len = node->text_len;
404        }
405       
406        /* Count the number of attributes and allocate the new array. */
407        for( i = 0; node->attr[i].key; i ++ );
408        dup->attr = g_new0( struct xt_attr, i + 1 );
409       
410        /* Copy them all! */
411        for( i --; i >= 0; i -- )
412        {
413                dup->attr[i].key = g_strdup( node->attr[i].key );
414                dup->attr[i].value = g_strdup( node->attr[i].value );
415        }
416       
417        /* This nice mysterious loop takes care of the children. */
418        for( c = node->children; c; c = c->next )
419        {
420                if( dc == NULL )
421                        dc = dup->children = xt_dup( c );
422                else
423                        dc = ( dc->next = xt_dup( c ) );
424               
425                dc->parent = dup;
426        }
427       
428        return dup;
429}
430
[8f243ad]431/* Frees a node. This doesn't clean up references to itself from parents! */
432void xt_free_node( struct xt_node *node )
433{
434        int i;
435       
[21167d2]436        if( !node )
437                return;
438       
[8f243ad]439        g_free( node->name );
440        g_free( node->text );
441       
442        for( i = 0; node->attr[i].key; i ++ )
443        {
444                g_free( node->attr[i].key );
445                g_free( node->attr[i].value );
446        }
447        g_free( node->attr );
448       
449        while( node->children )
450        {
451                struct xt_node *next = node->children->next;
452               
453                xt_free_node( node->children );
454                node->children = next;
455        }
456       
457        g_free( node );
458}
459
460void xt_free( struct xt_parser *xt )
461{
[21167d2]462        if( !xt )
463                return;
464       
[8f243ad]465        if( xt->root )
466                xt_free_node( xt->root );
467       
468        g_markup_parse_context_free( xt->parser );
469       
470        g_free( xt );
471}
472
473/* To find a node's child with a specific name, pass the node's children
474   list, not the node itself! The reason you have to do this by hand: So
475   that you can also use this function as a find-next. */
[40ef702]476struct xt_node *xt_find_node( struct xt_node *node, const char *name )
[8f243ad]477{
478        while( node )
479        {
[3742fb6]480                char *colon;
481               
482                if( g_strcasecmp( node->name, name ) == 0 ||
483                    ( ( colon = strchr( node->name, ':' ) ) &&
484                      g_strcasecmp( colon + 1, name ) == 0 ) )
[8f243ad]485                        break;
486               
487                node = node->next;
488        }
489       
490        return node;
491}
492
[d912fe4]493/* More advanced than the one above, understands something like
494   ../foo/bar to find a subnode bar of a node foo which is a child
495   of node's parent. Pass the node directly, not its list of children. */
496struct xt_node *xt_find_path( struct xt_node *node, const char *name )
497{
498        while( name && *name && node )
499        {
500                char *colon, *slash;
501                int n;
502               
503                if( ( slash = strchr( name, '/' ) ) )
504                        n = slash - name;
505                else
506                        n = strlen( name );
507               
508                if( strncmp( name, "..", n ) == 0 )
509                {
510                        node = node->parent;
511                }
512                else
513                {
514                        node = node->children;
515                       
516                        while( node )
517                        {
518                                if( g_strncasecmp( node->name, name, n ) == 0 ||
519                                    ( ( colon = strchr( node->name, ':' ) ) &&
520                                      g_strncasecmp( colon + 1, name, n ) == 0 ) )
521                                        break;
522                               
523                                node = node->next;
524                        }
525                }
526               
527                name = slash ? slash + 1 : NULL;
528        }
529       
530        return node;
531}
532
[40ef702]533char *xt_find_attr( struct xt_node *node, const char *key )
[8f243ad]534{
535        int i;
[3742fb6]536        char *colon;
[8f243ad]537       
[21167d2]538        if( !node )
539                return NULL;
540       
[8f243ad]541        for( i = 0; node->attr[i].key; i ++ )
542                if( g_strcasecmp( node->attr[i].key, key ) == 0 )
543                        break;
544       
[3742fb6]545        /* This is an awful hack that only takes care of namespace prefixes
546           inside a tag. Since IMHO excessive namespace usage in XMPP is
547           massive overkill anyway (this code exists for almost four years
548           now and never really missed it): Meh. */
549        if( !node->attr[i].key && strcmp( key, "xmlns" ) == 0 &&
550            ( colon = strchr( node->name, ':' ) ) )
551        {
552                *colon = '\0';
553                for( i = 0; node->attr[i].key; i ++ )
554                        if( strncmp( node->attr[i].key, "xmlns:", 6 ) == 0 &&
555                            strcmp( node->attr[i].key + 6, node->name ) == 0 )
556                                break;
557                *colon = ':';
558        }
559       
[8f243ad]560        return node->attr[i].value;
561}
562
[e6b41b1]563/* Strip a few non-printable characters that aren't allowed in XML streams
564   (and upset some XMPP servers for example). */
565void xt_strip_text( char *in )
566{
567        char *out = in;
568        static const char nonprint[32] = {
569                0, 0, 0, 0, 0, 0, 0, 0, /* 0..7 */
570                0, 1, 1, 0, 0, 1, 0, 0, /* 9 (tab), 10 (\n), 13 (\r) */
571        };
572       
573        if( !in )
574                return;
575
576        while( *in )
577        {
578                if( (unsigned int) *in >= ' ' || nonprint[(unsigned int) *in] )
579                        *out ++ = *in;
580                in ++;
581        }
582        *out = *in;
583}
584
[94acdd0]585struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children )
[8f243ad]586{
587        struct xt_node *node, *c;
588       
589        node = g_new0( struct xt_node, 1 );
590        node->name = g_strdup( name );
591        node->children = children;
592        node->attr = g_new0( struct xt_attr, 1 );
593       
594        if( text )
595        {
[e6b41b1]596                node->text = g_strdup( text );
597                xt_strip_text( node->text );
598                node->text_len = strlen( node->text );
[8f243ad]599        }
600       
601        for( c = children; c; c = c->next )
602        {
603                if( c->parent != NULL )
604                {
605                        /* ERROR CONDITION: They seem to have a parent already??? */
606                }
607               
608                c->parent = node;
609        }
610       
611        return node;
612}
613
614void xt_add_child( struct xt_node *parent, struct xt_node *child )
615{
616        struct xt_node *node;
617       
618        /* This function can actually be used to add more than one child, so
619           do handle this properly. */
620        for( node = child; node; node = node->next )
621        {
622                if( node->parent != NULL )
623                {
624                        /* ERROR CONDITION: They seem to have a parent already??? */
625                }
626               
627                node->parent = parent;
628        }
629       
630        if( parent->children == NULL )
631        {
632                parent->children = child;
633        }
634        else
635        {
636                for( node = parent->children; node->next; node = node->next );
637                node->next = child;
638        }
639}
640
[b46769d]641/* Same, but at the beginning. */
642void xt_insert_child( struct xt_node *parent, struct xt_node *child )
643{
[afb9ea9]644        struct xt_node *node, *last = NULL;
645       
646        if( child == NULL )
647                return; /* BUG */
[b46769d]648       
649        for( node = child; node; node = node->next )
650        {
651                if( node->parent != NULL )
652                {
653                        /* ERROR CONDITION: They seem to have a parent already??? */
654                }
655               
656                node->parent = parent;
657                last = node;
658        }
659       
660        last->next = parent->children;
661        parent->children = child;
662}
663
[40ef702]664void xt_add_attr( struct xt_node *node, const char *key, const char *value )
[8f243ad]665{
666        int i;
667       
[090f1cb]668        /* Now actually it'd be nice if we can also change existing attributes
669           (which actually means this function doesn't have the right name).
670           So let's find out if we have this attribute already... */
671        for( i = 0; node->attr[i].key; i ++ )
672                if( strcmp( node->attr[i].key, key ) == 0 )
673                        break;
674       
675        if( node->attr[i].key == NULL )
676        {
677                /* If not, allocate space for a new attribute. */
678                node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
679                node->attr[i].key = g_strdup( key );
680                node->attr[i+1].key = NULL;
681        }
682        else
683        {
684                /* Otherwise, free the old value before setting the new one. */
685                g_free( node->attr[i].value );
686        }
687       
[8f243ad]688        node->attr[i].value = g_strdup( value );
689}
[259edd4]690
[40ef702]691int xt_remove_attr( struct xt_node *node, const char *key )
[259edd4]692{
693        int i, last;
694       
695        for( i = 0; node->attr[i].key; i ++ )
696                if( strcmp( node->attr[i].key, key ) == 0 )
697                        break;
698       
699        /* If we didn't find the attribute... */
700        if( node->attr[i].key == NULL )
701                return 0;
702       
703        g_free( node->attr[i].key );
704        g_free( node->attr[i].value );
705       
706        /* If it's the last, this is easy: */
707        if( node->attr[i+1].key == NULL )
708        {
709                node->attr[i].key = node->attr[i].value = NULL;
710        }
711        else /* It's also pretty easy, actually. */
712        {
713                /* Find the last item. */
714                for( last = i + 1; node->attr[last+1].key; last ++ );
715               
716                node->attr[i] = node->attr[last];
717                node->attr[last].key = NULL;
718                node->attr[last].value = NULL;
719        }
720       
721        /* Let's not bother with reallocating memory here. It takes time and
722           most packets don't stay in memory for long anyway. */
723       
724        return 1;
725}
Note: See TracBrowser for help on using the repository browser.