source: lib/xmltree.c @ 4c73ba62

Last change on this file since 4c73ba62 was ca974d7, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-04T19:14:29Z

Debug output tweaks: Try to send everything to stderr, and add ifdef to
enable printing of all SSL traffic.

  • Property mode set to 100644
File size: 17.5 KB
Line 
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
113struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data )
114{
115        struct xt_parser *xt = g_new0( struct xt_parser, 1 );
116       
117        xt->data = data;
118        xt->handlers = handlers;
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. */
143int xt_feed( struct xt_parser *xt, const char *text, int text_len )
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
155   NULL. This second argument is needed for recursive calls. */
156int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
157{
158        struct xt_node *c;
159        xt_status st;
160        int i;
161       
162        /* Just in case someone likes infinite loops... */
163        if( xt->root == NULL )
164                return 0;
165       
166        if( node == NULL )
167                return xt_handle( xt, xt->root, depth );
168       
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;
173       
174        if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
175        {
176                if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ )
177                {
178                        /* This one is fun! \o/ */
179                       
180                            /* If handler.name == NULL it means it should always match. */
181                        if( ( xt->handlers[i].name == NULL || 
182                              /* If it's not, compare. There should always be a name. */
183                              g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
184                            /* If handler.parent == NULL, it's a match. */
185                            ( xt->handlers[i].parent == NULL ||
186                              /* If there's a parent node, see if the name matches. */
187                              ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
188                              /* If there's no parent, the handler should mention <root> as a parent. */
189                                               strcmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
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. */
209void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
210{
211        struct xt_node *c, *prev;
212       
213        if( !xt || !xt->root )
214                return;
215       
216        if( node == NULL )
217        {
218                xt_cleanup( xt, xt->root, depth );
219                return;
220        }
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. */
256                        if( depth != 0 )
257                                xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
258                }
259        }
260}
261
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
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
325void xt_print( struct xt_node *node )
326{
327        int i;
328        struct xt_node *c;
329       
330        /* Indentation */
331        for( c = node; c->parent; c = c->parent )
332                fprintf( stderr, "    " );
333       
334        /* Start the tag */
335        fprintf( stderr, "<%s", node->name );
336       
337        /* Print the attributes */
338        for( i = 0; node->attr[i].key; i ++ )
339        {
340                char *v = g_markup_escape_text( node->attr[i].value, -1 );
341                fprintf( stderr, " %s=\"%s\"", node->attr[i].key, v );
342                g_free( v );
343        }
344       
345        /* /> in case there's really *nothing* inside this tag, otherwise
346           just >. */
347        /* If this tag doesn't have any content at all... */
348        if( node->text == NULL && node->children == NULL )
349        {
350                fprintf( stderr, "/>\n" );
351                return;
352                /* Then we're finished! */
353        }
354       
355        /* Otherwise... */
356        fprintf( stderr, ">" );
357       
358        /* Only print the text if it contains more than whitespace (TEST). */
359        if( node->text_len > 0 )
360        {
361                for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
362                if( node->text[i] )
363                {
364                        char *v = g_markup_escape_text( node->text, -1 );
365                        fprintf( stderr, "%s", v );
366                        g_free( v );
367                }
368        }
369       
370        if( node->children )
371                fprintf( stderr, "\n" );
372       
373        for( c = node->children; c; c = c->next )
374                xt_print( c );
375       
376        if( node->children )
377                for( c = node; c->parent; c = c->parent )
378                        fprintf( stderr, "    " );
379       
380        /* Non-empty tag is now finished. */
381        fprintf( stderr, "</%s>\n", node->name );
382}
383
384struct xt_node *xt_dup( struct xt_node *node )
385{
386        struct xt_node *dup = g_new0( struct xt_node, 1 );
387        struct xt_node *c, *dc = NULL;
388        int i;
389       
390        /* Let's NOT copy the parent element here BTW! Only do it for children. */
391       
392        dup->name = g_strdup( node->name );
393        dup->flags = node->flags;
394        if( node->text )
395        {
396                dup->text = g_memdup( node->text, node->text_len + 1 );
397                dup->text_len = node->text_len;
398        }
399       
400        /* Count the number of attributes and allocate the new array. */
401        for( i = 0; node->attr[i].key; i ++ );
402        dup->attr = g_new0( struct xt_attr, i + 1 );
403       
404        /* Copy them all! */
405        for( i --; i >= 0; i -- )
406        {
407                dup->attr[i].key = g_strdup( node->attr[i].key );
408                dup->attr[i].value = g_strdup( node->attr[i].value );
409        }
410       
411        /* This nice mysterious loop takes care of the children. */
412        for( c = node->children; c; c = c->next )
413        {
414                if( dc == NULL )
415                        dc = dup->children = xt_dup( c );
416                else
417                        dc = ( dc->next = xt_dup( c ) );
418               
419                dc->parent = dup;
420        }
421       
422        return dup;
423}
424
425/* Frees a node. This doesn't clean up references to itself from parents! */
426void xt_free_node( struct xt_node *node )
427{
428        int i;
429       
430        if( !node )
431                return;
432       
433        g_free( node->name );
434        g_free( node->text );
435       
436        for( i = 0; node->attr[i].key; i ++ )
437        {
438                g_free( node->attr[i].key );
439                g_free( node->attr[i].value );
440        }
441        g_free( node->attr );
442       
443        while( node->children )
444        {
445                struct xt_node *next = node->children->next;
446               
447                xt_free_node( node->children );
448                node->children = next;
449        }
450       
451        g_free( node );
452}
453
454void xt_free( struct xt_parser *xt )
455{
456        if( !xt )
457                return;
458       
459        if( xt->root )
460                xt_free_node( xt->root );
461       
462        g_markup_parse_context_free( xt->parser );
463       
464        g_free( xt );
465}
466
467/* To find a node's child with a specific name, pass the node's children
468   list, not the node itself! The reason you have to do this by hand: So
469   that you can also use this function as a find-next. */
470struct xt_node *xt_find_node( struct xt_node *node, const char *name )
471{
472        while( node )
473        {
474                char *colon;
475               
476                if( g_strcasecmp( node->name, name ) == 0 ||
477                    ( ( colon = strchr( node->name, ':' ) ) &&
478                      g_strcasecmp( colon + 1, name ) == 0 ) )
479                        break;
480               
481                node = node->next;
482        }
483       
484        return node;
485}
486
487/* More advanced than the one above, understands something like
488   ../foo/bar to find a subnode bar of a node foo which is a child
489   of node's parent. Pass the node directly, not its list of children. */
490struct xt_node *xt_find_path( struct xt_node *node, const char *name )
491{
492        while( name && *name && node )
493        {
494                char *colon, *slash;
495                int n;
496               
497                if( ( slash = strchr( name, '/' ) ) )
498                        n = slash - name;
499                else
500                        n = strlen( name );
501               
502                if( strncmp( name, "..", n ) == 0 )
503                {
504                        node = node->parent;
505                }
506                else
507                {
508                        node = node->children;
509                       
510                        while( node )
511                        {
512                                if( g_strncasecmp( node->name, name, n ) == 0 ||
513                                    ( ( colon = strchr( node->name, ':' ) ) &&
514                                      g_strncasecmp( colon + 1, name, n ) == 0 ) )
515                                        break;
516                               
517                                node = node->next;
518                        }
519                }
520               
521                name = slash ? slash + 1 : NULL;
522        }
523       
524        return node;
525}
526
527char *xt_find_attr( struct xt_node *node, const char *key )
528{
529        int i;
530        char *colon;
531       
532        if( !node )
533                return NULL;
534       
535        for( i = 0; node->attr[i].key; i ++ )
536                if( g_strcasecmp( node->attr[i].key, key ) == 0 )
537                        break;
538       
539        /* This is an awful hack that only takes care of namespace prefixes
540           inside a tag. Since IMHO excessive namespace usage in XMPP is
541           massive overkill anyway (this code exists for almost four years
542           now and never really missed it): Meh. */
543        if( !node->attr[i].key && strcmp( key, "xmlns" ) == 0 &&
544            ( colon = strchr( node->name, ':' ) ) )
545        {
546                *colon = '\0';
547                for( i = 0; node->attr[i].key; i ++ )
548                        if( strncmp( node->attr[i].key, "xmlns:", 6 ) == 0 &&
549                            strcmp( node->attr[i].key + 6, node->name ) == 0 )
550                                break;
551                *colon = ':';
552        }
553       
554        return node->attr[i].value;
555}
556
557/* Strip a few non-printable characters that aren't allowed in XML streams
558   (and upset some XMPP servers for example). */
559void xt_strip_text( char *in )
560{
561        char *out = in;
562        static const char nonprint[32] = {
563                0, 0, 0, 0, 0, 0, 0, 0, /* 0..7 */
564                0, 1, 1, 0, 0, 1, 0, 0, /* 9 (tab), 10 (\n), 13 (\r) */
565        };
566       
567        if( !in )
568                return;
569
570        while( *in )
571        {
572                if( (unsigned int) *in >= ' ' || nonprint[(unsigned int) *in] )
573                        *out ++ = *in;
574                in ++;
575        }
576        *out = *in;
577}
578
579struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children )
580{
581        struct xt_node *node, *c;
582       
583        node = g_new0( struct xt_node, 1 );
584        node->name = g_strdup( name );
585        node->children = children;
586        node->attr = g_new0( struct xt_attr, 1 );
587       
588        if( text )
589        {
590                node->text = g_strdup( text );
591                xt_strip_text( node->text );
592                node->text_len = strlen( node->text );
593        }
594       
595        for( c = children; c; c = c->next )
596        {
597                if( c->parent != NULL )
598                {
599                        /* ERROR CONDITION: They seem to have a parent already??? */
600                }
601               
602                c->parent = node;
603        }
604       
605        return node;
606}
607
608void xt_add_child( struct xt_node *parent, struct xt_node *child )
609{
610        struct xt_node *node;
611       
612        /* This function can actually be used to add more than one child, so
613           do handle this properly. */
614        for( node = child; node; node = node->next )
615        {
616                if( node->parent != NULL )
617                {
618                        /* ERROR CONDITION: They seem to have a parent already??? */
619                }
620               
621                node->parent = parent;
622        }
623       
624        if( parent->children == NULL )
625        {
626                parent->children = child;
627        }
628        else
629        {
630                for( node = parent->children; node->next; node = node->next );
631                node->next = child;
632        }
633}
634
635/* Same, but at the beginning. */
636void xt_insert_child( struct xt_node *parent, struct xt_node *child )
637{
638        struct xt_node *node, *last = NULL;
639       
640        if( child == NULL )
641                return; /* BUG */
642       
643        for( node = child; node; node = node->next )
644        {
645                if( node->parent != NULL )
646                {
647                        /* ERROR CONDITION: They seem to have a parent already??? */
648                }
649               
650                node->parent = parent;
651                last = node;
652        }
653       
654        last->next = parent->children;
655        parent->children = child;
656}
657
658void xt_add_attr( struct xt_node *node, const char *key, const char *value )
659{
660        int i;
661       
662        /* Now actually it'd be nice if we can also change existing attributes
663           (which actually means this function doesn't have the right name).
664           So let's find out if we have this attribute already... */
665        for( i = 0; node->attr[i].key; i ++ )
666                if( strcmp( node->attr[i].key, key ) == 0 )
667                        break;
668       
669        if( node->attr[i].key == NULL )
670        {
671                /* If not, allocate space for a new attribute. */
672                node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
673                node->attr[i].key = g_strdup( key );
674                node->attr[i+1].key = NULL;
675        }
676        else
677        {
678                /* Otherwise, free the old value before setting the new one. */
679                g_free( node->attr[i].value );
680        }
681       
682        node->attr[i].value = g_strdup( value );
683}
684
685int xt_remove_attr( struct xt_node *node, const char *key )
686{
687        int i, last;
688       
689        for( i = 0; node->attr[i].key; i ++ )
690                if( strcmp( node->attr[i].key, key ) == 0 )
691                        break;
692       
693        /* If we didn't find the attribute... */
694        if( node->attr[i].key == NULL )
695                return 0;
696       
697        g_free( node->attr[i].key );
698        g_free( node->attr[i].value );
699       
700        /* If it's the last, this is easy: */
701        if( node->attr[i+1].key == NULL )
702        {
703                node->attr[i].key = node->attr[i].value = NULL;
704        }
705        else /* It's also pretty easy, actually. */
706        {
707                /* Find the last item. */
708                for( last = i + 1; node->attr[last+1].key; last ++ );
709               
710                node->attr[i] = node->attr[last];
711                node->attr[last].key = NULL;
712                node->attr[last].value = NULL;
713        }
714       
715        /* Let's not bother with reallocating memory here. It takes time and
716           most packets don't stay in memory for long anyway. */
717       
718        return 1;
719}
Note: See TracBrowser for help on using the repository browser.