source: lib/xmltree.c @ 7b40f17

Last change on this file since 7b40f17 was 7b40f17, checked in by dequis <dx@…>, at 2014-10-11T02:20:53Z

Add support for XEP-0203: Delayed delivery (message timestamps)

Very similar to XEP-0091 which is already supported, but was marked as
obsolete, replaced by XEP-0203. The main differences are the tag name
and the timestamp format.

Due to the similarities, both XEPs are still supported.

  • Property mode set to 100644
File size: 17.1 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-2012 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
32#define g_strcasecmp g_ascii_strcasecmp
33#define g_strncasecmp g_ascii_strncasecmp
34
35static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
36{
37        struct xt_parser *xt = data;
38        struct xt_node *node = g_new0( struct xt_node, 1 ), *nt;
39        int i;
40       
41        node->parent = xt->cur;
42        node->name = g_strdup( element_name );
43       
44        /* First count the number of attributes */
45        for( i = 0; attr_names[i]; i ++ );
46       
47        /* Then allocate a NULL-terminated array. */
48        node->attr = g_new0( struct xt_attr, i + 1 );
49       
50        /* And fill it, saving one variable by starting at the end. */
51        for( i --; i >= 0; i -- )
52        {
53                node->attr[i].key = g_strdup( attr_names[i] );
54                node->attr[i].value = g_strdup( attr_values[i] );
55        }
56       
57        /* Add it to the linked list of children nodes, if we have a current
58           node yet. */
59        if( xt->cur )
60        {
61                if( xt->cur->children )
62                {
63                        for( nt = xt->cur->children; nt->next; nt = nt->next );
64                        nt->next = node;
65                }
66                else
67                {
68                        xt->cur->children = node;
69                }
70        }
71        else if( xt->root )
72        {
73                /* ERROR situation: A second root-element??? */
74        }
75       
76        /* Now this node will be the new current node. */
77        xt->cur = node;
78        /* And maybe this is the root? */
79        if( xt->root == NULL )
80                xt->root = node;
81}
82
83static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
84{
85        struct xt_parser *xt = data;
86        struct xt_node *node = xt->cur;
87       
88        if( node == NULL )
89                return;
90       
91        /* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */
92        node->text = g_renew( char, node->text, node->text_len + text_len + 1 );
93        memcpy( node->text + node->text_len, text, text_len );
94        node->text_len += text_len;
95        /* Zero termination is always nice to have. */
96        node->text[node->text_len] = 0;
97}
98
99static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
100{
101        struct xt_parser *xt = data;
102       
103        xt->cur->flags |= XT_COMPLETE;
104        xt->cur = xt->cur->parent;
105}
106
107GMarkupParser xt_parser_funcs =
108{
109        xt_start_element,
110        xt_end_element,
111        xt_text,
112        NULL,
113        NULL
114};
115
116struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data )
117{
118        struct xt_parser *xt = g_new0( struct xt_parser, 1 );
119       
120        xt->data = data;
121        xt->handlers = handlers;
122        xt_reset( xt );
123       
124        return xt;
125}
126
127/* Reset the parser, flush everything we have so far. For example, we need
128   this for XMPP when doing TLS/SASL to restart the stream. */
129void xt_reset( struct xt_parser *xt )
130{
131        if( xt->parser )
132                g_markup_parse_context_free( xt->parser );
133       
134        xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL );
135       
136        if( xt->root )
137        {
138                xt_free_node( xt->root );
139                xt->root = NULL;
140                xt->cur = NULL;
141        }
142}
143
144/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
145   end-of-stream and 1 otherwise. */
146int xt_feed( struct xt_parser *xt, const char *text, int text_len )
147{
148        if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
149        {
150                return -1;
151        }
152       
153        return !( xt->root && xt->root->flags & XT_COMPLETE );
154}
155
156/* Find completed nodes and see if a handler has to be called. Passing
157   a node isn't necessary if you want to start at the root, just pass
158   NULL. This second argument is needed for recursive calls. */
159int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
160{
161        struct xt_node *c;
162        xt_status st;
163        int i;
164       
165        if( xt->root == NULL )
166                return 1;
167       
168        if( node == NULL )
169                return xt_handle( xt, xt->root, depth );
170       
171        if( depth != 0 )
172                for( c = node->children; c; c = c->next )
173                        if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) )
174                                return 0;
175       
176        if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
177        {
178                if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ )
179                {
180                        /* This one is fun! \o/ */
181                       
182                            /* If handler.name == NULL it means it should always match. */
183                        if( ( xt->handlers[i].name == NULL || 
184                              /* If it's not, compare. There should always be a name. */
185                              g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
186                            /* If handler.parent == NULL, it's a match. */
187                            ( xt->handlers[i].parent == NULL ||
188                              /* If there's a parent node, see if the name matches. */
189                              ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 : 
190                              /* If there's no parent, the handler should mention <root> as a parent. */
191                                               strcmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
192                        {
193                                st = xt->handlers[i].func( node, xt->data );
194                               
195                                if( st == XT_ABORT )
196                                        return 0;
197                                else if( st != XT_NEXT )
198                                        break;
199                        }
200                }
201               
202                node->flags |= XT_SEEN;
203        }
204       
205        return 1;
206}
207
208/* Garbage collection: Cleans up all nodes that are handled. Useful for
209   streams because there's no reason to keep a complete packet history
210   in memory. */
211void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
212{
213        struct xt_node *c, *prev;
214       
215        if( !xt || !xt->root )
216                return;
217       
218        if( node == NULL )
219        {
220                xt_cleanup( xt, xt->root, depth );
221                return;
222        }
223       
224        if( node->flags & XT_SEEN && node == xt->root )
225        {
226                xt_free_node( xt->root );
227                xt->root = xt->cur = NULL;
228                /* xt->cur should be NULL already, BTW... */
229               
230                return;
231        }
232       
233        /* c contains the current node, prev the previous node (or NULL).
234           I admit, this one's pretty horrible. */
235        for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
236        {
237                if( c->flags & XT_SEEN )
238                {
239                        /* Remove the node from the linked list. */
240                        if( prev )
241                                prev->next = c->next;
242                        else
243                                node->children = c->next;
244                       
245                        xt_free_node( c );
246                       
247                        /* Since the for loop wants to get c->next, make sure
248                           c points at something that exists (and that c->next
249                           will actually be the next item we should check). c
250                           can be NULL now, if we just removed the first item.
251                           That explains the ? thing in for(). */
252                        c = prev;
253                }
254                else
255                {
256                        /* This node can't be cleaned up yet, but maybe a
257                           subnode can. */
258                        if( depth != 0 )
259                                xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
260                }
261        }
262}
263
264struct xt_node *xt_from_string( const char *in, int len )
265{
266        struct xt_parser *parser;
267        struct xt_node *ret = NULL;
268       
269        if( len == 0 )
270                len = strlen( in );
271       
272        parser = xt_new( NULL, NULL );
273        xt_feed( parser, in, len );
274        if( parser->cur == NULL )
275        {
276                ret = parser->root;
277                parser->root = NULL;
278        }
279        xt_free( parser );
280       
281        return ret;
282}
283
284static void xt_to_string_real( struct xt_node *node, GString *str, int indent )
285{
286        char *buf;
287        struct xt_node *c;
288        int i;
289       
290        if( indent > 1 )
291                g_string_append_len( str, "\n\t\t\t\t\t\t\t\t",
292                                     indent < 8 ? indent : 8 );
293       
294        g_string_append_printf( str, "<%s", node->name );
295       
296        for( i = 0; node->attr[i].key; i ++ )
297        {
298                buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
299                g_string_append( str, buf );
300                g_free( buf );
301        }
302       
303        if( node->text == NULL && node->children == NULL )
304        {
305                g_string_append( str, "/>" );
306                return;
307        }
308       
309        g_string_append( str, ">" );
310        if( node->text_len > 0 )
311        {
312                buf = g_markup_escape_text( node->text, node->text_len );
313                g_string_append( str, buf );
314                g_free( buf );
315        }
316       
317        for( c = node->children; c; c = c->next )
318                xt_to_string_real( c, str, indent ? indent + 1 : 0 );
319       
320        if( indent > 0 && node->children )
321                g_string_append_len( str, "\n\t\t\t\t\t\t\t\t",
322                                     indent < 8 ? indent : 8 );
323       
324        g_string_append_printf( str, "</%s>", node->name );
325}
326
327char *xt_to_string( struct xt_node *node )
328{
329        GString *ret;
330       
331        ret = g_string_new( "" );
332        xt_to_string_real( node, ret, 0 );
333        return g_string_free( ret, FALSE );
334}
335
336/* WITH indentation! */
337char *xt_to_string_i( struct xt_node *node )
338{
339        GString *ret;
340       
341        ret = g_string_new( "" );
342        xt_to_string_real( node, ret, 1 );
343        return g_string_free( ret, FALSE );
344}
345
346void xt_print( struct xt_node *node )
347{
348        char *str = xt_to_string_i( node );
349        fprintf( stderr, "%s", str );
350        g_free( str );
351}
352
353struct xt_node *xt_dup( struct xt_node *node )
354{
355        struct xt_node *dup = g_new0( struct xt_node, 1 );
356        struct xt_node *c, *dc = NULL;
357        int i;
358       
359        /* Let's NOT copy the parent element here BTW! Only do it for children. */
360       
361        dup->name = g_strdup( node->name );
362        dup->flags = node->flags;
363        if( node->text )
364        {
365                dup->text = g_memdup( node->text, node->text_len + 1 );
366                dup->text_len = node->text_len;
367        }
368       
369        /* Count the number of attributes and allocate the new array. */
370        for( i = 0; node->attr[i].key; i ++ );
371        dup->attr = g_new0( struct xt_attr, i + 1 );
372       
373        /* Copy them all! */
374        for( i --; i >= 0; i -- )
375        {
376                dup->attr[i].key = g_strdup( node->attr[i].key );
377                dup->attr[i].value = g_strdup( node->attr[i].value );
378        }
379       
380        /* This nice mysterious loop takes care of the children. */
381        for( c = node->children; c; c = c->next )
382        {
383                if( dc == NULL )
384                        dc = dup->children = xt_dup( c );
385                else
386                        dc = ( dc->next = xt_dup( c ) );
387               
388                dc->parent = dup;
389        }
390       
391        return dup;
392}
393
394/* Frees a node. This doesn't clean up references to itself from parents! */
395void xt_free_node( struct xt_node *node )
396{
397        int i;
398       
399        if( !node )
400                return;
401       
402        g_free( node->name );
403        g_free( node->text );
404       
405        for( i = 0; node->attr[i].key; i ++ )
406        {
407                g_free( node->attr[i].key );
408                g_free( node->attr[i].value );
409        }
410        g_free( node->attr );
411       
412        while( node->children )
413        {
414                struct xt_node *next = node->children->next;
415               
416                xt_free_node( node->children );
417                node->children = next;
418        }
419       
420        g_free( node );
421}
422
423void xt_free( struct xt_parser *xt )
424{
425        if( !xt )
426                return;
427       
428        if( xt->root )
429                xt_free_node( xt->root );
430       
431        g_markup_parse_context_free( xt->parser );
432       
433        g_free( xt );
434}
435
436/* To find a node's child with a specific name, pass the node's children
437   list, not the node itself! The reason you have to do this by hand: So
438   that you can also use this function as a find-next. */
439struct xt_node *xt_find_node( struct xt_node *node, const char *name )
440{
441        while( node )
442        {
443                char *colon;
444               
445                if( g_strcasecmp( node->name, name ) == 0 ||
446                    ( ( colon = strchr( node->name, ':' ) ) &&
447                      g_strcasecmp( colon + 1, name ) == 0 ) )
448                        break;
449               
450                node = node->next;
451        }
452       
453        return node;
454}
455
456/* More advanced than the one above, understands something like
457   ../foo/bar to find a subnode bar of a node foo which is a child
458   of node's parent. Pass the node directly, not its list of children. */
459struct xt_node *xt_find_path( struct xt_node *node, const char *name )
460{
461        while( name && *name && node )
462        {
463                char *colon, *slash;
464                int n;
465               
466                if( ( slash = strchr( name, '/' ) ) )
467                        n = slash - name;
468                else
469                        n = strlen( name );
470               
471                if( strncmp( name, "..", n ) == 0 )
472                {
473                        node = node->parent;
474                }
475                else
476                {
477                        node = node->children;
478                       
479                        while( node )
480                        {
481                                if( g_strncasecmp( node->name, name, n ) == 0 ||
482                                    ( ( colon = strchr( node->name, ':' ) ) &&
483                                      g_strncasecmp( colon + 1, name, n ) == 0 ) )
484                                        break;
485                               
486                                node = node->next;
487                        }
488                }
489               
490                name = slash ? slash + 1 : NULL;
491        }
492       
493        return node;
494}
495
496char *xt_find_attr( struct xt_node *node, const char *key )
497{
498        int i;
499        char *colon;
500       
501        if( !node )
502                return NULL;
503       
504        for( i = 0; node->attr[i].key; i ++ )
505                if( g_strcasecmp( node->attr[i].key, key ) == 0 )
506                        break;
507       
508        /* This is an awful hack that only takes care of namespace prefixes
509           inside a tag. Since IMHO excessive namespace usage in XMPP is
510           massive overkill anyway (this code exists for almost four years
511           now and never really missed it): Meh. */
512        if( !node->attr[i].key && strcmp( key, "xmlns" ) == 0 &&
513            ( colon = strchr( node->name, ':' ) ) )
514        {
515                *colon = '\0';
516                for( i = 0; node->attr[i].key; i ++ )
517                        if( strncmp( node->attr[i].key, "xmlns:", 6 ) == 0 &&
518                            strcmp( node->attr[i].key + 6, node->name ) == 0 )
519                                break;
520                *colon = ':';
521        }
522       
523        return node->attr[i].value;
524}
525
526struct xt_node *xt_find_node_by_attr( struct xt_node *xt, const char *tag, const char *key, const char *value ) {
527        struct xt_node *c;
528        char *s;
529
530        for( c = xt; ( c = xt_find_node( c, tag ) ); c = c->next )
531        {
532                if( ( s = xt_find_attr( c, key ) ) && strcmp( s, value ) == 0 )
533                {
534                        return c;
535                }
536        }
537        return NULL;
538}
539
540
541/* Strip a few non-printable characters that aren't allowed in XML streams
542   (and upset some XMPP servers for example). */
543void xt_strip_text( char *in )
544{
545        char *out = in;
546        static const char nonprint[32] = {
547                0, 0, 0, 0, 0, 0, 0, 0, /* 0..7 */
548                0, 1, 1, 0, 0, 1, 0, 0, /* 9 (tab), 10 (\n), 13 (\r) */
549        };
550       
551        if( !in )
552                return;
553
554        while( *in )
555        {
556                if( (unsigned int) *in >= ' ' || nonprint[(unsigned int) *in] )
557                        *out ++ = *in;
558                in ++;
559        }
560        *out = *in;
561}
562
563struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children )
564{
565        struct xt_node *node, *c;
566       
567        node = g_new0( struct xt_node, 1 );
568        node->name = g_strdup( name );
569        node->children = children;
570        node->attr = g_new0( struct xt_attr, 1 );
571       
572        if( text )
573        {
574                node->text = g_strdup( text );
575                xt_strip_text( node->text );
576                node->text_len = strlen( node->text );
577        }
578       
579        for( c = children; c; c = c->next )
580        {
581                if( c->parent != NULL )
582                {
583                        /* ERROR CONDITION: They seem to have a parent already??? */
584                }
585               
586                c->parent = node;
587        }
588       
589        return node;
590}
591
592void xt_add_child( struct xt_node *parent, struct xt_node *child )
593{
594        struct xt_node *node;
595       
596        /* This function can actually be used to add more than one child, so
597           do handle this properly. */
598        for( node = child; node; node = node->next )
599        {
600                if( node->parent != NULL )
601                {
602                        /* ERROR CONDITION: They seem to have a parent already??? */
603                }
604               
605                node->parent = parent;
606        }
607       
608        if( parent->children == NULL )
609        {
610                parent->children = child;
611        }
612        else
613        {
614                for( node = parent->children; node->next; node = node->next );
615                node->next = child;
616        }
617}
618
619/* Same, but at the beginning. */
620void xt_insert_child( struct xt_node *parent, struct xt_node *child )
621{
622        struct xt_node *node, *last = NULL;
623       
624        if( child == NULL )
625                return; /* BUG */
626       
627        for( node = child; node; node = node->next )
628        {
629                if( node->parent != NULL )
630                {
631                        /* ERROR CONDITION: They seem to have a parent already??? */
632                }
633               
634                node->parent = parent;
635                last = node;
636        }
637       
638        last->next = parent->children;
639        parent->children = child;
640}
641
642void xt_add_attr( struct xt_node *node, const char *key, const char *value )
643{
644        int i;
645       
646        /* Now actually it'd be nice if we can also change existing attributes
647           (which actually means this function doesn't have the right name).
648           So let's find out if we have this attribute already... */
649        for( i = 0; node->attr[i].key; i ++ )
650                if( strcmp( node->attr[i].key, key ) == 0 )
651                        break;
652       
653        if( node->attr[i].key == NULL )
654        {
655                /* If not, allocate space for a new attribute. */
656                node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
657                node->attr[i].key = g_strdup( key );
658                node->attr[i+1].key = NULL;
659        }
660        else
661        {
662                /* Otherwise, free the old value before setting the new one. */
663                g_free( node->attr[i].value );
664        }
665       
666        node->attr[i].value = g_strdup( value );
667}
668
669int xt_remove_attr( struct xt_node *node, const char *key )
670{
671        int i, last;
672       
673        for( i = 0; node->attr[i].key; i ++ )
674                if( strcmp( node->attr[i].key, key ) == 0 )
675                        break;
676       
677        /* If we didn't find the attribute... */
678        if( node->attr[i].key == NULL )
679                return 0;
680       
681        g_free( node->attr[i].key );
682        g_free( node->attr[i].value );
683       
684        /* If it's the last, this is easy: */
685        if( node->attr[i+1].key == NULL )
686        {
687                node->attr[i].key = node->attr[i].value = NULL;
688        }
689        else /* It's also pretty easy, actually. */
690        {
691                /* Find the last item. */
692                for( last = i + 1; node->attr[last+1].key; last ++ );
693               
694                node->attr[i] = node->attr[last];
695                node->attr[last].key = NULL;
696                node->attr[last].value = NULL;
697        }
698       
699        /* Let's not bother with reallocating memory here. It takes time and
700           most packets don't stay in memory for long anyway. */
701       
702        return 1;
703}
Note: See TracBrowser for help on using the repository browser.