Coverage Report

Created: 2026-02-14 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pidgin/libpurple/xmlnode.c
Line
Count
Source
1
/**
2
 * @file xmlnode.c XML DOM functions
3
 */
4
5
/* purple
6
 *
7
 * Purple is the legal property of its developers, whose names are too numerous
8
 * to list here.  Please refer to the COPYRIGHT file distributed with this
9
 * source distribution.
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 2 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program; if not, write to the Free Software
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
24
 */
25
26
/* A lot of this code at least resembles the code in libxode, but since
27
 * libxode uses memory pools that we simply have no need for, I decided to
28
 * write my own stuff.  Also, re-writing this lets me be as lightweight
29
 * as I want to be.  Thank you libxode for giving me a good starting point */
30
#define _PURPLE_XMLNODE_C_
31
32
#include "internal.h"
33
#include "glibcompat.h"
34
#include "debug.h"
35
36
#include <libxml/parser.h>
37
#include <string.h>
38
#include <glib.h>
39
40
#include "dbus-maybe.h"
41
#include "util.h"
42
#include "xmlnode.h"
43
44
#ifdef _WIN32
45
# define NEWLINE_S "\r\n"
46
#else
47
0
# define NEWLINE_S "\n"
48
#endif
49
50
static xmlnode*
51
new_node(const char *name, XMLNodeType type)
52
18.9k
{
53
18.9k
  xmlnode *node = g_new0(xmlnode, 1);
54
55
18.9k
  node->name = g_strdup(name);
56
18.9k
  node->type = type;
57
58
18.9k
  PURPLE_DBUS_REGISTER_POINTER(node, xmlnode);
59
60
18.9k
  return node;
61
18.9k
}
62
63
xmlnode*
64
xmlnode_new(const char *name)
65
2.39k
{
66
2.39k
  g_return_val_if_fail(name != NULL && *name != '\0', NULL);
67
68
2.39k
  return new_node(name, XMLNODE_TYPE_TAG);
69
2.39k
}
70
71
xmlnode *
72
xmlnode_new_child(xmlnode *parent, const char *name)
73
7.30k
{
74
7.30k
  xmlnode *node;
75
76
7.30k
  g_return_val_if_fail(parent != NULL, NULL);
77
7.30k
  g_return_val_if_fail(name != NULL && *name != '\0', NULL);
78
79
7.30k
  node = new_node(name, XMLNODE_TYPE_TAG);
80
81
7.30k
  xmlnode_insert_child(parent, node);
82
83
7.30k
  return node;
84
7.30k
}
85
86
void
87
xmlnode_insert_child(xmlnode *parent, xmlnode *child)
88
16.5k
{
89
16.5k
  g_return_if_fail(parent != NULL);
90
16.5k
  g_return_if_fail(child != NULL);
91
92
16.5k
  child->parent = parent;
93
94
16.5k
  if(parent->lastchild) {
95
8.64k
    parent->lastchild->next = child;
96
8.64k
  } else {
97
7.93k
    parent->child = child;
98
7.93k
  }
99
100
16.5k
  parent->lastchild = child;
101
16.5k
}
102
103
void
104
xmlnode_insert_data(xmlnode *node, const char *data, gssize size)
105
3.35k
{
106
3.35k
  xmlnode *child;
107
3.35k
  gsize real_size;
108
109
3.35k
  g_return_if_fail(node != NULL);
110
3.35k
  g_return_if_fail(data != NULL);
111
3.35k
  g_return_if_fail(size != 0);
112
113
3.35k
  real_size = size == -1 ? strlen(data) : (gsize)size;
114
115
3.35k
  child = new_node(NULL, XMLNODE_TYPE_DATA);
116
117
3.35k
  child->data = g_memdup2(data, real_size);
118
3.35k
  child->data_sz = real_size;
119
120
3.35k
  xmlnode_insert_child(node, child);
121
3.35k
}
122
123
void
124
xmlnode_remove_attrib(xmlnode *node, const char *attr)
125
0
{
126
0
  xmlnode *attr_node, *sibling = NULL;
127
128
0
  g_return_if_fail(node != NULL);
129
0
  g_return_if_fail(attr != NULL);
130
131
0
  attr_node = node->child;
132
0
  while (attr_node) {
133
0
    if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
134
0
        purple_strequal(attr_node->name, attr))
135
0
    {
136
0
      if (node->lastchild == attr_node) {
137
0
        node->lastchild = sibling;
138
0
      }
139
0
      if (sibling == NULL) {
140
0
        node->child = attr_node->next;
141
0
        xmlnode_free(attr_node);
142
0
        attr_node = node->child;
143
0
      } else {
144
0
        sibling->next = attr_node->next;
145
0
        sibling = attr_node->next;
146
0
        xmlnode_free(attr_node);
147
0
        attr_node = sibling;
148
0
      }
149
0
    }
150
0
    else
151
0
    {
152
0
      attr_node = attr_node->next;
153
0
    }
154
0
    sibling = attr_node;
155
0
  }
