source: protocols/jabber/xmltree.c @ 5d7dc00

Last change on this file since 5d7dc00 was 40ef702, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-07-01T13:08:47Z

Less copy-pasting in the service discovery reply and added MUC support to
that list. And adding some const stuff in the xmltree functions.

  • Property mode set to 100644
File size: 14.5 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, const 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, const 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, const char *key, const char *value )
527{
528        int i;
529       
530        /* Now actually it'd be nice if we can also change existing attributes
531           (which actually means this function doesn't have the right name).
532           So let's find out if we have this attribute already... */
533        for( i = 0; node->attr[i].key; i ++ )
534                if( strcmp( node->attr[i].key, key ) == 0 )
535                        break;
536       
537        if( node->attr[i].key == NULL )
538        {
539                /* If not, allocate space for a new attribute. */
540                node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
541                node->attr[i].key = g_strdup( key );
542                node->attr[i+1].key = NULL;
543        }
544        else
545        {
546                /* Otherwise, free the old value before setting the new one. */
547                g_free( node->attr[i].value );
548        }
549       
550        node->attr[i].value = g_strdup( value );
551}
552
553int xt_remove_attr( struct xt_node *node, const char *key )
554{
555        int i, last;
556       
557        for( i = 0; node->attr[i].key; i ++ )
558                if( strcmp( node->attr[i].key, key ) == 0 )
559                        break;
560       
561        /* If we didn't find the attribute... */
562        if( node->attr[i].key == NULL )
563                return 0;
564       
565        g_free( node->attr[i].key );
566        g_free( node->attr[i].value );
567       
568        /* If it's the last, this is easy: */
569        if( node->attr[i+1].key == NULL )
570        {
571                node->attr[i].key = node->attr[i].value = NULL;
572        }
573        else /* It's also pretty easy, actually. */
574        {
575                /* Find the last item. */
576                for( last = i + 1; node->attr[last+1].key; last ++ );
577               
578                node->attr[i] = node->attr[last];
579                node->attr[last].key = NULL;
580                node->attr[last].value = NULL;
581        }
582       
583        /* Let's not bother with reallocating memory here. It takes time and
584           most packets don't stay in memory for long anyway. */
585       
586        return 1;
587}
Note: See TracBrowser for help on using the repository browser.