source: protocols/jabber/xmltree.c @ 2ff2076

Last change on this file since 2ff2076 was ebb95b6, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-11-14T23:42:07Z

Merging from devel/Jelmer.

  • Property mode set to 100644
File size: 14.6 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
5*                                                                           *
6*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
7*                                                                           *
8*  This library is free software; you can redistribute it and/or            *
9*  modify it under the terms of the GNU Lesser General Public               *
10*  License as published by the Free Software Foundation, version            *
11*  2.1.                                                                     *
12*                                                                           *
13*  This library is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
16*  Lesser General Public License for more details.                          *
17*                                                                           *
18*  You should have received a copy of the GNU Lesser General Public License *
19*  along with this library; if not, write to the Free Software Foundation,  *
20*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
21*                                                                           *
22****************************************************************************/
23
24#include <glib.h>
25#include <string.h>
26#include <unistd.h>
27#include <ctype.h>
28#include <stdio.h>
29
30#include "xmltree.h"
31
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
307#ifdef DEBUG
308void xt_print( struct xt_node *node )
309{
310        int i;
311        struct xt_node *c;
312       
313        /* Indentation */
314        for( c = node; c->parent; c = c->parent )
315                printf( "\t" );
316       
317        /* Start the tag */
318        printf( "<%s", node->name );
319       
320        /* Print the attributes */
321        for( i = 0; node->attr[i].key; i ++ )
322                printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) );
323       
324        /* /> in case there's really *nothing* inside this tag, otherwise
325           just >. */
326        /* If this tag doesn't have any content at all... */
327        if( node->text == NULL && node->children == NULL )
328        {
329                printf( "/>\n" );
330                return;
331                /* Then we're finished! */
332        }
333       
334        /* Otherwise... */
335        printf( ">" );
336       
337        /* Only print the text if it contains more than whitespace (TEST). */
338        if( node->text_len > 0 )
339        {
340                for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
341                if( node->text[i] )
342                        printf( "%s", g_markup_escape_text( node->text, -1 ) );
343        }
344       
345        if( node->children )
346                printf( "\n" );
347       
348        for( c = node->children; c; c = c->next )
349                xt_print( c );
350       
351        if( node->children )
352                for( c = node; c->parent; c = c->parent )
353                        printf( "\t" );
354       
355        /* Non-empty tag is now finished. */
356        printf( "</%s>\n", node->name );
357}
358#endif
359
360struct xt_node *xt_dup( struct xt_node *node )
361{
362        struct xt_node *dup = g_new0( struct xt_node, 1 );
363        struct xt_node *c, *dc = NULL;
364        int i;
365       
366        /* Let's NOT copy the parent element here BTW! Only do it for children. */
367       
368        dup->name = g_strdup( node->name );
369        dup->flags = node->flags;
370        if( node->text )
371        {
372                dup->text = g_memdup( node->text, node->text_len + 1 );
373                dup->text_len = node->text_len;
374        }
375       
376        /* Count the number of attributes and allocate the new array. */
377        for( i = 0; node->attr[i].key; i ++ );
378        dup->attr = g_new0( struct xt_attr, i + 1 );
379       
380        /* Copy them all! */
381        for( i --; i >= 0; i -- )
382        {
383                dup->attr[i].key = g_strdup( node->attr[i].key );
384                dup->attr[i].value = g_strdup( node->attr[i].value );
385        }
386       
387        /* This nice mysterious loop takes care of the children. */
388        for( c = node->children; c; c = c->next )
389        {
390                if( dc == NULL )
391                        dc = dup->children = xt_dup( c );
392                else
393                        dc = ( dc->next = xt_dup( c ) );
394               
395                dc->parent = dup;
396        }
397       
398        return dup;
399}
400
401/* Frees a node. This doesn't clean up references to itself from parents! */
402void xt_free_node( struct xt_node *node )
403{
404        int i;
405       
406        if( !node )
407                return;
408       
409        g_free( node->name );
410        g_free( node->text );
411       
412        for( i = 0; node->attr[i].key; i ++ )
413        {
414                g_free( node->attr[i].key );
415                g_free( node->attr[i].value );
416        }
417        g_free( node->attr );
418       
419        while( node->children )
420        {
421                struct xt_node *next = node->children->next;
422               
423                xt_free_node( node->children );
424                node->children = next;
425        }
426       
427        g_free( node );
428}
429
430void xt_free( struct xt_parser *xt )
431{
432        if( !xt )
433                return;
434       
435        if( xt->root )
436                xt_free_node( xt->root );
437       
438        g_markup_parse_context_free( xt->parser );
439       
440        g_free( xt );
441}
442
443/* To find a node's child with a specific name, pass the node's children
444   list, not the node itself! The reason you have to do this by hand: So
445   that you can also use this function as a find-next. */
446struct xt_node *xt_find_node( struct xt_node *node, const char *name )
447{
448        while( node )
449        {
450                if( g_strcasecmp( node->name, name ) == 0 )
451                        break;
452               
453                node = node->next;
454        }
455       
456        return node;
457}
458
459char *xt_find_attr( struct xt_node *node, const char *key )
460{
461        int i;
462       
463        if( !node )
464                return NULL;
465       
466        for( i = 0; node->attr[i].key; i ++ )
467                if( g_strcasecmp( node->attr[i].key, key ) == 0 )
468                        break;
469       
470        return node->attr[i].value;
471}
472
473struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children )
474{
475        struct xt_node *node, *c;
476       
477        node = g_new0( struct xt_node, 1 );
478        node->name = g_strdup( name );
479        node->children = children;
480        node->attr = g_new0( struct xt_attr, 1 );
481       
482        if( text )
483        {
484                node->text_len = strlen( text );
485                node->text = g_memdup( text, node->text_len + 1 );
486        }
487       
488        for( c = children; c; c = c->next )
489        {
490                if( c->parent != NULL )
491                {
492                        /* ERROR CONDITION: They seem to have a parent already??? */
493                }
494               
495                c->parent = node;
496        }
497       
498        return node;
499}
500
501void xt_add_child( struct xt_node *parent, struct xt_node *child )
502{
503        struct xt_node *node;
504       
505        /* This function can actually be used to add more than one child, so
506           do handle this properly. */
507        for( node = child; node; node = node->next )
508        {
509                if( node->parent != NULL )
510                {
511                        /* ERROR CONDITION: They seem to have a parent already??? */
512                }
513               
514                node->parent = parent;
515        }
516       
517        if( parent->children == NULL )
518        {
519                parent->children = child;
520        }
521        else
522        {
523                for( node = parent->children; node->next; node = node->next );
524                node->next = child;
525        }
526}
527
528void xt_add_attr( struct xt_node *node, const char *key, const char *value )
529{
530        int i;
531       
532        /* Now actually it'd be nice if we can also change existing attributes
533           (which actually means this function doesn't have the right name).
534           So let's find out if we have this attribute already... */
535        for( i = 0; node->attr[i].key; i ++ )
536                if( strcmp( node->attr[i].key, key ) == 0 )
537                        break;
538       
539        if( node->attr[i].key == NULL )
540        {
541                /* If not, allocate space for a new attribute. */
542                node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
543                node->attr[i].key = g_strdup( key );
544                node->attr[i+1].key = NULL;
545        }
546        else
547        {
548                /* Otherwise, free the old value before setting the new one. */
549                g_free( node->attr[i].value );
550        }
551       
552        node->attr[i].value = g_strdup( value );
553}
554
555int xt_remove_attr( struct xt_node *node, const char *key )
556{
557        int i, last;
558       
559        for( i = 0; node->attr[i].key; i ++ )
560                if( strcmp( node->attr[i].key, key ) == 0 )
561                        break;
562       
563        /* If we didn't find the attribute... */
564        if( node->attr[i].key == NULL )
565                return 0;
566       
567        g_free( node->attr[i].key );
568        g_free( node->attr[i].value );
569       
570        /* If it's the last, this is easy: */
571        if( node->attr[i+1].key == NULL )
572        {
573                node->attr[i].key = node->attr[i].value = NULL;
574        }
575        else /* It's also pretty easy, actually. */
576        {
577                /* Find the last item. */
578                for( last = i + 1; node->attr[last+1].key; last ++ );
579               
580                node->attr[i] = node->attr[last];
581                node->attr[last].key = NULL;
582                node->attr[last].value = NULL;
583        }
584       
585        /* Let's not bother with reallocating memory here. It takes time and
586           most packets don't stay in memory for long anyway. */
587       
588        return 1;
589}
Note: See TracBrowser for help on using the repository browser.