source: lib/xmltree.c @ 820a2a7

Last change on this file since 820a2a7 was e31e5b8, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-04-20T13:05:55Z

Merging "storage" branch which I wrote long ago. It separates generation of
XML-formatted user configs from disk I/O so we can try to start using other
mechanisms to store them (a REST API or something, for example).

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