source: protocols/jabber/xmltree.c @ 0e7ab64

Last change on this file since 0e7ab64 was 43671b9, checked in by Wilmer van der Gaast <wilmer@…>, at 2007-04-22T23:39:37Z

You can send messages too now. But it's still very kludgy and doesn't work
with anonymous rooms (ie about 95% of all available Jabber chatrooms?).

  • 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//                              xt_print( node );
191                               
192                                st = xt->handlers[i].func( node, xt->data );
193                               
194                                if( st == XT_ABORT )
195                                        return 0;
196                                else if( st != XT_NEXT )
197                                        break;
198                        }
199                }
200               
201                node->flags |= XT_SEEN;
202        }
203       
204        return 1;
205}
206
207/* Garbage collection: Cleans up all nodes that are handled. Useful for
208   streams because there's no reason to keep a complete packet history
209   in memory. */
210void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
211{
212        struct xt_node *c, *prev;
213       
214        if( !xt || !xt->root )
215                return;
216       
217        if( node == NULL )
218                return xt_cleanup( xt, xt->root, depth );
219       
220        if( node->flags & XT_SEEN && node == xt->root )
221        {
222                xt_free_node( xt->root );
223                xt->root = xt->cur = NULL;
224                /* xt->cur should be NULL already, BTW... */
225               
226                return;
227        }
228       
229        /* c contains the current node, prev the previous node (or NULL).
230           I admit, this one's pretty horrible. */
231        for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
232        {
233                if( c->flags & XT_SEEN )
234                {
235                        /* Remove the node from the linked list. */
236                        if( prev )
237                                prev->next = c->next;
238                        else
239                                node->children = c->next;
240                       
241                        xt_free_node( c );
242                       
243                        /* Since the for loop wants to get c->next, make sure
244                           c points at something that exists (and that c->next
245                           will actually be the next item we should check). c
246                           can be NULL now, if we just removed the first item.
247                           That explains the ? thing in for(). */
248                        c = prev;
249                }
250                else
251                {
252                        /* This node can't be cleaned up yet, but maybe a
253                           subnode can. */
254                        if( depth != 0 )
255                                xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
256                }
257        }
258}
259
260static void xt_to_string_real( struct xt_node *node, GString *str )
261{
262        char *buf;
263        struct xt_node *c;
264        int i;
265       
266        g_string_append_printf( str, "<%s", node->name );
267       
268        for( i = 0; node->attr[i].key; i ++ )
269        {
270                buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
271                g_string_append( str, buf );
272                g_free( buf );
273        }
274       
275        if( node->text == NULL && node->children == NULL )
276        {
277                g_string_append( str, "/>" );
278                return;
279        }
280       
281        g_string_append( str, ">" );
282        if( node->text_len > 0 )
283        {
284                buf = g_markup_escape_text( node->text, node->text_len );
285                g_string_append( str, buf );
286                g_free( buf );
287        }
288       
289        for( c = node->children; c; c = c->next )
290                xt_to_string_real( c, str );
291       
292        g_string_append_printf( str, "</%s>", node->name );
293}
294
295char *xt_to_string( struct xt_node *node )
296{
297        GString *ret;
298        char *real;
299       
300        ret = g_string_new( "" );
301        xt_to_string_real( node, ret );
302       
303        real = ret->str;
304        g_string_free( ret, FALSE );
305       
306        return real;
307}
308
309void xt_print( struct xt_node *node )
310{
311        int i;
312        struct xt_node *c;
313       
314        /* Indentation */
315        for( c = node; c->parent; c = c->parent )
316                printf( "\t" );
317       
318        /* Start the tag */
319        printf( "<%s", node->name );
320       
321        /* Print the attributes */
322        for( i = 0; node->attr[i].key; i ++ )
323                printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) );
324       
325        /* /> in case there's really *nothing* inside this tag, otherwise
326           just >. */
327        /* If this tag doesn't have any content at all... */
328        if( node->text == NULL && node->children == NULL )
329        {
330                printf( "/>\n" );
331                return;
332                /* Then we're finished! */
333        }
334       
335        /* Otherwise... */
336        printf( ">" );
337       
338        /* Only print the text if it contains more than whitespace (TEST). */
339        if( node->text_len > 0 )
340        {
341                for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
342                if( node->text[i] )
343                        printf( "%s", g_markup_escape_text( node->text, -1 ) );
344        }
345       
346        if( node->children )
347                printf( "\n" );
348       
349        for( c = node->children; c; c = c->next )
350                xt_print( c );
351       
352        if( node->children )
353                for( c = node; c->parent; c = c->parent )
354                        printf( "\t" );
355       
356        /* Non-empty tag is now finished. */
357        printf( "</%s>\n", node->name );
358}
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, 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, 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, char *key, 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, 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.