source: lib/xmltree.c @ b46769d

Last change on this file since b46769d was b46769d, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-12T22:44:56Z

Some syntax checking fixups; don't make the same mistake of failing just if
the MSN server is sending a little bit *more* info.
And adding xt_insert_node() used in the ADL generation code.

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