Merge heads.
[pidgin-git.git] / libpurple / xmlnode.c
blob323c33cfb32cef5b65823dc4a9868841865197de
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 */
27 #include "internal.h"
28 #include "debug.h"
30 #include <libxml/parser.h>
31 #include <string.h>
32 #include <glib.h>
34 #include "util.h"
35 #include "xmlnode.h"
37 #ifdef _WIN32
38 # define NEWLINE_S "\r\n"
39 #else
40 # define NEWLINE_S "\n"
41 #endif
43 static PurpleXmlNode*
44 new_node(const char *name, PurpleXmlNodeType type)
46 PurpleXmlNode *node = g_new0(PurpleXmlNode, 1);
48 node->name = g_strdup(name);
49 node->type = type;
51 return node;
54 PurpleXmlNode*
55 purple_xmlnode_new(const char *name)
57 g_return_val_if_fail(name != NULL && *name != '\0', NULL);
59 return new_node(name, PURPLE_XMLNODE_TYPE_TAG);
62 PurpleXmlNode *
63 purple_xmlnode_new_child(PurpleXmlNode *parent, const char *name)
65 PurpleXmlNode *node;
67 g_return_val_if_fail(parent != NULL, NULL);
68 g_return_val_if_fail(name != NULL && *name != '\0', NULL);
70 node = new_node(name, PURPLE_XMLNODE_TYPE_TAG);
72 purple_xmlnode_insert_child(parent, node);
74 return node;
77 void
78 purple_xmlnode_insert_child(PurpleXmlNode *parent, PurpleXmlNode *child)
80 g_return_if_fail(parent != NULL);
81 g_return_if_fail(child != NULL);
83 child->parent = parent;
85 if(parent->lastchild) {
86 parent->lastchild->next = child;
87 } else {
88 parent->child = child;
91 parent->lastchild = child;
94 void
95 purple_xmlnode_insert_data(PurpleXmlNode *node, const char *data, gssize size)
97 PurpleXmlNode *child;
98 gsize real_size;
100 g_return_if_fail(node != NULL);
101 g_return_if_fail(data != NULL);
102 g_return_if_fail(size != 0);
104 real_size = size == -1 ? strlen(data) : (gsize)size;
106 child = new_node(NULL, PURPLE_XMLNODE_TYPE_DATA);
108 child->data = g_memdup(data, real_size);
109 child->data_sz = real_size;
111 purple_xmlnode_insert_child(node, child);
114 void
115 purple_xmlnode_remove_attrib(PurpleXmlNode *node, const char *attr)
117 PurpleXmlNode *attr_node, *sibling = NULL;
119 g_return_if_fail(node != NULL);
120 g_return_if_fail(attr != NULL);
122 attr_node = node->child;
123 while (attr_node) {
124 if(attr_node->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
125 purple_strequal(attr_node->name, attr))
127 if (node->lastchild == attr_node) {
128 node->lastchild = sibling;
130 if (sibling == NULL) {
131 node->child = attr_node->next;
132 purple_xmlnode_free(attr_node);
133 attr_node = node->child;
134 } else {
135 sibling->next = attr_node->next;
136 sibling = attr_node->next;
137 purple_xmlnode_free(attr_node);
138 attr_node = sibling;
141 else
143 attr_node = attr_node->next;
145 sibling = attr_node;
149 void
150 purple_xmlnode_remove_attrib_with_namespace(PurpleXmlNode *node, const char *attr, const char *xmlns)
152 PurpleXmlNode *attr_node, *sibling = NULL;
154 g_return_if_fail(node != NULL);
155 g_return_if_fail(attr != NULL);
157 for(attr_node = node->child; attr_node; attr_node = attr_node->next)
159 if(attr_node->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
160 purple_strequal(attr, attr_node->name) &&
161 purple_strequal(xmlns, attr_node->xmlns))
163 if(sibling == NULL) {
164 node->child = attr_node->next;
165 } else {
166 sibling->next = attr_node->next;
168 if (node->lastchild == attr_node) {
169 node->lastchild = sibling;
171 purple_xmlnode_free(attr_node);
172 return;
174 sibling = attr_node;
178 void
179 purple_xmlnode_set_attrib(PurpleXmlNode *node, const char *attr, const char *value)
181 purple_xmlnode_remove_attrib(node, attr);
182 purple_xmlnode_set_attrib_full(node, attr, NULL, NULL, value);
185 void
186 purple_xmlnode_set_attrib_full(PurpleXmlNode *node, const char *attr, const char *xmlns, const char *prefix, const char *value)
188 PurpleXmlNode *attrib_node;
190 g_return_if_fail(node != NULL);
191 g_return_if_fail(attr != NULL);
192 g_return_if_fail(value != NULL);
194 purple_xmlnode_remove_attrib_with_namespace(node, attr, xmlns);
195 attrib_node = new_node(attr, PURPLE_XMLNODE_TYPE_ATTRIB);
197 attrib_node->data = g_strdup(value);
198 attrib_node->xmlns = g_strdup(xmlns);
199 attrib_node->prefix = g_strdup(prefix);
201 purple_xmlnode_insert_child(node, attrib_node);
205 const char *
206 purple_xmlnode_get_attrib(const PurpleXmlNode *node, const char *attr)
208 PurpleXmlNode *x;
210 g_return_val_if_fail(node != NULL, NULL);
211 g_return_val_if_fail(attr != NULL, NULL);
213 for(x = node->child; x; x = x->next) {
214 if(x->type == PURPLE_XMLNODE_TYPE_ATTRIB && purple_strequal(attr, x->name)) {
215 return x->data;
219 return NULL;
222 const char *
223 purple_xmlnode_get_attrib_with_namespace(const PurpleXmlNode *node, const char *attr, const char *xmlns)
225 const PurpleXmlNode *x;
227 g_return_val_if_fail(node != NULL, NULL);
228 g_return_val_if_fail(attr != NULL, NULL);
230 for(x = node->child; x; x = x->next) {
231 if(x->type == PURPLE_XMLNODE_TYPE_ATTRIB &&
232 purple_strequal(attr, x->name) &&
233 purple_strequal(xmlns, x->xmlns)) {
234 return x->data;
238 return NULL;
242 void purple_xmlnode_set_namespace(PurpleXmlNode *node, const char *xmlns)
244 char *tmp;
245 g_return_if_fail(node != NULL);
247 tmp = node->xmlns;
248 node->xmlns = g_strdup(xmlns);
250 if (node->namespace_map) {
251 g_hash_table_insert(node->namespace_map,
252 g_strdup(""), g_strdup(xmlns));
255 g_free(tmp);
258 const char *purple_xmlnode_get_namespace(const PurpleXmlNode *node)
260 g_return_val_if_fail(node != NULL, NULL);
262 return node->xmlns;
265 const char *purple_xmlnode_get_default_namespace(const PurpleXmlNode *node)
267 const PurpleXmlNode *current_node;
268 const char *ns = NULL;
270 g_return_val_if_fail(node != NULL, NULL);
272 current_node = node;
273 while (current_node) {
274 /* If this node does *not* have a prefix, node->xmlns is the default
275 * namespace. Otherwise, it's the prefix namespace.
277 if (!current_node->prefix && current_node->xmlns) {
278 return current_node->xmlns;
279 } else if (current_node->namespace_map) {
280 ns = g_hash_table_lookup(current_node->namespace_map, "");
281 if (ns && *ns)
282 return ns;
285 current_node = current_node->parent;
288 return ns;
291 void purple_xmlnode_set_prefix(PurpleXmlNode *node, const char *prefix)
293 g_return_if_fail(node != NULL);
295 g_free(node->prefix);
296 node->prefix = g_strdup(prefix);
299 const char *purple_xmlnode_get_prefix(const PurpleXmlNode *node)
301 g_return_val_if_fail(node != NULL, NULL);
302 return node->prefix;
305 const char *purple_xmlnode_get_prefix_namespace(const PurpleXmlNode *node, const char *prefix)
307 const PurpleXmlNode *current_node;
309 g_return_val_if_fail(node != NULL, NULL);
310 g_return_val_if_fail(prefix != NULL, purple_xmlnode_get_default_namespace(node));
312 current_node = node;
313 while (current_node) {
314 if (current_node->prefix && g_str_equal(prefix, current_node->prefix) &&
315 current_node->xmlns) {
316 return current_node->xmlns;
317 } else if (current_node->namespace_map) {
318 const char *ns = g_hash_table_lookup(current_node->namespace_map, prefix);
319 if (ns && *ns) {
320 return ns;
324 current_node = current_node->parent;
327 return NULL;
330 void purple_xmlnode_strip_prefixes(PurpleXmlNode *node)
332 PurpleXmlNode *child;
333 const char *prefix;
335 g_return_if_fail(node != NULL);
337 for (child = node->child; child; child = child->next) {
338 if (child->type == PURPLE_XMLNODE_TYPE_TAG)
339 purple_xmlnode_strip_prefixes(child);
342 prefix = purple_xmlnode_get_prefix(node);
343 if (prefix) {
344 const char *ns = purple_xmlnode_get_prefix_namespace(node, prefix);
345 purple_xmlnode_set_namespace(node, ns);
346 purple_xmlnode_set_prefix(node, NULL);
347 } else {
348 purple_xmlnode_set_namespace(node, purple_xmlnode_get_default_namespace(node));
352 PurpleXmlNode *purple_xmlnode_get_parent(const PurpleXmlNode *child)
354 g_return_val_if_fail(child != NULL, NULL);
355 return child->parent;
358 void
359 purple_xmlnode_free(PurpleXmlNode *node)
361 PurpleXmlNode *x, *y;
363 g_return_if_fail(node != NULL);
365 /* if we're part of a tree, remove ourselves from the tree first */
366 if(NULL != node->parent) {
367 if(node->parent->child == node) {
368 node->parent->child = node->next;
369 if (node->parent->lastchild == node)
370 node->parent->lastchild = node->next;
371 } else {
372 PurpleXmlNode *prev = node->parent->child;
373 while(prev && prev->next != node) {
374 prev = prev->next;
376 if(prev) {
377 prev->next = node->next;
378 if (node->parent->lastchild == node)
379 node->parent->lastchild = prev;
384 /* now free our children */
385 x = node->child;
386 while(x) {
387 y = x->next;
388 purple_xmlnode_free(x);
389 x = y;
392 /* now dispose of ourselves */
393 g_free(node->name);
394 g_free(node->data);
395 g_free(node->xmlns);
396 g_free(node->prefix);
398 if(node->namespace_map)
399 g_hash_table_destroy(node->namespace_map);
401 g_free(node);
404 PurpleXmlNode*
405 purple_xmlnode_get_child(const PurpleXmlNode *parent, const char *name)
407 return purple_xmlnode_get_child_with_namespace(parent, name, NULL);
410 PurpleXmlNode *
411 purple_xmlnode_get_child_with_namespace(const PurpleXmlNode *parent, const char *name, const char *ns)
413 PurpleXmlNode *x, *ret = NULL;
414 char **names;
415 char *parent_name, *child_name;
417 g_return_val_if_fail(parent != NULL, NULL);
418 g_return_val_if_fail(name != NULL, NULL);
420 names = g_strsplit(name, "/", 2);
421 parent_name = names[0];
422 child_name = names[1];
424 for(x = parent->child; x; x = x->next) {
425 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
426 const char *xmlns = NULL;
427 if(ns)
428 xmlns = purple_xmlnode_get_namespace(x);
430 if(x->type == PURPLE_XMLNODE_TYPE_TAG && purple_strequal(parent_name, x->name)
431 && purple_strequal(ns, xmlns)) {
432 ret = x;
433 break;
437 if(child_name && ret)
438 ret = purple_xmlnode_get_child(ret, child_name);
440 g_strfreev(names);
441 return ret;
444 char *
445 purple_xmlnode_get_data(const PurpleXmlNode *node)
447 GString *str = NULL;
448 PurpleXmlNode *c;
450 g_return_val_if_fail(node != NULL, NULL);
452 for(c = node->child; c; c = c->next) {
453 if(c->type == PURPLE_XMLNODE_TYPE_DATA) {
454 if(!str)
455 str = g_string_new_len(c->data, c->data_sz);
456 else
457 str = g_string_append_len(str, c->data, c->data_sz);
461 if (str == NULL)
462 return NULL;
464 return g_string_free(str, FALSE);
467 char *
468 purple_xmlnode_get_data_unescaped(const PurpleXmlNode *node)
470 char *escaped = purple_xmlnode_get_data(node);
472 char *unescaped = escaped ? purple_unescape_html(escaped) : NULL;
474 g_free(escaped);
476 return unescaped;
479 static void
480 purple_xmlnode_to_str_foreach_append_ns(const char *key, const char *value,
481 GString *buf)
483 if (*key) {
484 g_string_append_printf(buf, " xmlns:%s='%s'", key, value);
485 } else {
486 g_string_append_printf(buf, " xmlns='%s'", value);
490 static char *
491 purple_xmlnode_to_str_helper(const PurpleXmlNode *node, int *len, gboolean formatting, int depth)
493 GString *text;
494 const char *prefix;
495 const PurpleXmlNode *c;
496 char *node_name, *esc, *esc2, *tab = NULL;
497 gboolean need_end = FALSE, pretty = formatting;
499 g_return_val_if_fail(node != NULL, NULL);
501 text = g_string_new("");
503 if(pretty && depth) {
504 tab = g_strnfill(depth, '\t');
505 text = g_string_append(text, tab);
508 node_name = g_markup_escape_text(node->name, -1);
509 prefix = purple_xmlnode_get_prefix(node);
511 if (prefix) {
512 g_string_append_printf(text, "<%s:%s", prefix, node_name);
513 } else {
514 g_string_append_printf(text, "<%s", node_name);
517 if (node->namespace_map) {
518 g_hash_table_foreach(node->namespace_map,
519 (GHFunc)purple_xmlnode_to_str_foreach_append_ns, text);
520 } else {
521 /* Figure out if this node has a different default namespace from parent */
522 const char *xmlns = NULL;
523 const char *parent_xmlns = NULL;
524 if (!prefix)
525 xmlns = node->xmlns;
527 if (!xmlns)
528 xmlns = purple_xmlnode_get_default_namespace(node);
529 if (node->parent)
530 parent_xmlns = purple_xmlnode_get_default_namespace(node->parent);
531 if (!purple_strequal(xmlns, parent_xmlns))
533 char *escaped_xmlns = g_markup_escape_text(xmlns, -1);
534 g_string_append_printf(text, " xmlns='%s'", escaped_xmlns);
535 g_free(escaped_xmlns);
538 for(c = node->child; c; c = c->next)
540 if(c->type == PURPLE_XMLNODE_TYPE_ATTRIB) {
541 const char *aprefix = purple_xmlnode_get_prefix(c);
542 esc = g_markup_escape_text(c->name, -1);
543 esc2 = g_markup_escape_text(c->data, -1);
544 if (aprefix) {
545 g_string_append_printf(text, " %s:%s='%s'", aprefix, esc, esc2);
546 } else {
547 g_string_append_printf(text, " %s='%s'", esc, esc2);
549 g_free(esc);
550 g_free(esc2);
551 } else if(c->type == PURPLE_XMLNODE_TYPE_TAG || c->type == PURPLE_XMLNODE_TYPE_DATA) {
552 if(c->type == PURPLE_XMLNODE_TYPE_DATA)
553 pretty = FALSE;
554 need_end = TRUE;
558 if(need_end) {
559 g_string_append_printf(text, ">%s", pretty ? NEWLINE_S : "");
561 for(c = node->child; c; c = c->next)
563 if(c->type == PURPLE_XMLNODE_TYPE_TAG) {
564 int esc_len;
565 esc = purple_xmlnode_to_str_helper(c, &esc_len, pretty, depth+1);
566 text = g_string_append_len(text, esc, esc_len);
567 g_free(esc);
568 } else if(c->type == PURPLE_XMLNODE_TYPE_DATA && c->data_sz > 0) {
569 esc = g_markup_escape_text(c->data, c->data_sz);
570 text = g_string_append(text, esc);
571 g_free(esc);
575 if(tab && pretty)
576 text = g_string_append(text, tab);
577 if (prefix) {
578 g_string_append_printf(text, "</%s:%s>%s", prefix, node_name, formatting ? NEWLINE_S : "");
579 } else {
580 g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : "");
582 } else {
583 g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : "");
586 g_free(node_name);
588 g_free(tab);
590 if(len)
591 *len = text->len;
593 return g_string_free(text, FALSE);
596 char *
597 purple_xmlnode_to_str(const PurpleXmlNode *node, int *len)
599 return purple_xmlnode_to_str_helper(node, len, FALSE, 0);
602 char *
603 purple_xmlnode_to_formatted_str(const PurpleXmlNode *node, int *len)
605 char *xml, *xml_with_declaration;
607 g_return_val_if_fail(node != NULL, NULL);
609 xml = purple_xmlnode_to_str_helper(node, len, TRUE, 0);
610 xml_with_declaration =
611 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml);
612 g_free(xml);
614 if (len)
615 *len += sizeof("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S) - 1;
617 return xml_with_declaration;
620 struct _xmlnode_parser_data {
621 PurpleXmlNode *current;
622 gboolean error;
625 static void
626 purple_xmlnode_parser_element_start_libxml(void *user_data,
627 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *xmlns,
628 int nb_namespaces, const xmlChar **namespaces,
629 int nb_attributes, int nb_defaulted, const xmlChar **attributes)
631 struct _xmlnode_parser_data *xpd = user_data;
632 PurpleXmlNode *node;
633 int i, j;
635 if(!element_name || xpd->error) {
636 return;
637 } else {
638 if(xpd->current)
639 node = purple_xmlnode_new_child(xpd->current, (const char*) element_name);
640 else
641 node = purple_xmlnode_new((const char *) element_name);
643 purple_xmlnode_set_namespace(node, (const char *) xmlns);
644 purple_xmlnode_set_prefix(node, (const char *)prefix);
646 if (nb_namespaces != 0) {
647 node->namespace_map = g_hash_table_new_full(
648 g_str_hash, g_str_equal, g_free, g_free);
650 for (i = 0, j = 0; i < nb_namespaces; i++, j += 2) {
651 const char *key = (const char *)namespaces[j];
652 const char *val = (const char *)namespaces[j + 1];
653 g_hash_table_insert(node->namespace_map,
654 g_strdup(key ? key : ""), g_strdup(val ? val : ""));
658 for(i=0; i < nb_attributes * 5; i+=5) {
659 const char *name = (const char *)attributes[i];
660 const char *prefix = (const char *)attributes[i+1];
661 char *txt;
662 int attrib_len = attributes[i+4] - attributes[i+3];
663 char *attrib = g_strndup((const char *)attributes[i+3], attrib_len);
664 txt = attrib;
665 attrib = purple_unescape_text(txt);
666 g_free(txt);
667 purple_xmlnode_set_attrib_full(node, name, NULL, prefix, attrib);
668 g_free(attrib);
671 xpd->current = node;
675 static void
676 purple_xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
677 const xmlChar *prefix, const xmlChar *xmlns)
679 struct _xmlnode_parser_data *xpd = user_data;
681 if(!element_name || !xpd->current || xpd->error)
682 return;
684 if(xpd->current->parent) {
685 if(!xmlStrcmp((xmlChar*) xpd->current->name, element_name))
686 xpd->current = xpd->current->parent;
690 static void
691 purple_xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
693 struct _xmlnode_parser_data *xpd = user_data;
695 if(!xpd->current || xpd->error)
696 return;
698 if(!text || !text_len)
699 return;
701 purple_xmlnode_insert_data(xpd->current, (const char*) text, text_len);
704 static void
705 purple_xmlnode_parser_error_libxml(void *user_data, const char *msg, ...)
707 struct _xmlnode_parser_data *xpd = user_data;
708 char errmsg[2048];
709 va_list args;
711 xpd->error = TRUE;
713 va_start(args, msg);
714 vsnprintf(errmsg, sizeof(errmsg), msg, args);
715 va_end(args);
717 purple_debug_error("xmlnode", "Error parsing xml file: %s", errmsg);
720 static void
721 purple_xmlnode_parser_structural_error_libxml(void *user_data, xmlErrorPtr error)
723 struct _xmlnode_parser_data *xpd = user_data;
725 if (error && (error->level == XML_ERR_ERROR ||
726 error->level == XML_ERR_FATAL)) {
727 xpd->error = TRUE;
728 purple_debug_error("xmlnode", "XML parser error for PurpleXmlNode %p: "
729 "Domain %i, code %i, level %i: %s",
730 user_data, error->domain, error->code, error->level,
731 error->message ? error->message : "(null)\n");
732 } else if (error)
733 purple_debug_warning("xmlnode", "XML parser error for PurpleXmlNode %p: "
734 "Domain %i, code %i, level %i: %s",
735 user_data, error->domain, error->code, error->level,
736 error->message ? error->message : "(null)\n");
737 else
738 purple_debug_warning("xmlnode", "XML parser error for PurpleXmlNode %p\n",
739 user_data);
742 static xmlSAXHandler purple_xmlnode_parser_libxml = {
743 NULL, /* internalSubset */
744 NULL, /* isStandalone */
745 NULL, /* hasInternalSubset */
746 NULL, /* hasExternalSubset */
747 NULL, /* resolveEntity */
748 NULL, /* getEntity */
749 NULL, /* entityDecl */
750 NULL, /* notationDecl */
751 NULL, /* attributeDecl */
752 NULL, /* elementDecl */
753 NULL, /* unparsedEntityDecl */
754 NULL, /* setDocumentLocator */
755 NULL, /* startDocument */
756 NULL, /* endDocument */
757 NULL, /* startElement */
758 NULL, /* endElement */
759 NULL, /* reference */
760 purple_xmlnode_parser_element_text_libxml, /* characters */
761 NULL, /* ignorableWhitespace */
762 NULL, /* processingInstruction */
763 NULL, /* comment */
764 NULL, /* warning */
765 purple_xmlnode_parser_error_libxml, /* error */
766 NULL, /* fatalError */
767 NULL, /* getParameterEntity */
768 NULL, /* cdataBlock */
769 NULL, /* externalSubset */
770 XML_SAX2_MAGIC, /* initialized */
771 NULL, /* _private */
772 purple_xmlnode_parser_element_start_libxml, /* startElementNs */
773 purple_xmlnode_parser_element_end_libxml, /* endElementNs */
774 purple_xmlnode_parser_structural_error_libxml, /* serror */
777 PurpleXmlNode *
778 purple_xmlnode_from_str(const char *str, gssize size)
780 struct _xmlnode_parser_data *xpd;
781 PurpleXmlNode *ret;
782 gsize real_size;
784 g_return_val_if_fail(str != NULL, NULL);
786 real_size = size < 0 ? strlen(str) : (gsize)size;
787 xpd = g_new0(struct _xmlnode_parser_data, 1);
789 if (xmlSAXUserParseMemory(&purple_xmlnode_parser_libxml, xpd, str, real_size) < 0) {
790 while(xpd->current && xpd->current->parent)
791 xpd->current = xpd->current->parent;
792 if(xpd->current)
793 purple_xmlnode_free(xpd->current);
794 xpd->current = NULL;
796 ret = xpd->current;
797 if (xpd->error) {
798 ret = NULL;
799 if (xpd->current)
800 purple_xmlnode_free(xpd->current);
803 g_free(xpd);
804 return ret;
807 PurpleXmlNode *
808 purple_xmlnode_from_file(const char *dir, const char *filename, const char *description, const char *process)
810 gchar *filename_full;
811 GError *error = NULL;
812 gchar *contents = NULL;
813 gsize length;
814 PurpleXmlNode *node = NULL;
816 g_return_val_if_fail(dir != NULL, NULL);
818 purple_debug_misc(process, "Reading file %s from directory %s\n",
819 filename, dir);
821 filename_full = g_build_filename(dir, filename, NULL);
823 if (!g_file_test(filename_full, G_FILE_TEST_EXISTS))
825 purple_debug_info(process, "File %s does not exist (this is not "
826 "necessarily an error)\n", filename_full);
827 g_free(filename_full);
828 return NULL;
831 if (!g_file_get_contents(filename_full, &contents, &length, &error))
833 purple_debug_error(process, "Error reading file %s: %s\n",
834 filename_full, error->message);
835 g_error_free(error);
838 if ((contents != NULL) && (length > 0))
840 node = purple_xmlnode_from_str(contents, length);
842 /* If we were unable to parse the file then save its contents to a backup file */
843 if (node == NULL)
845 gchar *filename_temp, *filename_temp_full;
847 filename_temp = g_strdup_printf("%s~", filename);
848 filename_temp_full = g_build_filename(dir, filename_temp, NULL);
850 purple_debug_error("util", "Error parsing file %s. Renaming old "
851 "file to %s\n", filename_full, filename_temp);
852 purple_util_write_data_to_file_absolute(filename_temp_full, contents, length);
854 g_free(filename_temp_full);
855 g_free(filename_temp);
858 g_free(contents);
861 /* If we could not parse the file then show the user an error message */
862 if (node == NULL)
864 gchar *title, *msg;
865 title = g_strdup_printf(_("Error Reading %s"), filename);
866 msg = g_strdup_printf(_("An error was encountered reading your "
867 "%s. The file has not been loaded, and the old file "
868 "has been renamed to %s~."), description, filename_full);
869 purple_notify_error(NULL, NULL, title, msg, NULL);
870 g_free(title);
871 g_free(msg);
874 g_free(filename_full);
876 return node;
879 static void
880 purple_xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
882 GHashTable *ret = (GHashTable *)user_data;
883 g_hash_table_insert(ret, g_strdup(key), g_strdup(value));
886 PurpleXmlNode *
887 purple_xmlnode_copy(const PurpleXmlNode *src)
889 PurpleXmlNode *ret;
890 PurpleXmlNode *child;
891 PurpleXmlNode *sibling = NULL;
893 g_return_val_if_fail(src != NULL, NULL);
895 ret = new_node(src->name, src->type);
896 ret->xmlns = g_strdup(src->xmlns);
897 if (src->data) {
898 if (src->data_sz) {
899 ret->data = g_memdup(src->data, src->data_sz);
900 ret->data_sz = src->data_sz;
901 } else {
902 ret->data = g_strdup(src->data);
905 ret->prefix = g_strdup(src->prefix);
906 if (src->namespace_map) {
907 ret->namespace_map = g_hash_table_new_full(g_str_hash, g_str_equal,
908 g_free, g_free);
909 g_hash_table_foreach(src->namespace_map, purple_xmlnode_copy_foreach_ns, ret->namespace_map);
912 for (child = src->child; child; child = child->next) {
913 if (sibling) {
914 sibling->next = purple_xmlnode_copy(child);
915 sibling = sibling->next;
916 } else {
917 ret->child = sibling = purple_xmlnode_copy(child);
919 sibling->parent = ret;
922 ret->lastchild = sibling;
924 return ret;
927 PurpleXmlNode *
928 purple_xmlnode_get_next_twin(PurpleXmlNode *node)
930 PurpleXmlNode *sibling;
931 const char *ns = purple_xmlnode_get_namespace(node);
933 g_return_val_if_fail(node != NULL, NULL);
934 g_return_val_if_fail(node->type == PURPLE_XMLNODE_TYPE_TAG, NULL);
936 for(sibling = node->next; sibling; sibling = sibling->next) {
937 /* XXX: Is it correct to ignore the namespace for the match if none was specified? */
938 const char *xmlns = NULL;
939 if(ns)
940 xmlns = purple_xmlnode_get_namespace(sibling);
942 if(sibling->type == PURPLE_XMLNODE_TYPE_TAG && purple_strequal(node->name, sibling->name) &&
943 purple_strequal(ns, xmlns))
944 return sibling;
947 return NULL;
950 GType
951 purple_xmlnode_get_type(void)
953 static GType type = 0;
955 if (type == 0) {
956 type = g_boxed_type_register_static("PurpleXmlNode",
957 (GBoxedCopyFunc)purple_xmlnode_copy,
958 (GBoxedFreeFunc)purple_xmlnode_free);
961 return type;