source: lib/xmltree.c @ af09afd

Last change on this file since af09afd was af09afd, checked in by GitHub <noreply@…>, at 2023-04-02T16:40:12Z

Simplify g_memdup2() usage (#179)

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