source: lib/xmltree.c @ f01bc6f

Last change on this file since f01bc6f was afb9ea9, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-10-07T06:25:35Z

Silencing some (mostly whiny) compiler warnings.

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