source: lib/xmltree.c @ 10425b2

Last change on this file since 10425b2 was 552da22, checked in by GitHub <noreply@…>, at 2023-04-01T20:29:42Z

Use g_memdup2() with glib versions >= 2.68.0 (#168)

  • Use g_memdup2() with glib versions >= 2.68.0

g_memdup() was deprecated in glib 2.68.0. The new function is
g_memdup2(). Still support building with older versions of glib via
macros.

Signed-off-by: David Cantrell <dcantrell@…>

  • Fall back to g_memdup when g_memdup2 is not available, rather than defining custom macro

Signed-off-by: David Cantrell <dcantrell@…>
Co-authored-by: Jelmer Vernooij <jelmer@…>

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