156
0
}
157
158
void
159
xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns)
160
5.91k
{
161
5.91k
  xmlnode *attr_node, *sibling = NULL;
162
163
5.91k
  g_return_if_fail(node != NULL);
164
5.91k
  g_return_if_fail(attr != NULL);
165
166
13.4k
  for(attr_node = node->child; attr_node; attr_node = attr_node->next)
167
8.89k
  {
168
8.89k
    if(attr_node->type == XMLNODE_TYPE_ATTRIB &&
169
8.89k
       purple_strequal(attr,  attr_node->name) &&
170
1.38k
       purple_strequal(xmlns, attr_node->xmlns))
171
1.38k
    {
172
1.38k
      if(sibling == NULL) {
173
359
        node->child = attr_node->next;
174
1.02k
      } else {
175
1.02k
        sibling->next = attr_node->next;
176
1.02k
      }
177
1.38k
      if (node->lastchild == attr_node) {
178
530
        node->lastchild = sibling;
179
530
      }
180
1.38k
      xmlnode_free(attr_node);
181
1.38k
      return;
182
1.38k
    }
183
7.51k
    sibling = attr_node;
184
7.51k
  }
185
5.91k
}
186
187
void
188
xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value)
189
0
{
190
0
  xmlnode_remove_attrib(node, attr);
191
0
  xmlnode_set_attrib_full(node, attr, NULL, NULL, value);
192
0
}
193
194
void
195
xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value)
196
0
{
197
0
  xmlnode_set_attrib_full(node, attr, xmlns, NULL, value);
198
0
}
199
200
void
201
xmlnode_set_attrib_with_prefix(xmlnode *node, const char *attr, const char *prefix, const char *value)
202
0
{
203
0
  xmlnode_set_attrib_full(node, attr, NULL, prefix, value);
204
0
}
205
206
void
207
xmlnode_set_attrib_full(xmlnode *node, const char *attr, const char *xmlns, const char *prefix, const char *value)
208
5.91k
{
209
5.91k
  xmlnode *attrib_node;
210
211
5.91k
  g_return_if_fail(node != NULL);
212
5.91k
  g_return_if_fail(attr != NULL);
213
5.91k
  g_return_if_fail(value != NULL);
214
215
5.91k
  xmlnode_remove_attrib_with_namespace(node, attr, xmlns);
216
5.91k
  attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB);
217
218
5.91k
  attrib_node->data = g_strdup(value);
219
5.91k
  attrib_node->xmlns = g_strdup(xmlns);
220
5.91k
  attrib_node->prefix = g_strdup(prefix);
221
222
5.91k
  xmlnode_insert_child(node, attrib_node);
