source: lib/xmltree.c @ 4c9d377

Last change on this file since 4c9d377 was 6f55bec, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-09-23T23:25:32Z

xt_from_string() will return NULL if the string wasn't terminated properly.
Adding an extra layer of defense against truncated responses from Twitter.
But as long as the response from xt_from_string() isn't NULL-checked this
won't help much. So that's my next step. :-)

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