source: protocols/jabber/xmltree.c @ 101d84f

Last change on this file since 101d84f was 101d84f, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-10-05T22:55:54Z

Added max. recursion depth arguments to xt_handle()/_cleanup() to make sure
commands that still have to be handled don't get (partially) cleaned up
already.

  • Property mode set to 100644
File size: 13.2 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( gpointer data )
114{
115        struct xt_parser *xt = g_new0( struct xt_parser, 1 );
116       
117        xt->data = data;
118        xt_reset( xt );
119       
120        return xt;
121}
122
123/* Reset the parser, flush everything we have so far. For example, we need
124   this for XMPP when doing TLS/SASL to restart the stream. */
125void xt_reset( struct xt_parser *xt )
126{
127        if( xt->parser )
128                g_markup_parse_context_free( xt->parser );
129       
130        xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL );
131       
132        if( xt->root )
133        {
134                xt_free_node( xt->root );
135                xt->root = NULL;
136                xt->cur = NULL;
137        }
138}
139
140/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
141   end-of-stream and 1 otherwise. */
142int xt_feed( struct xt_parser *xt, char *text, int text_len )
143{
144        if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
145        {
146                return -1;
147        }
148       
149        return !( xt->root && xt->root->flags & XT_COMPLETE );
150}
151
152/* Find completed nodes and see if a handler has to be called. Passing
153   a node isn't necessary if you want to start at the root, just pass
154   NULL. This second argument is needed for recursive calls. */
155int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
156{
157        struct xt_node *c;
158        xt_status st;
159        int i;
160       
161        /* Just in case someone likes infinite loops... */
162        if( xt->root == NULL )
163                return 0;
164       
165        if( node == NULL )
166                return xt_handle( xt, xt->root, depth );
167       
168        if( depth != 0 )
169                for( c = node->children; c; c = c->next )
170                        if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) )
171                                return 0;
172       
173        if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
174        {
175                for( i = 0; xt->handlers[i].func; i ++ )
176                {
177                        /* This one is fun! \o/ */
178                       
179                                                /* If handler.name == NULL it means it should always match. */
180                        if( ( xt->handlers[i].name == NULL || 
181                                                /* If it's not, compare. There should always be a name. */
182                              g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
183                                                /* If handler.parent == NULL, it's a match. */
184                            ( xt->handlers[i].parent == NULL ||
185                                                /* If there's a parent node, see if the name matches. */
186                              ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
187                                                /* If there's no parent, the handler should mention <root> as a parent. */
188                                               g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
189                        {
190                                st = xt->handlers[i].func( node, xt->data );
191                               
192                                if( st == XT_ABORT )
193                                        return 0;
194                                else if( st != XT_NEXT )
195                                        break;
196                        }
197                }
198               
199                node->flags |= XT_SEEN;
200        }
201       
202        return 1;
203}
204
205/* Garbage collection: Cleans up all nodes that are handled. Useful for
206   streams because there's no reason to keep a complete packet history
207   in memory. */
208void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
209{
210        struct xt_node *c, *prev;
211       
212        if( !xt || !xt->root )
213                return;
214       
215        if( node == NULL )
216                return xt_cleanup( xt, xt->root, depth );
217       
218        if( node->flags & XT_SEEN && node == xt->root )
219        {
220                xt_free_node( xt->root );
221                xt->root = xt->cur = NULL;
222                /* xt->cur should be NULL already, BTW... */
223               
224                return;
225        }
226       
227        /* c contains the current node, prev the previous node (or NULL).
228           I admit, this one's pretty horrible. */
229        for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
230        {
231                if( c->flags & XT_SEEN )
232                {
233                        /* Remove the node from the linked list. */
234                        if( prev )
235                                prev->next = c->next;
236                        else
237                                node->children = c->next;
238                       
239                        xt_free_node( c );
240                       
241                        /* Since the for loop wants to get c->next, make sure
242                           c points at something that exists (and that c->next
243                           will actually be the next item we should check). c
244                           can be NULL now, if we just removed the first item.
245                           That explains the ? thing in for(). */
246                        c = prev;
247                }
248                else
249                {
250                        /* This node can't be cleaned up yet, but maybe a
251                           subnode can. */
252                        if( depth != 0 )
253                                xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
254                }
255        }
256}
257
258static void xt_to_string_real( struct xt_node *node, GString *str )
259{
260        char *buf;
261        struct xt_node *c;
262        int i;
263       
264        g_string_append_printf( str, "<%s", node->name );
265       
266        for( i = 0; node->attr[i].key; i ++ )
267        {
268                buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
269                g_string_append( str, buf );
270                g_free( buf );
271        }
272       
273        if( node->text == NULL && node->children == NULL )
274        {
275                g_string_append( str, "/>" );
276                return;
277        }
278       
279        g_string_append( str, ">" );
280        if( node->text_len > 0 )
281        {
282                buf = g_markup_escape_text( node->text, node->text_len );
283                g_string_append( str, buf );
284                g_free( buf );
285        }
286       
287        for( c = node->children; c; c = c->next )
288                xt_to_string_real( c, str );
289       
290        g_string_append_printf( str, "</%s>", node->name );
291}
292
293char *xt_to_string( struct xt_node *node )
294{
295        GString *ret;
296        char *real;
297       
298        ret = g_string_new( "" );
299        xt_to_string_real( node, ret );
300       
301        real = ret->str;
302        g_string_free( ret, FALSE );
303       
304        return real;
305}
306
307void xt_print( struct xt_node *node )
308{
309        int i;
310        struct xt_node *c;
311       
312        /* Indentation */
313        for( c = node; c->parent; c = c->parent )
314                printf( "\t" );
315       
316        /* Start the tag */
317        printf( "<%s", node->name );
318       
319        /* Print the attributes */
320        for( i = 0; node->attr[i].key; i ++ )
321                printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) );
322       
323        /* /> in case there's really *nothing* inside this tag, otherwise
324           just >. */
325        /* If this tag doesn't have any content at all... */
326        if( node->text == NULL && node->children == NULL )
327        {
328                printf( "/>\n" );
329                return;
330                /* Then we're finished! */
331        }
332       
333        /* Otherwise... */
334        printf( ">" );
335       
336        /* Only print the text if it contains more than whitespace (TEST). */
337        if( node->text_len > 0 )
338        {
339                for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
340                if( node->text[i] )
341                        printf( "%s", g_markup_escape_text( node->text, -1 ) );
342        }
343       
344        if( node->children )
345                printf( "\n" );
346       
347        for( c = node->children; c; c = c->next )
348                xt_print( c );
349       
350        if( node->children )
351                for( c = node; c->parent; c = c->parent )
352                        printf( "\t" );
353       
354        /* Non-empty tag is now finished. */
355        printf( "</%s>\n", node->name );
356}
357
358struct xt_node *xt_dup( struct xt_node *node )
359{
360        struct xt_node *dup = g_new0( struct xt_node, 1 );
361        struct xt_node *c, *dc = NULL;
362        int i;
363       
364        /* Let's NOT copy the parent element here BTW! Only do it for children. */
365       
366        dup->name = g_strdup( node->name );
367        dup->flags = node->flags;
368        if( node->text )
369        {
370                dup->text = g_memdup( node->text, node->text_len + 1 );
371                dup->text_len = node->text_len;
372        }
373       
374        /* Count the number of attributes and allocate the new array. */
375        for( i = 0; node->attr[i].key; i ++ );
376        dup->attr = g_new0( struct xt_attr, i + 1 );
377       
378        /* Copy them all! */
379        for( i --; i >= 0; i -- )
380        {
381                dup->attr[i].key = g_strdup( node->attr[i].key );
382                dup->attr[i].value = g_strdup( node->attr[i].value );
383        }
384       
385        /* This nice mysterious loop takes care of the children. */
386        for( c = node->children; c; c = c->next )
387        {
388                if( dc == NULL )
389                        dc = dup->children = xt_dup( c );
390                else
391                        dc = ( dc->next = xt_dup( c ) );
392               
393                dc->parent = dup;
394        }
395       
396        return dup;
397}
398
399/* Frees a node. This doesn't clean up references to itself from parents! */
400void xt_free_node( struct xt_node *node )
401{
402        int i;
403       
404        if( !node )
405                return;
406       
407        g_free( node->name );
408        g_free( node->text );
409       
410        for( i = 0; node->attr[i].key; i ++ )
411        {
412                g_free( node->attr[i].key );
413                g_free( node->attr[i].value );
414        }
415        g_free( node->attr );
416       
417        while( node->children )
418        {
419                struct xt_node *next = node->children->next;
420               
421                xt_free_node( node->children );
422                node->children = next;
423        }
424       
425        g_free( node );
426}
427
428void xt_free( struct xt_parser *xt )
429{
430        if( !xt )
431                return;
432       
433        if( xt->root )
434                xt_free_node( xt->root );
435       
436        g_markup_parse_context_free( xt->parser );
437       
438        g_free( xt );
439}
440
441/* To find a node's child with a specific name, pass the node's children
442   list, not the node itself! The reason you have to do this by hand: So
443   that you can also use this function as a find-next. */
444struct xt_node *xt_find_node( struct xt_node *node, char *name )
445{
446        while( node )
447        {
448                if( g_strcasecmp( node->name, name ) == 0 )
449                        break;
450               
451                node = node->next;
452        }
453       
454        return node;
455}
456
457char *xt_find_attr( struct xt_node *node, char *key )
458{
459        int i;
460       
461        if( !node )
462                return NULL;
463       
464        for( i = 0; node->attr[i].key; i ++ )
465                if( g_strcasecmp( node->attr[i].key, key ) == 0 )
466                        break;
467       
468        return node->attr[i].value;
469}
470
471struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children )
472{
473        struct xt_node *node, *c;
474       
475        node = g_new0( struct xt_node, 1 );
476        node->name = g_strdup( name );
477        node->children = children;
478        node->attr = g_new0( struct xt_attr, 1 );
479       
480        if( text )
481        {
482                node->text_len = strlen( text );
483                node->text = g_memdup( text, node->text_len + 1 );
484        }
485       
486        for( c = children; c; c = c->next )
487        {
488                if( c->parent != NULL )
489                {
490                        /* ERROR CONDITION: They seem to have a parent already??? */
491                }
492               
493                c->parent = node;
494        }
495       
496        return node;
497}
498
499void xt_add_child( struct xt_node *parent, struct xt_node *child )
500{
501        struct xt_node *node;
502       
503        /* This function can actually be used to add more than one child, so
504           do handle this properly. */
505        for( node = child; node; node = node->next )
506        {
507                if( node->parent != NULL )
508                {
509                        /* ERROR CONDITION: They seem to have a parent already??? */
510                }
511               
512                node->parent = parent;
513        }
514       
515        if( parent->children == NULL )
516        {
517                parent->children = child;
518        }
519        else
520        {
521                for( node = parent->children; node->next; node = node->next );
522                node->next = child;
523        }
524}
525
526void xt_add_attr( struct xt_node *node, char *key, char *value )
527{
528        int i;
529       
530        for( i = 0; node->attr[i].key; i ++ );
531        node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
532        node->attr[i].key = g_strdup( key );
533        node->attr[i].value = g_strdup( value );
534        node->attr[i+1].key = NULL;
535}
Note: See TracBrowser for help on using the repository browser.