223
5.91k
}
224
225
226
const char *
227
xmlnode_get_attrib(const xmlnode *node, const char *attr)
228
0
{
229
0
  xmlnode *x;
230
231
0
  g_return_val_if_fail(node != NULL, NULL);
232
0
  g_return_val_if_fail(attr != NULL, NULL);
233
234
0
  for(x = node->child; x; x = x->next) {
235
0
    if(x->type == XMLNODE_TYPE_ATTRIB && purple_strequal(attr, x->name)) {
236
0
      return x->data;
237
0
    }
238
0
  }
239
240
0
  return NULL;
241
0
}
242
243
const char *
244
xmlnode_get_attrib_with_namespace(const xmlnode *node, const char *attr, const char *xmlns)
245
0
{
246
0
  const xmlnode *x;
247
248
0
  g_return_val_if_fail(node != NULL, NULL);
249
0
  g_return_val_if_fail(attr != NULL, NULL);
250
251
0
  for(x = node->child; x; x = x->next) {
252
0
    if(x->type == XMLNODE_TYPE_ATTRIB &&
253
0
       purple_strequal(attr,  x->name) &&
254
0
       purple_strequal(xmlns, x->xmlns)) {
255
0
      return x->data;
256
0
    }
257
0
  }
258
259
0
  return NULL;
260
0
}
261
262
263
void xmlnode_set_namespace(xmlnode *node, const char *xmlns)
264
9.70k
{
265
9.70k
  g_return_if_fail(node != NULL);
266
267
9.70k
  g_free(node->xmlns);
268
9.70k
  node->xmlns = g_strdup(xmlns);
269
9.70k
}
270
271
const char *xmlnode_get_namespace(xmlnode *node)
272
0
{
273
0
  g_return_val_if_fail(node != NULL, NULL);
274
275
0
  return node->xmlns;
276
0
}
277
278
void xmlnode_set_prefix(xmlnode *node, const char *prefix)
279
9.70k
{
280
9.70k
  g_return_if_fail(node != NULL);
281
282
9.70k
  g_free(node->prefix);
283
9.70k
  node->prefix = g_strdup(prefix);
284
9.70k
}
285
286
const char *xmlnode_get_prefix(const xmlnode *node)
287
0
{
288
0
  g_return_val_if_fail(node != NULL, NULL);
289
0
  return node->prefix;
290
0
}
291
292
xmlnode *xmlnode_get_parent(const xmlnode *child)
293
0
{
294
0
  g_return_val_if_fail(child != NULL, NULL);
295
0
  return child->parent;
296
0
}
297
298
void
299
xmlnode_free(xmlnode *node)
300
18.9k
{
301
18.9k
  xmlnode *x, *y;
302
303
18.9k
  g_return_if_fail(node != NULL);
304
305
  /* if we're part of a tree, remove ourselves from the tree first */
306
18.9k
  if(NULL != node->parent) {
307
16.5k
    if(node->parent->child == node) {
308
15.1k
      node->parent->child = node->next;
309
15.1k
      if (node->parent->lastchild == node)
310
7.80k
        node->parent->lastchild = node->next;
311
15.1k
    } else {
312
1.38k
      xmlnode *prev = node->parent->child;
313
4.32k
      while(prev && prev->next != node) {
314
2.94k
        prev = prev->next;
315
2.94k
      }
316
1.38k
      if(prev) {
317
0
        prev->next = node->next;
318
0
        if (node->parent->lastchild == node)
319
0
          node->parent->lastchild = prev;
320
0
      }
321
1.38k
    }
322
16.5k
  }
323
324
  /* now free our children */
325
18.9k
  x = node->child;
326
34.1k
  while(x) {
327
15.1k
    y = x->next;
328
15.1k
    xmlnode_free(x);
329
15.1k
    x = y;
330
15.1k
  }
331
332
  /* now dispose of ourselves */
333
18.9k
  g_free(node->name);
334
18.9k
  g_free(node->data);
335
18.9k
  g_free(node->xmlns);
336
18.9k
  g_free(node->prefix);
337
338
18.9k
  if(node->namespace_map)
339
3.02k
    g_hash_table_destroy(node->namespace_map);
340
341
18.9k
  PURPLE_DBUS_UNREGISTER_POINTER(node);
342
18.9k
  g_free(node);
343
18.9k
}
344
345
xmlnode*
346
xmlnode_get_child(const xmlnode *parent, const char *name)
347
0
{
348
0
  return xmlnode_get_child_with_namespace(parent, name, NULL);
349
0
}
350
351
xmlnode *
352
xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *ns)
353
0
{
354
0
  xmlnode *x, *ret = NULL;
355
0
  char **names;
356
0
  char *parent_name, *child_name;
357
358
0
  g_return_val_if_fail(parent != NULL, NULL);
359
0
  g_return_val_if_fail(name != NULL, NULL);
360
361
0
  names = g_strsplit(name, "/", 2);
362
0
  parent_name = names[0];
363
0
  child_name = names[1];
364
365
0
  for(x = parent->child; x; x = x->next) {
366
    /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
367
0
    const char *xmlns = NULL;
368
0
    if(ns)
369
0
      xmlns = xmlnode_get_namespace(x);
370
371
0
    if(x->type == XMLNODE_TYPE_TAG && purple_strequal(parent_name, x->name)
372
0
        && purple_strequal(ns, xmlns)) {
373
0
      ret = x;
374
0
      break;
375
0
    }
376
0
  }
377
378
0
  if(child_name && ret)
379
0
    ret = xmlnode_get_child(ret, child_name);
380
381
0
  g_strfreev(names);
382
0
  return ret;
383
0
}
384
385
char *
386
xmlnode_get_data(const xmlnode *node)
387
0
{
388
0
  GString *str = NULL;
389
0
  xmlnode *c;
390
391
0
  g_return_val_if_fail(node != NULL, NULL);
392
393
0
  for(c = node->child; c; c = c->next) {
394
0
    if(c->type == XMLNODE_TYPE_DATA) {
395
0
      if(!str)
396
0
        str = g_string_new_len(c->data, c->data_sz);
397
0
      else
398
0
        str = g_string_append_len(str, c->data, c->data_sz);
399
0
    }
400
0
  }
401
402
0
  if (str == NULL)
403
0
    return NULL;
404
405
0
  return g_string_free(str, FALSE);
406
0
}
407
408
char *
409
xmlnode_get_data_unescaped(const xmlnode *node)
410
0
{
411
0
  char *escaped = xmlnode_get_data(node);
412
413
0
  char *unescaped = escaped ? purple_unescape_html(escaped) : NULL;
414
415
0
  g_free(escaped);
416
417
0
  return unescaped;
418
0
}
419
420
static void
421
xmlnode_to_str_foreach_append_ns(const char *key, const char *value,
422
  GString *buf)
