source: lib/xmltree.c @ bfafb99

Last change on this file since bfafb99 was 222b440, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-06-05T20:19:56Z

Add xt_to_string_i() and use it to get indentation back in saved settings.
Also, use it in xt_print() instead of replicating most of xt_to_string()
in it. This changed four-space indents into tabs but oh well, we'll live.

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