prefs: Convert Status/Idle page to glade.
[pidgin-git.git] / libpurple / xmlnode.c
bloba0ff9ada263ae397cdaf261cdb56214ed7244a83
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 /* A lot of this code at least resembles the code in libxode, but since
23 * libxode uses memory pools that we simply have no need for, I decided to
24 * write my own stuff. Also, re-writing this lets me be as lightweight
25 * as I want to be. Thank you libxode for giving me a good starting point */
26 #define _PURPLE_XMLNODE_C_
28 #include "internal.h"
29 #include "debug.h"
31 #include <libxml/parser.h>
32 #include <string.h>
33 #include <glib.h>
35 #include "dbus-maybe.h"
36 #include "util.h"
37 #include "xmlnode.h"
39 #ifdef _WIN32
40 # define NEWLINE_S "\r\n"
41 #else
42 # define NEWLINE_S "\n"
43 #endif
45 static PurpleXmlNode*
46 new_node(const char *name, PurpleXmlNodeType type)
48 PurpleXmlNode *node = g_new0(PurpleXmlNode, 1);
50 node->name = g_strdup(name);
51 node->type = type;
53 // PURPLE_DBUS_REGISTER_POINTER(node, PurpleXmlNode);
55 return node;
58 PurpleXmlNode*
59 purple_xmlnode_new(const char *name)
61 g_return_val_if_fail(name != NULL && *name != '\0', NULL);
63 return new_node(name, PURPLE_XMLNODE_TYPE_TAG);
66 PurpleXmlNode *
67 purple_xmlnode_new_child(PurpleXmlNode *parent, const char *name)
69 PurpleXmlNode *node;
71 g_return_val_if_fail(parent != NULL, NULL);
72 g_return_val_if_fail(name != NULL && *name != '\0', NULL);
74 node = new_node(name, PURPLE_XMLNODE_TYPE_TAG);
76 purple_xmlnode_insert_child(parent, node);
78 return node;
81 void
82 purple_xmlnode_insert_child(PurpleXmlNode *parent, PurpleXmlNode *child)
84 g_return_if_fail(parent != NULL);
85 g_return_if_fail(child != NULL);
87 child->parent = parent;
89 if(parent->lastchild) {
90 parent->lastchild->next = child;
91 } else {
92 parent->child = child;
95 parent->lastchild = child;
98 void
99 purple_xmlnode_insert_data(PurpleXmlNode *node, const char *data, gssize size)
101 PurpleXmlNode *child;
102 gsize real_size;
104 g_return_if_fail(node != NULL);
105 g_return_if_fail(data != NULL);
106 g_return_if_fail(size != 0);
108 real_size = size == -1 ? strlen(data) : (gsize)size;
110 child = new_node(NULL, PURPLE_XMLNODE_TYPE_DATA);
112 child->data = g_memdup(data, real_size);
113 child->data_sz = real_size;
115 purple_xmlnode_insert_child(node, child);
118 void
119 purple_xmlnode_remove_attrib(PurpleXmlNode *node, const char *attr)
121 PurpleXmlNode *attr_node, *sibling = NULL;
123 g_return_if_fail(node != NULL);
124 g_return_if_fail(attr != NULL);
126 attr_node = node->child;
127 while (attr_node) {
128 if(attr_node->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
129 purple_strequal(attr_node->name, attr))
131 if (node->lastchild == attr_node) {
132 node->lastchild = sibling;
134 if (sibling == NULL) {
135 node->child = attr_node->next;
136 purple_xmlnode_free(attr_node);
137 attr_node = node->child;
138 } else {
139 sibling->next = attr_node->next;
140 sibling = attr_node->next;
141 purple_xmlnode_free(attr_node);
142 attr_node = sibling;
145 else
147 attr_node = attr_node->next;
149 sibling = attr_node;
153 void
154 purple_xmlnode_remove_attrib_with_namespace(PurpleXmlNode *node, const char *attr, const char *xmlns)
156 PurpleXmlNode *attr_node, *sibling = NULL;
158 g_return_if_fail(node != NULL);
159 g_return_if_fail(attr != NULL);
161 for(attr_node = node->child; attr_node; attr_node = attr_node->next)
163 if(attr_node->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
164 purple_strequal(attr, attr_node->name) &&
165 purple_strequal(xmlns, attr_node->xmlns))
167 if(sibling == NULL) {
168 node->child = attr_node->next;
169 } else {
170 sibling->next = attr_node->next;
172 if (node->lastchild == attr_node) {
173 node->lastchild = sibling;
175 purple_xmlnode_free(attr_node);
176 return;
178 sibling = attr_node;
182 void
183 purple_xmlnode_set_attrib(PurpleXmlNode *node, const char *attr, const char *value)
185 purple_xmlnode_remove_attrib(node, attr);
186 purple_xmlnode_set_attrib_full(node, attr, NULL, NULL, value);
189 void
190 purple_xmlnode_set_attrib_full(PurpleXmlNode *node, const char *attr, const char *xmlns, const char *prefix, const char *value)
192 PurpleXmlNode *attrib_node;
194 g_return_if_fail(node != NULL);
195 g_return_if_fail(attr != NULL);
196 g_return_if_fail(value != NULL);
198 purple_xmlnode_remove_attrib_with_namespace(node, attr, xmlns);
199 attrib_node = new_node(attr, PURPLE_XMLNODE_TYPE_ATTRIB);
201 attrib_node->data = g_strdup(value);
202 attrib_node->xmlns = g_strdup(xmlns);
203 attrib_node->prefix = g_strdup(prefix);
205 purple_xmlnode_insert_child(node, attrib_node);
209 const char *
210 purple_xmlnode_get_attrib(const PurpleXmlNode *node, const char *attr)
212 PurpleXmlNode *x;
214 g_return_val_if_fail(node != NULL, NULL);
215 g_return_val_if_fail(attr != NULL, NULL);
217 for(x = node->child; x; x = x->next) {
218 if(x->type == PURPLE_XMLNODE_TYPE_ATTRIB && purple_strequal(attr, x->name)) {
219 return x->data;
223 return NULL;
226 const char *
227 purple_xmlnode_get_attrib_with_namespace(const PurpleXmlNode *node, const char *attr, const char *xmlns)
229 const PurpleXmlNode *x;
231 g_return_val_if_fail(node != NULL, NULL);
232 g_return_val_if_fail(attr != NULL, NULL);
234 for(x = node->child; x; x = x->next) {
235 if(x->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
236 purple_strequal(attr, x->name) &&
237 purple_strequal(xmlns, x->xmlns)) {
238 return x->data;
242 return NULL;
246 void purple_xmlnode_set_namespace(PurpleXmlNode *node, const char *xmlns)
248 char *tmp;
249 g_return_if_fail(node != NULL);
251 tmp = node->xmlns;
252 node->xmlns = g_strdup(xmlns);
254 if (node->namespace_map) {
255 g_hash_table_insert(node->namespace_map,
256 g_strdup(""), g_strdup(xmlns));
259 g_free(tmp);
262 const char *purple_xmlnode_get_namespace(const PurpleXmlNode *node)
264 g_return_val_if_fail(node != NULL, NULL);
266 return node->xmlns;
269 const char *purple_xmlnode_get_default_namespace(const PurpleXmlNode *node)
271 const PurpleXmlNode *current_node;
272 const char *ns = NULL;
274 g_return_val_if_fail(node != NULL, NULL);
276 current_node = node;
277 while (current_node) {
278 /* If this node does *not* have a prefix, node->xmlns is the default
279 * namespace. Otherwise, it's the prefix namespace.
281 if (!current_node->prefix && current_node->xmlns) {
282 return current_node->xmlns;
283 } else if (current_node->namespace_map) {
284 ns = g_hash_table_lookup(current_node->namespace_map, "");
285 if (ns && *ns)
286 return ns;
289 current_node = current_node->parent;
292 return ns;
295 void purple_xmlnode_set_prefix(PurpleXmlNode *node, const char *prefix)
297 g_return_if_fail(node != NULL);
299 g_free(node->prefix);
300 node->prefix = g_strdup(prefix);
303 const char *purple_xmlnode_get_prefix(const PurpleXmlNode *node)
305 g_return_val_if_fail(node != NULL, NULL);
306 return node->prefix;
309 const char *purple_xmlnode_get_prefix_namespace(const PurpleXmlNode *node, const char *prefix)
311 const PurpleXmlNode *current_node;
313 g_return_val_if_fail(node != NULL, NULL);
314 g_return_val_if_fail(prefix != NULL, purple_xmlnode_get_default_namespace(node));
316 current_node = node;
317 while (current_node) {
318 if (current_node->prefix && g_str_equal(prefix, current_node->prefix) &&
319 current_node->xmlns) {
320 return current_node->xmlns;
321 } else if (current_node->namespace_map) {
322 const char *ns = g_hash_table_lookup(current_node->namespace_map, prefix);
323 if (ns && *ns) {
324 return ns;
328 current_node = current_node->parent;
331 return NULL;
334 void purple_xmlnode_strip_prefixes(PurpleXmlNode *node)
336 PurpleXmlNode *child;
337 const char *prefix;
339 g_return_if_fail(node != NULL);
341 for (child = node->child; child; child = child->next) {
342 if (child->type == PURPLE_XMLNODE_TYPE_TAG)
343 purple_xmlnode_strip_prefixes(child);
346 prefix = purple_xmlnode_get_prefix(node);
347 if (prefix) {
348 const char *ns = purple_xmlnode_get_prefix_namespace(node, prefix);
349 purple_xmlnode_set_namespace(node, ns);
350 purple_xmlnode_set_prefix(node, NULL);
351 } else {
352 purple_xmlnode_set_namespace(node, purple_xmlnode_get_default_namespace(node));
356 PurpleXmlNode *purple_xmlnode_get_parent(const PurpleXmlNode *child)
358 g_return_val_if_fail(child != NULL, NULL);
359 return child->parent;
362 void
363 purple_xmlnode_free(PurpleXmlNode *node)
365 PurpleXmlNode *x, *y;
367 g_return_if_fail(node != NULL);
369 /* if we're part of a tree, remove ourselves from the tree first */
370 if(NULL != node->parent) {
371 if(node->parent->child == node) {
372 node->parent->child = node->next;
373 if (node->parent->lastchild == node)
374 node->parent->lastchild = node->next;
375 } else {
376 PurpleXmlNode *prev = node->parent->child;
377 while(prev && prev->next != node) {
378 prev = prev->next;
380 if(prev) {
381 prev->next = node->next;
382 if (node->parent->lastchild == node)
383 node->parent->lastchild = prev;
388 /* now free our children */
389 x = node->child;
390 while(x) {
391 y = x->next;
392 purple_xmlnode_free(x);
393 x = y;
396 /* now dispose of ourselves */
397 g_free(node->name);
398 g_free(node->data);
399 g_free(node->xmlns);
400 g_free(node->prefix);
402 if(node->namespace_map)
403 g_hash_table_destroy(node->namespace_map);
405 // PURPLE_DBUS_UNREGISTER_POINTER(node);
406 g_free(node);
409 PurpleXmlNode*
410 purple_xmlnode_get_child(const PurpleXmlNode *parent, const char *name)
412 return purple_xmlnode_get_child_with_namespace(parent, name, NULL);
415 PurpleXmlNode *
416 purple_xmlnode_get_child_with_namespace(const PurpleXmlNode *parent, const char *name, const char *ns)
418 PurpleXmlNode *x, *ret = NULL;
419 char **names;
420 char *parent_name, *child_name;
422 g_return_val_if_fail(parent != NULL, NULL);
423 g_return_val_if_fail(name != NULL, NULL);
425 names = g_strsplit(name, "/", 2);
426 parent_name = names[0];
427 child_name = names[1];
429 for(x = parent->child; x; x = x->next) {
430 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
431 const char *xmlns = NULL;
432 if(ns)
433 xmlns = purple_xmlnode_get_namespace(x);
435 if(x->type == PURPLE_XMLNODE_TYPE_TAG && purple_strequal(parent_name, x->name)
436 && purple_strequal(ns, xmlns)) {
437 ret = x;
438 break;
442 if(child_name && ret)
443 ret = purple_xmlnode_get_child(ret, child_name);
445 g_strfreev(names);
446 return ret;
449 char *
450 purple_xmlnode_get_data(const PurpleXmlNode *node)
452 GString *str = NULL;
453 PurpleXmlNode *c;
455 g_return_val_if_fail(node != NULL, NULL);
457 for(c = node->child; c; c = c->next) {
458 if(c->type == PURPLE_XMLNODE_TYPE_DATA) {
459 if(!str)
460 str = g_string_new_len(c->data, c->data_sz);
461 else
462 str = g_string_append_len(str, c->data, c->data_sz);
466 if (str == NULL)
467 return NULL;
469 return g_string_free(str, FALSE);
472 char *
473 purple_xmlnode_get_data_unescaped(const PurpleXmlNode *node)
475 char *escaped = purple_xmlnode_get_data(node);
477 char *unescaped = escaped ? purple_unescape_html(escaped) : NULL;
479 g_free(escaped);
481 return unescaped;
484 static void
485 purple_xmlnode_to_str_foreach_append_ns(const char *key, const char *value,
486 GString *buf)
488 if (*key) {
489 g_string_append_printf(buf, " xmlns:%s='%s'", key, value);
490 } else {
491 g_string_append_printf(buf, " xmlns='%s'", value);
495 static char *
496 purple_xmlnode_to_str_helper(const PurpleXmlNode *node, int *len, gboolean formatting, int depth)
498 GString *text = g_string_new("");
499 const char *prefix;
500 const PurpleXmlNode *c;
501 char *node_name, *esc, *esc2, *tab = NULL;
502 gboolean need_end = FALSE, pretty = formatting;
504 g_return_val_if_fail(node != NULL, NULL);
506 if(pretty && depth) {
507 tab = g_strnfill(depth, '\t');
508 text = g_string_append(text, tab);
511 node_name = g_markup_escape_text(node->name, -1);
512 prefix = purple_xmlnode_get_prefix(node);
514 if (prefix) {
515 g_string_append_printf(text, "<%s:%s", prefix, node_name);
516 } else {
517 g_string_append_printf(text, "<%s", node_name);
520 if (node->namespace_map) {
521 g_hash_table_foreach(node->namespace_map,
522 (GHFunc)purple_xmlnode_to_str_foreach_append_ns, text);
523 } else {
524 /* Figure out if this node has a different default namespace from parent */
525 const char *xmlns = NULL;
526 const char *parent_xmlns = NULL;
527 if (!prefix)
528 xmlns = node->xmlns;
530 if (!xmlns)
531 xmlns = purple_xmlnode_get_default_namespace(node);
532 if (node->parent)
533 parent_xmlns = purple_xmlnode_get_default_namespace(node->parent);
534 if (!purple_strequal(xmlns, parent_xmlns))
536 char *escaped_xmlns = g_markup_escape_text(xmlns, -1);
537 g_string_append_printf(text, " xmlns='%s'", escaped_xmlns);
538 g_free(escaped_xmlns);
541 for(c = node->child; c; c = c->next)
543 if(c->type == PURPLE_XMLNODE_TYPE_ATTRIB) {
544 const char *aprefix = purple_xmlnode_get_prefix(c);
545 esc = g_markup_escape_text(c->name, -1);
546 esc2 = g_markup_escape_text(c->data, -1);
547 if (aprefix) {
548 g_string_append_printf(text, " %s:%s='%s'", aprefix, esc, esc2);
549 } else {
550 g_string_append_printf(text, " %s='%s'", esc, esc2);
552 g_free(esc);
553 g_free(esc2);
554 } else if(c->type == PURPLE_XMLNODE_TYPE_TAG || c->type == PURPLE_XMLNODE_TYPE_DATA) {
555 if(c->type == PURPLE_XMLNODE_TYPE_DATA)
556 pretty = FALSE;
557 need_end = TRUE;
561 if(need_end) {
562 g_string_append_printf(text, ">%s", pretty ? NEWLINE_S : "");
564 for(c = node->child; c; c = c->next)
566 if(c->type == PURPLE_XMLNODE_TYPE_TAG) {
567 int esc_len;
568 esc = purple_xmlnode_to_str_helper(c, &esc_len, pretty, depth+1);
569 text = g_string_append_len(text, esc, esc_len);
570 g_free(esc);
571 } else if(c->type == PURPLE_XMLNODE_TYPE_DATA && c->data_sz > 0) {
572 esc = g_markup_escape_text(c->data, c->data_sz);
573 text = g_string_append(text, esc);
574 g_free(esc);
578 if(tab && pretty)
579 text = g_string_append(text, tab);
580 if (prefix) {
581 g_string_append_printf(text, "</%s:%s>%s", prefix, node_name, formatting ? NEWLINE_S : "");
582 } else {
583 g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : "");
585 } else {
586 g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : "");
589 g_free(node_name);
591 g_free(tab);
593 if(len)
594 *len = text->len;
596 return g_string_free(text, FALSE);
599 char *
600 purple_xmlnode_to_str(const PurpleXmlNode *node, int *len)
602 return purple_xmlnode_to_str_helper(node, len, FALSE, 0);
605 char *
606 purple_xmlnode_to_formatted_str(const PurpleXmlNode *node, int *len)
608 char *xml, *xml_with_declaration;
610 g_return_val_if_fail(node != NULL, NULL);
612 xml = purple_xmlnode_to_str_helper(node, len, TRUE, 0);
613 xml_with_declaration =
614 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml);
615 g_free(xml);
617 if (len)
618 *len += sizeof("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S) - 1;
620 return xml_with_declaration;
623 struct _xmlnode_parser_data {
624 PurpleXmlNode *current;
625 gboolean error;
628 static void
629 purple_xmlnode_parser_element_start_libxml(void *user_data,
630 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *xmlns,
631 int nb_namespaces, const xmlChar **namespaces,
632 int nb_attributes, int nb_defaulted, const xmlChar **attributes)
634 struct _xmlnode_parser_data *xpd = user_data;
635 PurpleXmlNode *node;
636 int i, j;
638 if(!element_name || xpd->error) {
639 return;
640 } else {
641 if(xpd->current)
642 node = purple_xmlnode_new_child(xpd->current, (const char*) element_name);
643 else
644 node = purple_xmlnode_new((const char *) element_name);
646 purple_xmlnode_set_namespace(node, (const char *) xmlns);
647 purple_xmlnode_set_prefix(node, (const char *)prefix);
649 if (nb_namespaces != 0) {
650 node->namespace_map = g_hash_table_new_full(
651 g_str_hash, g_str_equal, g_free, g_free);
653 for (i = 0, j = 0; i < nb_namespaces; i++, j += 2) {
654 const char *key = (const char *)namespaces[j];
655 const char *val = (const char *)namespaces[j + 1];
656 g_hash_table_insert(node->namespace_map,
657 g_strdup(key ? key : ""), g_strdup(val ? val : ""));
661 for(i=0; i < nb_attributes * 5; i+=5) {
662 const char *name = (const char *)attributes[i];
663 const char *prefix = (const char *)attributes[i+1];
664 char *txt;
665 int attrib_len = attributes[i+4] - attributes[i+3];
666 char *attrib = g_strndup((const char *)attributes[i+3], attrib_len);
667 txt = attrib;
668 attrib = purple_unescape_text(txt);
669 g_free(txt);
670 purple_xmlnode_set_attrib_full(node, name, NULL, prefix, attrib);
671 g_free(attrib);
674 xpd->current = node;
678 static void
679 purple_xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
680 const xmlChar *prefix, const xmlChar *xmlns)
682 struct _xmlnode_parser_data *xpd = user_data;
684 if(!element_name || !xpd->current || xpd->error)
685 return;
687 if(xpd->current->parent) {
688 if(!xmlStrcmp((xmlChar*) xpd->current->name, element_name))
689 xpd->current = xpd->current->parent;
693 static void
694 purple_xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
696 struct _xmlnode_parser_data *xpd = user_data;
698 if(!xpd->current || xpd->error)
699 return;
701 if(!text || !text_len)
702 return;
704 purple_xmlnode_insert_data(xpd->current, (const char*) text, text_len);
707 static void
708 purple_xmlnode_parser_error_libxml(void *user_data, const char *msg, ...)
710 struct _xmlnode_parser_data *xpd = user_data;
711 char errmsg[2048];
712 va_list args;
714 xpd->error = TRUE;
716 va_start(args, msg);
717 vsnprintf(errmsg, sizeof(errmsg), msg, args);
718 va_end(args);
720 purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg);
723 static void
724 purple_xmlnode_parser_structural_error_libxml(void *user_data, xmlErrorPtr error)
726 struct _xmlnode_parser_data *xpd = user_data;
728 if (error && (error->level == XML_ERR_ERROR ||
729 error->level == XML_ERR_FATAL)) {
730 xpd->error = TRUE;
731 purple_debug_error("xmlnode", "XML parser error for PurpleXmlNode %p: "
732 "Domain %i, code %i, level %i: %s",
733 user_data, error->domain, error->code, error->level,
734 error->message ? error->message : "(null)\n");
735 } else if (error)
736 purple_debug_warning("xmlnode", "XML parser error for PurpleXmlNode %p: "
737 "Domain %i, code %i, level %i: %s",
738 user_data, error->domain, error->code, error->level,
739 error->message ? error->message : "(null)\n");
740 else
741 purple_debug_warning("xmlnode", "XML parser error for PurpleXmlNode %p\n",
742 user_data);
745 static xmlSAXHandler purple_xmlnode_parser_libxml = {
746 NULL, /* internalSubset */
747 NULL, /* isStandalone */
748 NULL, /* hasInternalSubset */
749 NULL, /* hasExternalSubset */
750 NULL, /* resolveEntity */
751 NULL, /* getEntity */
752 NULL, /* entityDecl */
753 NULL, /* notationDecl */
754 NULL, /* attributeDecl */
755 NULL, /* elementDecl */
756 NULL, /* unparsedEntityDecl */
757 NULL, /* setDocumentLocator */
758 NULL, /* startDocument */
759 NULL, /* endDocument */
760 NULL, /* startElement */
761 NULL, /* endElement */
762 NULL, /* reference */
763 purple_xmlnode_parser_element_text_libxml, /* characters */
764 NULL, /* ignorableWhitespace */
765 NULL, /* processingInstruction */
766 NULL, /* comment */
767 NULL, /* warning */
768 purple_xmlnode_parser_error_libxml, /* error */
769 NULL, /* fatalError */
770 NULL, /* getParameterEntity */
771 NULL, /* cdataBlock */
772 NULL, /* externalSubset */
773 XML_SAX2_MAGIC, /* initialized */
774 NULL, /* _private */
775 purple_xmlnode_parser_element_start_libxml, /* startElementNs */
776 purple_xmlnode_parser_element_end_libxml, /* endElementNs */
777 purple_xmlnode_parser_structural_error_libxml, /* serror */
780 PurpleXmlNode *
781 purple_xmlnode_from_str(const char *str, gssize size)
783 struct _xmlnode_parser_data *xpd;
784 PurpleXmlNode *ret;
785 gsize real_size;
787 g_return_val_if_fail(str != NULL, NULL);
789 real_size = size < 0 ? strlen(str) : (gsize)size;
790 xpd = g_new0(struct _xmlnode_parser_data, 1);
792 if (xmlSAXUserParseMemory(&purple_xmlnode_parser_libxml, xpd, str, real_size) < 0) {
793 while(xpd->current && xpd->current->parent)
794 xpd->current = xpd->current->parent;
795 if(xpd->current)
796 purple_xmlnode_free(xpd->current);
797 xpd->current = NULL;
799 ret = xpd->current;
800 if (xpd->error) {
801 ret = NULL;
802 if (xpd->current)
803 purple_xmlnode_free(xpd->current);
806 g_free(xpd);
807 return ret;
810 PurpleXmlNode *
811 purple_xmlnode_from_file(const char *dir, const char *filename, const char *description, const char *process)
813 gchar *filename_full;
814 GError *error = NULL;
815 gchar *contents = NULL;
816 gsize length;
817 PurpleXmlNode *node = NULL;
819 g_return_val_if_fail(dir != NULL, NULL);
821 purple_debug_misc(process, "Reading file %s from directory %s\n",
822 filename, dir);
824 filename_full = g_build_filename(dir, filename, NULL);
826 if (!g_file_test(filename_full, G_FILE_TEST_EXISTS))
828 purple_debug_info(process, "File %s does not exist (this is not "
829 "necessarily an error)\n", filename_full);
830 g_free(filename_full);
831 return NULL;
834 if (!g_file_get_contents(filename_full, &contents, &length, &error))
836 purple_debug_error(process, "Error reading file %s: %s\n",
837 filename_full, error->message);
838 g_error_free(error);
841 if ((contents != NULL) && (length > 0))
843 node = purple_xmlnode_from_str(contents, length);
845 /* If we were unable to parse the file then save its contents to a backup file */
846 if (node == NULL)
848 gchar *filename_temp, *filename_temp_full;
850 filename_temp = g_strdup_printf("%s~", filename);
851 filename_temp_full = g_build_filename(dir, filename_temp, NULL);
853 purple_debug_error("util", "Error parsing file %s. Renaming old "
854 "file to %s\n", filename_full, filename_temp);
855 purple_util_write_data_to_file_absolute(filename_temp_full, contents, length);
857 g_free(filename_temp_full);
858 g_free(filename_temp);
861 g_free(contents);
864 /* If we could not parse the file then show the user an error message */
865 if (node == NULL)
867 gchar *title, *msg;
868 title = g_strdup_printf(_("Error Reading %s"), filename);
869 msg = g_strdup_printf(_("An error was encountered reading your "
870 "%s. The file has not been loaded, and the old file "
871 "has been renamed to %s~."), description, filename_full);
872 purple_notify_error(NULL, NULL, title, msg, NULL);
873 g_free(title);
874 g_free(msg);
877 g_free(filename_full);
879 return node;
882 static void
883 purple_xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
885 GHashTable *ret = (GHashTable *)user_data;
886 g_hash_table_insert(ret, g_strdup(key), g_strdup(value));
889 PurpleXmlNode *
890 purple_xmlnode_copy(const PurpleXmlNode *src)
892 PurpleXmlNode *ret;
893 PurpleXmlNode *child;
894 PurpleXmlNode *sibling = NULL;
896 g_return_val_if_fail(src != NULL, NULL);
898 ret = new_node(src->name, src->type);
899 ret->xmlns = g_strdup(src->xmlns);
900 if (src->data) {
901 if (src->data_sz) {
902 ret->data = g_memdup(src->data, src->data_sz);
903 ret->data_sz = src->data_sz;
904 } else {
905 ret->data = g_strdup(src->data);
908 ret->prefix = g_strdup(src->prefix);
909 if (src->namespace_map) {
910 ret->namespace_map = g_hash_table_new_full(g_str_hash, g_str_equal,
911 g_free, g_free);
912 g_hash_table_foreach(src->namespace_map, purple_xmlnode_copy_foreach_ns, ret->namespace_map);
915 for (child = src->child; child; child = child->next) {
916 if (sibling) {
917 sibling->next = purple_xmlnode_copy(child);
918 sibling = sibling->next;
919 } else {
920 ret->child = sibling = purple_xmlnode_copy(child);
922 sibling->parent = ret;
925 ret->lastchild = sibling;
927 return ret;
930 PurpleXmlNode *
931 purple_xmlnode_get_next_twin(PurpleXmlNode *node)
933 PurpleXmlNode *sibling;
934 const char *ns = purple_xmlnode_get_namespace(node);
936 g_return_val_if_fail(node != NULL, NULL);
937 g_return_val_if_fail(node->type == PURPLE_XMLNODE_TYPE_TAG, NULL);
939 for(sibling = node->next; sibling; sibling = sibling->next) {
940 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
941 const char *xmlns = NULL;
942 if(ns)
943 xmlns = purple_xmlnode_get_namespace(sibling);
945 if(sibling->type == PURPLE_XMLNODE_TYPE_TAG && purple_strequal(node->name, sibling->name) &&
946 purple_strequal(ns, xmlns))
947 return sibling;
950 return NULL;
953 GType
954 purple_xmlnode_get_type(void)
956 static GType type = 0;
958 if (type == 0) {
959 type = g_boxed_type_register_static("PurpleXmlNode",
960 (GBoxedCopyFunc)purple_xmlnode_copy,
961 (GBoxedFreeFunc)purple_xmlnode_free);
964 return type;