423
0
{
424
0
  if (*key) {
425
0
    g_string_append_printf(buf, " xmlns:%s='%s'", key, value);
426
0
  } else {
427
0
    g_string_append_printf(buf, " xmlns='%s'", value);
428
0
  }
429
0
}
430
431
static char *
432
xmlnode_to_str_helper(const xmlnode *node, int *len, gboolean formatting, int depth)
433
0
{
434
0
  GString *text = g_string_new("");
435
0
  const char *prefix;
436
0
  const xmlnode *c;
437
0
  char *node_name, *esc, *esc2, *tab = NULL;
438
0
  gboolean need_end = FALSE, pretty = formatting;
439
440
0
  g_return_val_if_fail(node != NULL, NULL);
441
442
0
  if(pretty && depth) {
443
0
    tab = g_strnfill(depth, '\t');
444
0
    text = g_string_append(text, tab);
445
0
  }
446
447
0
  node_name = g_markup_escape_text(node->name, -1);
448
0
  prefix = xmlnode_get_prefix(node);
449
450
0
  if (prefix) {
451
0
    g_string_append_printf(text, "<%s:%s", prefix, node_name);
452
0
  } else {
453
0
    g_string_append_printf(text, "<%s", node_name);
454
0
  }
455
456
0
  if (node->namespace_map) {
457
0
    g_hash_table_foreach(node->namespace_map,
458
0
      (GHFunc)xmlnode_to_str_foreach_append_ns, text);
459
0
  } else if (node->xmlns) {
460
0
    if(!node->parent || !purple_strequal(node->xmlns, node->parent->xmlns))
461
0
    {
462
0
      char *xmlns = g_markup_escape_text(node->xmlns, -1);
463
0
      g_string_append_printf(text, " xmlns='%s'", xmlns);
464
0
      g_free(xmlns);
465
0
    }
466
0
  }
467
0
  for(c = node->child; c; c = c->next)
468
0
  {
469
0
    if(c->type == XMLNODE_TYPE_ATTRIB) {
470
0
      const char *aprefix = xmlnode_get_prefix(c);
471
0
      esc = g_markup_escape_text(c->name, -1);
472
0
      esc2 = g_markup_escape_text(c->data, -1);
473
0
      if (aprefix) {
474
0
        g_string_append_printf(text, " %s:%s='%s'", aprefix, esc, esc2);
475
0
      } else {
476
0
        g_string_append_printf(text, " %s='%s'", esc, esc2);
477
0
      }
478
0
      g_free(esc);
479
0
      g_free(esc2);
480
0
    } else if(c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) {
481
0
      if(c->type == XMLNODE_TYPE_DATA)
482
0
        pretty = FALSE;
483
0
      need_end = TRUE;
484
0
    }
485
0
  }
486
487
0
  if(need_end) {
488
0
    g_string_append_printf(text, ">%s", pretty ? NEWLINE_S : "");
489
490
0
    for(c = node->child; c; c = c->next)
491
0
    {
492
0
      if(c->type == XMLNODE_TYPE_TAG) {
493
0
        int esc_len;
494
0
        esc = xmlnode_to_str_helper(c, &esc_len, pretty, depth+1);
495
0
        text = g_string_append_len(text, esc, esc_len);
496
0
        g_free(esc);
497
0
      } else if(c->type == XMLNODE_TYPE_DATA && c->data_sz > 0) {
498
0
        esc = g_markup_escape_text(c->data, c->data_sz);
499
0
        text = g_string_append(text, esc);
500
0
        g_free(esc);
501
0
      }
502
0
    }
503
504
0
    if(tab && pretty)
505
0
      text = g_string_append(text, tab);
506
0
    if (prefix) {
507
0
      g_string_append_printf(text, "</%s:%s>%s", prefix, node_name, formatting ? NEWLINE_S : "");
508
0
    } else {
509
0
      g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : "");
510
0
    }
511
0
  } else {
512
0
    g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : "");
513
0
  }
514
515
0
  g_free(node_name);
516
517
0
  g_free(tab);
518
519
0
  if(len)
520
0
    *len = text->len;
521
522
0
  return g_string_free(text, FALSE);
523
0
}
524
525
char *
526
xmlnode_to_str(const xmlnode *node, int *len)
527
0
{
528
0
  return xmlnode_to_str_helper(node, len, FALSE, 0);
529
0
}
530
531
char *
532
xmlnode_to_formatted_str(const xmlnode *node, int *len)
533
0
{
534
0
  char *xml, *xml_with_declaration;
535
536
0
  g_return_val_if_fail(node != NULL, NULL);
537
538
0
  xml = xmlnode_to_str_helper(node, len, TRUE, 0);
539
0
  xml_with_declaration =
540
0
    g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml);
541
0
  g_free(xml);
542
543
0
  if (len)
544
0
    *len += sizeof("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S) - 1;
545
546
0
  return xml_with_declaration;
547
0
}
548
549
struct _xmlnode_parser_data {
550
  xmlnode *current;
551
  gboolean error;
552
};
553
554
static void
555
xmlnode_parser_element_start_libxml(void *user_data,
556
           const xmlChar *element_name, const xmlChar *prefix, const xmlChar *xmlns,
557
           int nb_namespaces, const xmlChar **namespaces,
558
           int nb_attributes, int nb_defaulted, const xmlChar **attributes)
559
12.6k
{
560
12.6k
  struct _xmlnode_parser_data *xpd = user_data;
561
12.6k
  xmlnode *node;
562
12.6k
  int i, j;
563
564
12.6k
  if(!element_name || xpd->error) {
565
2.93k
    return;
566
9.70k
  } else {
567
9.70k
    if(xpd->current)
568
7.30k
      node = xmlnode_new_child(xpd->current, (const char*) element_name);
569
2.39k
    else
570
2.39k
      node = xmlnode_new((const char *) element_name);
571
572
9.70k
    xmlnode_set_namespace(node, (const char *) xmlns);
573
9.70k
    xmlnode_set_prefix(node, (const char *)prefix);
574
575
9.70k
    if (nb_namespaces != 0) {
576
3.02k
      node->namespace_map = g_hash_table_new_full(
577
3.02k
        g_str_hash, g_str_equal, g_free, g_free);
578
579
18.7k
      for (i = 0, j = 0; i < nb_namespaces; i++, j += 2) {
580
15.7k
        const char *key = (const char *)namespaces[j];
581
15.7k
        const char *val = (const char *)namespaces[j + 1];
582
15.7k
        g_hash_table_insert(node->namespace_map,
583
15.7k
          g_strdup(key ? key : ""), g_strdup(val ? val : ""));
584
15.7k
      }
585
3.02k
    }
586
587
15.6k
    for(i=0; i < nb_attributes * 5; i+=5) {
588
5.91k
      const char *name = (const char *)attributes[i];
589
5.91k
      const char *prefix = (const char *)attributes[i+1];
590
5.91k
      char *txt;
591
5.91k
      int attrib_len = attributes[i+4] - attributes[i+3];
592
5.91k
      char *attrib = g_strndup((const char *)attributes[i+3], attrib_len);
593
5.91k
      txt = attrib;
594
5.91k
      attrib = purple_unescape_text(txt);
595
5.91k
      g_free(txt);
596
5.91k
      xmlnode_set_attrib_full(node, name, NULL, prefix, attrib);
597
5.91k
      g_free(attrib);
598
5.91k
    }
599
600
9.70k
    xpd->current = node;
601
9.70k
  }
602
12.6k
}
603
604
static void
605
xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
606
         const xmlChar *prefix, const xmlChar *xmlns)
607
905
{
608
905
  struct _xmlnode_parser_data *xpd = user_data;
609
610
905
  if(!element_name || !xpd->current || xpd->error)
611
530
    return;
612
613
375
  if(xpd->current->parent) {
614
341
    if(!xmlStrcmp((xmlChar*) xpd->current->name, element_name))
615
341
      xpd->current = xpd->current->parent;
616
341
  }
617
375
}
618
619
static void
620
xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
621
4.90k
{
622
4.90k
  struct _xmlnode_parser_data *xpd = user_data;
623
624
4.90k
  if(!xpd->current || xpd->error)
625
1.24k
    return;
626
627
3.65k
  if(!text || !text_len)
628
306
    return;
629
630
3.35k
  xmlnode_insert_data(xpd->current, (const char*) text, text_len);
631
3.35k
}
632
633
static void
634
xmlnode_parser_error_libxml(void *user_data, const char *msg, ...)
635
0
{
636
0
  struct _xmlnode_parser_data *xpd = user_data;
637
0
  char errmsg[2048];
638
0
  va_list args;
639
640
0
  xpd->error = TRUE;
641
642
0
  va_start(args, msg);
643
0
  vsnprintf(errmsg, sizeof(errmsg), msg, args);
644
0
  va_end(args);
645
646
0
  purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg);
647
0
}
648
649
static void
650
xmlnode_parser_structural_error_libxml(void *user_data, xmlErrorPtr error)
651
355k
{
652
355k
  struct _xmlnode_parser_data *xpd = user_data;
653
654
355k
  if (error && (error->level == XML_ERR_ERROR ||
655
334k
                error->level == XML_ERR_FATAL)) {
656
334k
    xpd->error = TRUE;
657
334k
    purple_debug_error("xmlnode", "XML parser error for xmlnode %p: "
658
334k
                       "Domain %i, code %i, level %i: %s",
659
334k
                       user_data, error->domain, error->code, error->level,
660
334k
                       error->message ? error->message : "(null)\n");
661
334k
  } else if (error)
662
21.1k
    purple_debug_warning("xmlnode", "XML parser error for xmlnode %p: "
663
21.1k
                         "Domain %i, code %i, level %i: %s",
664
21.1k
                         user_data, error->domain, error->code, error->level,
665
21.1k
                         error->message ? error->message : "(null)\n");
666
0
  else
667
0
    purple_debug_warning("xmlnode", "XML parser error for xmlnode %p\n",
668
0
                         user_data);
669
355k
}
670
671
static xmlSAXHandler xmlnode_parser_libxml = {
672
  NULL, /* internalSubset */
673
  NULL, /* isStandalone */
674
  NULL, /* hasInternalSubset */
675
  NULL, /* hasExternalSubset */
676
  NULL, /* resolveEntity */
677
  NULL, /* getEntity */
678
  NULL, /* entityDecl */
679
  NULL, /* notationDecl */
680
  NULL, /* attributeDecl */
681
  NULL, /* elementDecl */
682
  NULL, /* unparsedEntityDecl */
683
  NULL, /* setDocumentLocator */
684
  NULL, /* startDocument */
685
  NULL, /* endDocument */
686
  NULL, /* startElement */
687
  NULL, /* endElement */
688
  NULL, /* reference */
689
  xmlnode_parser_element_text_libxml, /* characters */
690
  NULL, /* ignorableWhitespace */
691
  NULL, /* processingInstruction */
692
  NULL, /* comment */
693
  NULL, /* warning */
694
  xmlnode_parser_error_libxml, /* error */
695
  NULL, /* fatalError */
696
  NULL, /* getParameterEntity */
697
  NULL, /* cdataBlock */
698
  NULL, /* externalSubset */
699
  XML_SAX2_MAGIC, /* initialized */
700
  NULL, /* _private */
701
  xmlnode_parser_element_start_libxml, /* startElementNs */
702
  xmlnode_parser_element_end_libxml,   /* endElementNs   */
703
  xmlnode_parser_structural_error_libxml, /* serror */
704
};
705
706
xmlnode *
707
xmlnode_from_str(const char *str, gssize size)
708
12.5k
{
709
12.5k
  struct _xmlnode_parser_data *xpd;
710
12.5k
  xmlnode *ret;
711
12.5k
  gsize real_size;
712
713
12.5k
  g_return_val_if_fail(str != NULL, NULL);
714
715
12.5k
  real_size = size < 0 ? strlen(str) : (gsize)size;
716
12.5k
  xpd = g_new0(struct _xmlnode_parser_data, 1);
717
718
12.5k
  if (xmlSAXUserParseMemory(&xmlnode_parser_libxml, xpd, str, real_size) < 0) {
719
0
    while(xpd->current && xpd->current->parent)
720
0
      xpd->current = xpd->current->parent;
721
0
    if(xpd->current)
722
0
      xmlnode_free(xpd->current);
723
0
    xpd->current = NULL;
724
0
  }
725
12.5k
  ret = xpd->current;
726
12.5k
  if (xpd->error) {
727
12.5k
    ret = NULL;
728
12.5k
    if (xpd->current) {
729
      /* If an error occurred while parsing, we may be
730
       * pointing at some random child, so walk back up the
731
       * tree in order to free everything. */
732
9.36k
      while (xpd->current->parent != NULL) {
733
6.96k
        xpd->current = xpd->current->parent;
734
6.96k
      }
735
2.39k
      xmlnode_free(xpd->current);
736
2.39k
    }
737
12.5k
  }
738
739
12.5k
  g_free(xpd);
740
12.5k
  return ret;
741
12.5k
}
742
743
xmlnode *
744
xmlnode_from_file(const char *dir,const char *filename, const char *description, const char *process)
745
0
{
746
0
  gchar *filename_full;
747
0
  GError *error = NULL;
748
0
  gchar *contents = NULL;
749
0
  gsize length;
750
0
  xmlnode *node = NULL;
751
752
0
  g_return_val_if_fail(dir != NULL, NULL);
753
754
0
  purple_debug_info(process, "Reading file %s from directory %s\n",
755
0
          filename, dir);
756
757
0
  filename_full = g_build_filename(dir, filename, NULL);
758
759
0
  if (!g_file_test(filename_full, G_FILE_TEST_EXISTS))
760
0
  {
761
0
    purple_debug_info(process, "File %s does not exist (this is not "
762
0
            "necessarily an error)\n", filename_full);
763
0
    g_free(filename_full);
764
0
    return NULL;
765
0
  }
766
767
0
  if (!g_file_get_contents(filename_full, &contents, &length, &error))
768
0
  {
769
0
    purple_debug_error(process, "Error reading file %s: %s\n",
770
0
             filename_full, error->message);
771
0
    g_error_free(error);
772
0
  }
773
774
0
  if ((contents != NULL) && (length > 0))
775
0
  {
776
0
    node = xmlnode_from_str(contents, length);
777
778
    /* If we were unable to parse the file then save its contents to a backup file */
779
0
    if (node == NULL)
780
0
    {
781
0
      gchar *filename_temp, *filename_temp_full;
782
783
0
      filename_temp = g_strdup_printf("%s~", filename);
784
0
      filename_temp_full = g_build_filename(dir, filename_temp, NULL);
785
786
0
      purple_debug_error("util", "Error parsing file %s.  Renaming old "
787
0
               "file to %s\n", filename_full, filename_temp);
788
0
      purple_util_write_data_to_file_absolute(filename_temp_full, contents, length);
789
790
0
      g_free(filename_temp_full);
791
0
      g_free(filename_temp);
792
0
    }
793
794
0
    g_free(contents);
795
0
  }
796
797
  /* If we could not parse the file then show the user an error message */
798
0
  if (node == NULL)
799
0
  {
800
0
    gchar *title, *msg;
801
0
    title = g_strdup_printf(_("Error Reading %s"), filename);
802
0
    msg = g_strdup_printf(_("An error was encountered reading your "
803
0
          "%s.  The file has not been loaded, and the old file "
804
0
          "has been renamed to %s~."), description, filename_full);
805
0
    purple_notify_error(NULL, NULL, title, msg);
806
0
    g_free(title);
807
0
    g_free(msg);
808
0
  }
809
810
0
  g_free(filename_full);
811
812
0
  return node;
813
0
}
814
815
static void
816
xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
817
0
{
818
0
  GHashTable *ret = (GHashTable *)user_data;
819
0
  g_hash_table_insert(ret, g_strdup(key), g_strdup(value));
820
0
}
821
822
xmlnode *
823
xmlnode_copy(const xmlnode *src)
824
0
{
825
0
  xmlnode *ret;
826
0
  xmlnode *child;
827
0
  xmlnode *sibling = NULL;
828
829
0
  g_return_val_if_fail(src != NULL, NULL);
830
831
0
  ret = new_node(src->name, src->type);
832
0
  ret->xmlns = g_strdup(src->xmlns);
833
0
  if (src->data) {
834
0
    if (src->data_sz) {
835
0
      ret->data = g_memdup2(src->data, src->data_sz);
836
0
      ret->data_sz = src->data_sz;
837
0
    } else {
838
0
      ret->data = g_strdup(src->data);
839
0
    }
840
0
  }
841
0
  ret->prefix = g_strdup(src->prefix);
842
0
  if (src->namespace_map) {
843
0
    ret->namespace_map = g_hash_table_new_full(g_str_hash, g_str_equal,
844
0
                                               g_free, g_free);
845
0
    g_hash_table_foreach(src->namespace_map, xmlnode_copy_foreach_ns, ret->namespace_map);
846
0
  }
847
848
0
  for (child = src->child; child; child = child->next) {
849
0
    if (sibling) {
850
0
      sibling->next = xmlnode_copy(child);
851
0
      sibling = sibling->next;
852
0
    } else {
853
0
      ret->child = sibling = xmlnode_copy(child);
854
0
    }
855
0
    sibling->parent = ret;
856
0
  }
857
858
0
  ret->lastchild = sibling;
859
860
0
  return ret;
861
0
}
862
863
xmlnode *
864
xmlnode_get_next_twin(xmlnode *node)
865
0
{
866
0
  xmlnode *sibling;
867
0
  const char *ns = xmlnode_get_namespace(node);
868
869
0
  g_return_val_if_fail(node != NULL, NULL);
870
0
  g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL);
871
872
0
  for(sibling = node->next; sibling; sibling = sibling->next) {
873
    /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
874
0
    const char *xmlns = NULL;
875
0
    if(ns)
876
0
      xmlns = xmlnode_get_namespace(sibling);
877
878
0
    if(sibling->type == XMLNODE_TYPE_TAG && purple_strequal(node->name, sibling->name) &&
879
0
        purple_strequal(ns, xmlns))
880
0
      return sibling;
881
0
  }
882
883
0
  return NULL;
884
0
}